【Spring AI 实战】三、Prompt 工程:模板化、结构化输出与 Advisors 顾问模式
本文是Spring AI实战系列的第三篇,重点讲解Prompt工程的核心技术与实战应用。主要内容包括: Prompt工程的重要性:介绍如何通过清晰表达意图降低Token消耗、提高输出质量 模板化体系:详解PromptTemplate的占位符渲染、文件管理及多语言消息构建 Few-Shot与思维链技术:展示如何通过示例引导和逐步推理提高模型表现 结构化输出:讲解如何让AI返回精确的JSON格式数据
【Spring AI 实战】三、Prompt 工程:模板化、结构化输出与 Advisors 顾问模式
大家好,我是冰点,今天我们继续Spring AI实战系列
所属阶段:第一阶段·核心基础
前置知识:建议先完成第一、二篇,熟悉 ChatClient 的基本调用链路。
适用版本:本文按 Spring AI 1.0.x 的 Prompt 与 Advisor 能力组织内容,不同版本的 Builder 与 OutputParser 细节可能略有差异。
1. Prompt 工程为什么重要
Prompt 工程(Prompt Engineering)不是玄学,它本质上是如何清晰地表达你的意图给模型。在 Spring AI 体系中,Prompt 不仅仅是传给模型的字符串,它被封装为完整的对象体系:
Prompt = messages(List<Message>) + parameters(Map<String, Object>)
Message = content + type(SYSTEM/USER/ASSISTANT) + media(可选)
一个设计良好的 Prompt 可以:
- 降低 Token 消耗:减少无效 token,节省 API 调用成本
- 提高输出质量:格式稳定、内容准确、逻辑清晰
- 实现复杂业务:多轮对话、条件分支、工具调用
1.1 Prompt 的三个层次
| 层次 | 描述 | 示例 |
|---|---|---|
| 零样本 | 直接给任务描述 | “把这段中文翻译成英文” |
| 少样本 | 提供参考示例 | “参考以下例子:中文→英文,给出下一个翻译” |
| 思维链 | 引导模型逐步推理 | “先分析问题,再给出解答” |
2. Prompt 模板体系

