Skip to content

Prompt & Messages

结构化提示词工程体系是 Spring AI 的核心设计之一,理解消息类型和 Prompt 构建是写好 AI 应用的基础。

消息类型体系

Spring AI 将 LLM 的对话结构抽象为一套完整的消息类型系统:

Message (接口)
├── SystemMessage      // 系统指令,定义 AI 角色和行为规范
├── UserMessage        // 用户输入,支持文本 + 媒体内容
├── AssistantMessage   // 模型回复,可包含 ToolCall 请求
└── ToolResponseMessage // 工具执行结果,返回给模型

SystemMessage

系统消息定义 AI 的角色、能力边界和行为规范,通常在对话开始时设置一次:

java
// 简单系统消息
SystemMessage system = new SystemMessage("你是一个专业的 Java 架构师,擅长微服务设计。");

// 复杂系统消息(多行)
String systemPrompt = """
    你是一个代码审查助手,职责如下:
    1. 检查代码规范和最佳实践
    2. 识别潜在的性能问题
    3. 发现安全漏洞
    4. 提供具体的改进建议
    
    回答格式:
    - 问题描述
    - 严重程度(高/中/低)
    - 改进建议
    """;
SystemMessage system = new SystemMessage(systemPrompt);

UserMessage

用户消息支持纯文本和多模态内容:

java
// 纯文本
UserMessage textMsg = new UserMessage("解释 Java 虚拟线程的工作原理");

// 多模态:文本 + 图片
UserMessage multimodalMsg = new UserMessage(
    "分析这张架构图,指出潜在问题",
    List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageResource))
);

// 多模态:文本 + 多张图片
UserMessage multiImageMsg = new UserMessage(
    "比较这两张 UML 图的差异",
    List.of(
        new Media(MimeTypeUtils.IMAGE_PNG, image1),
        new Media(MimeTypeUtils.IMAGE_PNG, image2)
    )
);

AssistantMessage

模型的回复消息,通常由框架自动创建,但在构建历史对话时需要手动创建:

java
// 构建对话历史
List<Message> history = new ArrayList<>();
history.add(new UserMessage("什么是 Spring Boot?"));
history.add(new AssistantMessage("Spring Boot 是一个简化 Spring 应用开发的框架..."));
history.add(new UserMessage("它和 Spring MVC 有什么区别?")); // 当前问题

// AssistantMessage 也可以包含 ToolCall(工具调用请求)
AssistantMessage withToolCall = new AssistantMessage(
    "",  // 文本内容为空
    Map.of(),
    List.of(new AssistantMessage.ToolCall(
        "call_001", "function", "getWeather",
        "{\"city\": \"北京\"}"
    ))
);

ToolResponseMessage

工具执行结果消息:

java
// 工具执行后,将结果封装为 ToolResponseMessage
ToolResponseMessage toolResult = new ToolResponseMessage(
    List.of(new ToolResponseMessage.ToolResponse(
        "call_001",           // 对应 ToolCall 的 id
        "getWeather",         // 工具名称
        "北京:晴,25°C"      // 工具执行结果
    ))
);

Prompt 构建

PromptChatModel.call() 的入参,封装了消息列表和调用选项:

java
// 单条消息
Prompt simple = new Prompt("你好");

// 多条消息(完整对话)
Prompt conversation = new Prompt(List.of(
    new SystemMessage("你是一个 Java 专家"),
    new UserMessage("解释泛型擦除"),
    new AssistantMessage("泛型擦除是指..."),
    new UserMessage("那运行时如何获取泛型信息?")
));

// 带选项的 Prompt
Prompt withOptions = new Prompt(
    "写一首诗",
    DashScopeChatOptions.builder()
        .withTemperature(0.9f)
        .withModel("qwen-max")
        .build()
);

PromptTemplate:动态提示词

PromptTemplate 支持变量替换,是构建可复用提示词的最佳方式:

java
// 基础用法
PromptTemplate template = new PromptTemplate(
    "请用 {language} 语言解释 {concept},面向 {audience} 读者。"
);

Prompt prompt = template.create(Map.of(
    "language", "中文",
    "concept", "依赖注入",
    "audience", "初学者"
));

// 从文件加载模板(推荐用于复杂提示词)
// resources/prompts/code-review.st
PromptTemplate fileTemplate = new PromptTemplate(
    new ClassPathResource("prompts/code-review.st")
);