2.1 PromptTemplate 核心用法
Spring AI 的 PromptTemplate 借鉴了 Spring MVC 的模板思想,支持占位符渲染:
// 定义模板
PromptTemplate template = PromptTemplate.from(
"请将以下{language}代码转换为{targetLanguage}," +
"只输出代码,不要解释:\n\n```{language}\n{code}\n```"
);
// 渲染变量
Prompt prompt = template.render(Map.of(
"language", "Python",
"targetLanguage", "Java",
"code", "print('Hello World')"
));
// 调用
String javaCode = chatClient.prompt(prompt).call().content();
2.2 模板文件管理
在真实项目中,Prompt 模板应从文件加载,而不是硬编码在代码中:
src/main/resources/prompts/
├── system-code-review.st
├── user-translation.st
├── system-summarizer.st
└── user-sql-generator.st
// 从文件加载模板
PromptTemplate codeReviewTemplate = new PromptTemplate(
new ClassPathResource("prompts/system-code-review.st")
);
// 支持 Placeholder 语法
PromptTemplate promptTemplate = PromptTemplate.builder()
.resource(new ClassPathResource("prompts/user-sql-generator.st"))
.build();
// 渲染
Prompt prompt = promptTemplate.render(Map.of(
"schema", "CREATE TABLE users (id BIGINT, name VARCHAR(100), email VARCHAR(200))",
"question", "查询所有邮箱以 @company.com 结尾的用户"
));
resources/prompts/system-code-review.st 示例:
你是一位资深代码审查专家,负责审查以下{language}代码。
审查维度:
1. 安全性:是否存在 SQL 注入、XSS、敏感信息泄露风险?
2. 性能:是否有 N+1 查询、死循环、大对象内存风险?
3. 可维护性:命名是否清晰、是否有重复代码?
4. 最佳实践:是否符合{framework}开发规范?
请按以下 JSON 格式输出审查结果:
{
"security": { "issues": [], "level": "LOW|MEDIUM|HIGH", "summary": "..." },
"performance": { "issues": [], "level": "LOW|MEDIUM|HIGH", "summary": "..." },
"maintainability": { "issues": [], "level": "LOW|MEDIUM|HIGH", "summary": "..." },
"bestPractices": { "issues": [], "level": "LOW|MEDIUM|HIGH", "summary": "..." },
"overallLevel": "LOW|MEDIUM|HIGH",
"suggestions": []
}
待审查代码:
```{language}
{code}
### 2.3 多语言消息构建
```java
// 构建多段对话
Prompt prompt = Prompt.builder()
.messages(
// 系统消息
MessageBuilder.createSystemMessage(
"你是一位熟悉{service}领域的技术专家,使用{style}风格回答"
).render(
Map.of("service", "电商", "style", "简洁专业")
),
// 用户历史消息
MessageBuilder.createUserMessage("什么是微服务架构?"),
// 助手历史回复
MessageBuilder.createAssistantMessage(
"微服务架构是一种将单体应用拆分为多个小型服务的架构风格..."
),
// 当前用户消息
MessageBuilder.createUserMessage("它和SOA架构有什么区别?")
)
.build();
3. Few-Shot 与 Chain of Thought
3.1 Few-Shot:提供参考示例
Few-Shot 的核心思想是让模型从示例中学习模式,而不是从零理解任务:
PromptTemplate fewShotTemplate = PromptTemplate.from(
"请根据以下示例,将中文句子转换为英文。\n" +
"\n" +
"示例1:\n" +
"输入: 今天天气真好\n" +
"输出: The weather is great today\n" +
"\n" +
"示例2:\n" +
"输入: {input}\n" +
"输出:"
);
String result = chatClient.prompt()
.user(
fewShotTemplate.render(Map.of("input", "我喜欢吃苹果"))
)
.call()
.content();
// 输出: I like eating apples
Few-Shot 技巧:
- 示例数量:2~5 个效果最好,过多会浪费 token
- 示例质量:覆盖主要场景,边界案例放后面
- 示例格式:与最终任务的输入输出一致
3.2 Chain of Thought(思维链)
思维链(CoT)通过引导模型先推理再回答来提高复杂问题的准确率:
PromptTemplate cotTemplate = PromptTemplate.from(
"请按以下步骤思考并回答问题:\n\n" +
"第1步 - 理解题意:{question}\n" +
"第2步 - 分析关键信息:\n" +
"第3步 - 逐步推理(至少3步):\n" +
"第4步 - 给出最终答案:\n\n" +
"问题:{question}\n"
);
String answer = chatClient.prompt()
.user(cotTemplate.render(Map.of("question", userQuestion)))
.options(ChatOptionsBuilder.builder().withTemperature(0.0).build())
.call()
.content();
3.3 Few-CoT:Few-Shot + 思维链
最强组合——示例中包含推理过程:
PromptTemplate fewCotTemplate = PromptTemplate.from(
"请根据以下示例,按推理步骤回答问题。\n\n" +
"示例问题: 小明买了3支铅笔,每支2元,又买了1个橡皮擦3元,一共花了多少钱?\n" +
"推理: 铅笔总价=3×2=6元,加上橡皮擦3元,总共6+3=9元\n" +
"答案: 9元\n\n" +
"示例问题: 小红有10个苹果,给了小明3个,又收到5个,现在有多少个?\n" +
"推理: 10-3=7个,7+5=12个\n" +
"答案: 12个\n\n" +
"现在回答以下问题:\n" +
"问题: {question}\n" +
"推理:"
);
4. 结构化输出:让 AI 返回精确 JSON
结构化输出是 AI 应用的核心痛点之一。Spring AI 提供了**严格模式(Strict Mode)**来保证输出格式。
4.1 使用 outputParser 解析
// 定义输出结构
public record ReviewResult(
int score, // 1-10 分
List<String> pros, // 优点
List<String> cons, // 缺点
String summary // 总结
) {}
// 解析输出
String rawOutput = chatClient.prompt()
.user("分析这部电影《肖申克的救赎》的优缺点")
.call()
.content();
// 手动解析(基础方式)
ObjectMapper mapper = new ObjectMapper();
ReviewResult result = mapper.readValue(rawOutput, ReviewResult.class);
4.2 使用 BeanOutputParser(推荐)
Spring AI 提供 BeanOutputParser 自动完成解析:
// 定义 POJO
public record MovieReview(
int score,
List<String> pros,
List<String> cons,
String summary
) {}
// 使用 BeanOutputParser 自动生成 Prompt + 解析
BeanOutputParser<MovieReview> parser =
new BeanOutputParser<>(MovieReview.class);
String result = chatClient.prompt()
.user("分析电影《肖申克的救赎》的优缺点")
.defaultSystem(
"你是一位专业的影评人,用客观专业的语言评价电影。" +
"请严格按照以下JSON格式输出:{format}"
)
.param("format", parser.getFormat()) // 自动注入 JSON Schema
.call()
.content();
// 自动解析为 Java 对象
MovieReview review = parser.parse(result);
System.out.println(review.score()); // 9
System.out.println(review.pros()); // [信念与希望, 精彩的叙事...]
4.3 严格模式 Strict Output
对于生产环境,需要确保输出的 JSON 格式绝对正确,可以使用 StringOutputParser + 正则清洗:
@Service
public class StrictJsonParser {
private final ChatClient chatClient;
public StrictJsonParser(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public <T> T parseStrict(String question, Class<T> clazz) {
BeanOutputParser<T> parser =
new BeanOutputParser<>(clazz);
// 强制要求模型在 ```json ```块中输出
String prompt = question + "\n\n请严格按照以下JSON格式输出在 ```json 代码块中,不要有其他文字:\n" +
"```json\n" + parser.getFormat() + "\n```";
String raw = chatClient.prompt()
.user(prompt)
.options(
ChatOptionsBuilder.builder().withTemperature(0.0).build()
)
.call()
.content();
// 提取 JSON 内容
String json = extractJson(raw);
return parser.parse(json);
}
private String extractJson(String raw) {
Pattern pattern = Pattern.compile(
"```json\\s*(.*?)\\s*```", Pattern.DOTALL);
Matcher matcher = pattern.matcher(raw);
if (matcher.find()) {
return matcher.group(1).trim();
}
return raw.trim();
}
}
5. Advisors 顾问模式详解

Advisors 是 Spring AI 的拦截器链,类似于 Spring AOP 的 HandlerInterceptor 或 Servlet 的 Filter,在 AI 调用前后插入自定义逻辑。
5.1 Advisor 执行时机
User Request
│
▼
[Advisor 1: before() → after()]
│
▼
[Advisor 2: before() → after()]
│
▼
[ChatModel.call()] ← 实际 AI 调用
│
▼
[...after() 返回...]
5.2 内置 Advisors
Spring AI 提供多个开箱即用的 Advisors:
// 1. 对话记忆 Advisor(最常用)
MessageChatAdvisor.builder()
.chatMemory(new InMemoryChatMemory())
.build();
// 2. 信号拦截 Advisor(拦截特定信号触发动作)
SignalChatAdvisor.builder()
.signal("继续")
.advisorBehavior(...)
.build();
// 3. 重试 Advisor
RetryChatAdvisor.builder()
.maxAttempts(3)
.retryExceptions(AIException.class)
.build();
// 4. 限流 Advisor
RateLimitChatAdvisor.builder()
.tokensPerMinute(60)
.build();
5.3 自定义 Advisor
@Component
public class LoggingAdvisor implements org.springframework.ai.chat.client.advisor.Advisor {
private static final Logger log = LoggerFactory.getLogger(LoggingAdvisor.class);
@Override
public org.springframework.ai.chat.messages.Message around(
org.springframework.ai.chat.client.ChatClientRequest request,
org.springframework.ai.chat.client.ChatClientResponse response,
java.util.function.Supplier<org.springframework.ai.chat.client.ChatClientResponse> next) {
// 记录请求
log.info("【AI请求】用户输入: {}",
request.userText());
// 执行调用
var result = next.get();
// 记录响应
log.info("【AI响应】回复长度: {} 字,Token消耗: {}",
result.chatResponse().getResult().getOutput().getContent().length(),
result.chatResponse().getMetadata().getUsage());
return result;
}
@Override
public String getName() {
return "LoggingAdvisor";
}
@Override
public int getOrder() {
return 0; // 执行顺序,数字越小越先执行
}
}
5.4 Advisor 链组合
// 组合多个 Advisor
String result = chatClient.prompt()
.user(question)
.advisors(
// 1. 日志记录
new LoggingAdvisor(),
// 2. 对话记忆(session 隔离)
MessageChatAdvisor.builder()
.chatMemory(new InMemoryChatMemory())
.build(),
// 3. 重试策略
RetryChatAdvisor.builder()
.maxAttempts(3)
.build()
)
.call()
.content();
6. 实战:构建一个智能代码审查 Agent
结合 Prompt 模板 + 结构化输出 + Advisors,构建完整的代码审查服务:
@Service
@RequiredArgsConstructor
public class CodeReviewAgent {
private final ChatClient chatClient;
// 代码审查输出结构
public record CodeReviewResult(
String severity, // CRITICAL / HIGH / MEDIUM / LOW / INFO
String category, // SECURITY / PERFORMANCE / STYLE / ...
String description,
String location,
String suggestion
) {}
public List<CodeReviewResult> review(String code, String language) {
BeanOutputParser<List<CodeReviewResult>> parser =
new BeanOutputParser<>(
new ParameterizedTypeReference<>() {}
);
String systemPrompt = """
你是一位资深代码审查专家。审查{language}代码,识别以下类型的问题:
- SECURITY: 安全漏洞(SQL注入、XSS、敏感信息泄露等)
- PERFORMANCE: 性能问题(N+1查询、内存泄漏等)
- STYLE: 代码风格问题
- BEST_PRACTICE: 违反最佳实践
请输出一个JSON数组,每个问题一个对象,字段为:
severity, category, description, location, suggestion
如果没有问题,返回空数组 []。
""";
String raw = chatClient.prompt()
.system(sp)
.user("```" + language + "\n" + code + "\n```")
.param("format", parser.getFormat())
.options(
ChatOptionsBuilder.builder()
.withTemperature(0.1) // 降低随机性
.build()
)
.call()
.content();
return parser.parse(raw);
}
}
7. 常见 Prompt 反模式与优化策略
7.1 反模式
| 反模式 | 问题 | 优化方式 |
|---|---|---|
| 模糊指令 | “帮我看看代码” | 明确任务、输出格式、约束条件 |
| 过长 Prompt | 一股脑塞入所有上下文 | 拆分成多步,或用 RAG 检索 |
| 温度过高 | 随机性太强,输出不稳定 | 设置 temperature=0.0 |
| 无示例 | 模型理解偏差大 | 添加 Few-Shot 示例 |
| 指令与示例冲突 | 示例优先于指令 | 示例与指令保持一致 |
7.2 优化策略
策略一:角色定位 + 分段指令
// ❌ 模糊
"You are an assistant. Help me."
// ✅ 精确
"""
你是一位拥有10年经验的高级Java架构师。
第一步:根据业务场景,分析是否适合微服务架构;
第二步:给出架构设计图(ASCII格式);
第三步:提供核心代码示例。
"""
策略二:输出格式约束
"""
回答必须满足以下格式(严格遵守):
【结论】:一行话总结
【理由】:不超过3点
【代码示例】:Java代码(如涉及)
【风险提示】:如存在潜在问题
"""
策略三:分步验证
// 让模型先验证再执行
"""
任务:{user_task}
请先验证以下条件是否满足:
1. [条件1]
2. [条件2]
如果条件不满足,返回"INVALID: [原因]";
如果条件满足,执行任务并返回结果。
"""
8. 小结
本文系统讲解了 Spring AI 中的 Prompt 工程体系:
- Prompt 模板:从占位符渲染到模板文件管理,从单段落到多消息构建
- 高级技巧:Few-Shot 提供参考示例、Chain of Thought 引导推理
- 结构化输出:BeanOutputParser + 严格模式,保证 JSON 格式绝对正确
- Advisors 拦截器链:对话记忆、重试、限流、自定义逻辑的组合方式
- 实战:构建了一个完整的代码审查 Agent
下一篇预告:【Spring AI 实战】四、OpenAI / Anthropic / Azure——多模型适配与自动配置原理——我们将深入 Spring AI 的自动配置机制,理解如何通过配置文件切换不同 AI 服务商,以及多模型 A/B 测试、模型热切换等生产级实践。
📌 系列导航
📎 示例说明:本文以 Prompt 设计思路和代码片段为主,建议与后续 RAG 和 Function Calling 篇配合阅读。
更多推荐
所有评论(0)