Prompt reviewPrompt = fileTemplate.create(Map.of(
    "code", sourceCode,
    "language", "Java",
    "standards", "Google Java Style Guide"
));

模板文件示例

# resources/prompts/code-review.st
你是一个资深 {language} 代码审查专家,遵循 {standards}。

请审查以下代码:

```{language}
{code}

请从以下维度给出反馈:

  1. 代码规范
  2. 性能优化
  3. 安全问题
  4. 可维护性

每个问题请注明严重程度(🔴高 / 🟡中 / 🟢低)。


## ChatClient 中的消息构建

`ChatClient` 提供了更简洁的消息构建方式:

```java
// 方式一:字符串
chatClient.prompt()
    .system("你是专家")
    .user("问题")
    .call().content();

// 方式二:Lambda 构建(支持变量替换)
chatClient.prompt()
    .system(s -> s.text("你是 {role} 专家").param("role", "Java"))
    .user(u -> u.text("解释 {topic}").param("topic", "虚拟线程"))
    .call().content();

// 方式三:传入 Prompt 对象
Prompt myPrompt = new Prompt(List.of(
    new SystemMessage("..."),
    new UserMessage("...")
));
chatClient.prompt(myPrompt).call().content();

结构化输出

Spring AI 支持将模型输出自动解析为 Java 对象:

java
// 定义输出结构
record CodeReview(
    List<Issue> issues,
    int overallScore,
    String summary
) {}

record Issue(
    String description,
    String severity,  // HIGH / MEDIUM / LOW
    String suggestion,
    int lineNumber
) {}

// 自动解析
CodeReview review = chatClient.prompt()
    .system("你是代码审查专家,以 JSON 格式输出结果")
    .user("审查这段代码:\n" + code)
    .call()
    .entity(CodeReview.class);

// 框架自动:
// 1. 生成 JSON Schema 并注入到 Prompt
// 2. 调用模型
// 3. 解析 JSON 响应为 CodeReview 对象

提示词工程最佳实践

1. 角色设定(Role Prompting)

java
String systemPrompt = """
    你是一个拥有 10 年经验的 Java 架构师,专注于:
    - 微服务架构设计
    - 性能优化
    - 代码质量
    
    你的回答风格:
    - 简洁直接,避免废话
    - 给出具体可执行的建议
    - 必要时提供代码示例
    """;

2. 少样本学习(Few-Shot)

java
String fewShotPrompt = """
    将以下 Java 代码转换为 Kotlin:
    
    示例1:
    Java: public String getName() { return name; }
    Kotlin: fun getName(): String = name
    
    示例2:
    Java: if (obj != null) { obj.doSomething(); }
    Kotlin: obj?.doSomething()
    
    现在转换:
    Java: {code}
    Kotlin:
    """;

3. 思维链(Chain of Thought)

java
String cotPrompt = """
    分析以下系统设计问题,请按步骤思考:
    
    问题:{problem}
    
    请按以下步骤分析:
    1. 理解需求:明确功能和非功能需求
    2. 识别挑战:找出主要技术难点
    3. 方案设计:提出 2-3 个可行方案
    4. 方案对比:分析各方案的优缺点
    5. 最终建议:给出推荐方案及理由
    """;

4. 输出格式控制

java
String formatPrompt = """
    分析 {topic},以 Markdown 格式输出:
    
    ## 概述
    (2-3 句话概括)
    
    ## 核心原理
    (技术细节)
    
    ## 代码示例
    ```java
    // 示例代码
    ```
    
    ## 最佳实践
    - 实践1
    - 实践2
    
    ## 常见陷阱
    | 陷阱 | 原因 | 解决方案 |
    |------|------|----------|
    """;

消息 Token 计算

了解 Token 消耗对控制成本至关重要:

java
ChatResponse response = chatModel.call(prompt);
Usage usage = response.getMetadata().getUsage();

System.out.printf("""
    Token 使用情况:
    - 输入 Token:%d
    - 输出 Token:%d
    - 总计:%d
    """,
    usage.getPromptTokens(),
    usage.getGenerationTokens(),
    usage.getTotalTokens()
);

Token 优化建议

  • 系统提示词尽量简洁,避免冗余描述
  • 对话历史使用滑动窗口,不要无限累积
  • 结构化输出的 Schema 注入会增加 Token 消耗
  • 使用 qwen-long 处理超长文档,成本更低

相关组件

本站内容由 褚成志 整理编写,仅供学习参考