摘要:提示词工程(Prompt Engineering)是发挥大模型能力的关键技术。本文结合实际项目代码,系统讲解 Spring AI 中提示词工程的四大核心策略:角色设定、少样本学习、结构化输出和思维链推理。
一、什么是提示词工程?
提示词工程是通过精心设计的提示词(Prompt),引导大模型输出符合预期结果的技术。
为什么需要提示词工程?
同样的模型,不同的提示词,结果天差地别:
❌ 差的提示词:
帮我写个故事
→ 输出:随便写一个普通故事
✅ 好的提示词:
请以科幻为主题,写一个关于 AI 觉醒的短篇故事,要求:
1. 包含悬念和反转
2. 人物对话占比 40%
3. 字数 1000 字以内
→ 输出:高质量、符合要求的科幻故事
提示词工程的核心策略
- Role(角色设定):告诉 AI 它是谁
- Shot(少样本学习):给 AI 看几个例子
- Structure(结构化输出):规定输出格式
- Step(思维链):让 AI 逐步思考
二、策略一:角色设定(Role Prompting)
核心思想
通过 SystemMessage 定义 AI 的角色、性格和行为准则,让它以特定身份回答问题。
代码实现
@RestController
@RequestMapping("/prompt")
public class RolePromptingController implements InitializingBean {
@Autowired
private ChatModel chatModel;
private ChatClient chatClient;
@Override
public void afterPropertiesSet() {
// 设定默认角色:美食评论家
chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是一个专业的美食评论家,擅长用生动的语言描述菜品,评价要客观但带有个人风格")
.build();
}
@GetMapping("/food-review")
public String foodReview(String restaurant) {
return chatClient.prompt("评价一下" + restaurant).call().content();
}
}
测试效果
# 输入
curl "http://localhost:8000/prompt/food-review?restaurant=海底捞"
# 输出(美食评论家风格)
"海底捞的服务确实无可挑剔,但说实话,火锅的味道还停留在'不会出错但也不惊艳'的阶段。毛肚的脆度和牛油锅的醇厚是亮点,可如果你在追求味蕾的极致体验,可能会觉得少了点惊喜。"
角色设定技巧
1. 明确身份
.defaultSystem("""
你是一个拥有 10 年经验的 Java 架构师,
擅长 Spring Boot、微服务、分布式系统设计。
""")
2. 定义行为准则
.defaultSystem("""
你是一个耐心的编程老师,
1. 用简单易懂的语言解释概念
2. 提供代码示例
3. 指出常见错误
4. 鼓励学生思考
""")
3. 设定语气风格
// 正式专业
.defaultSystem("你是一位专业的技术顾问,请用正式的语言回答问题")
// 幽默风趣
.defaultSystem("你是一个幽默的程序员,喜欢用段子解释技术概念")
// 简洁直接
.defaultSystem("你是一个高效的技术专家,只说重点,不要废话")
实战场景
@Bean
public ChatClient teacherClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultSystem("""
你是一个耐心的 Python 编程老师,
教学风格:
1. 先解释概念,再给代码示例
2. 代码必须包含详细注释
3. 指出新手容易犯的错误
4. 最后给一个练习题
""")
.build();
}
三、策略二:少样本学习(Few-Shot Learning)
核心思想
通过提供几个输入-输出示例,让 AI 学习任务的模式和规则,而不需要明确说明。
代码实现
@GetMapping("/intent-recognition")
public String recognizeIntent(String message) {
return chatClient.prompt()
.system("""
请识别用户输入的真实意图,并返回对应的操作类型。
参考示例:
Input:明天北京天气怎么样?
Output:{"intent": "weather_query", "location": "北京", "time": "明天"}
Input:帮我订一张后天去上海的高铁票
Output:{"intent": "ticket_book", "type": "train", "from": "当前城市", "to": "上海", "date": "后天"}
Input:推荐几部好看的电影
Output:{"intent": "recommendation", "category": "movie"}
""")
.user(message)
.call()
.content();
}
测试效果
# 输入
curl "http://localhost:8000/prompt/intent-recognition?message=我想定一个下周去成都的机票,最好是上午的航班"
# 输出
{"intent": "ticket_book", "type": "flight", "to": "成都", "date": "下周", "preference": "上午航班"}
少样本学习的要素
1. 任务描述(可选)
请你根据用户输入的问题做改写,主要有以下改写策略:
1、改写其中的错别字。
2、做内容精简
2. 示例(核心)
Input:ni好
Output :{"错别字改写":"你好","内容精简":""}
Input:我今天心情不错...
Output :{"错别字改写":"","内容精简":"今天是什么天气?"}
3. 用户输入
.user(message)
示例设计原则
✅ 好的示例
.system("""
情感分析任务:
示例 1:
Input:这家餐厅太好吃了,服务也很棒!
Output:{"sentiment": "positive", "score": 0.95}
示例 2:
Input:菜太咸了,等了一个小时还没上菜
Output:{"sentiment": "negative", "score": 0.15}
示例 3:
Input:味道一般,价格还行
Output:{"sentiment": "neutral", "score": 0.50}
""")
优点:
- 覆盖不同情况(正面、负面、中性)
- 输出格式一致
- 标签清晰
❌ 差的示例
.system("""
情感分析:
示例:好吃 -> 正面
""")
缺点:
- 示例太少,无法覆盖边界情况
- 格式不统一
- 缺少具体数值
实战场景:文本分类
@GetMapping("/classify")
public String classifyText(String text) {
return chatClient.prompt()
.system("""
请将用户的问题分类为以下类型:
- code:代码相关
- math:数学计算
- writing:写作创作
- chat:闲聊聊天
示例:
Input:如何用 Spring Boot 实现登录功能?
Output:{"category": "code"}
Input:1+1等于几?
Output:{"category": "math"}
Input:写一首关于春天的诗
Output:{"category": "writing"}
Input:你好,今天天气不错
Output:{"category": "chat"}
""")
.user(text)
.call()
.content();
}
四、策略三:结构化输出(Structured Output)
核心思想
明确要求 AI 以特定格式(如 JSON、XML、Markdown)输出结果,方便程序解析和后续处理。
代码实现
@GetMapping("/analyze")
public String structuredAnalyze(String company) {
return chatClient.prompt()
.system("你是一个资深的商业分析师")
.user("请分析%s公司的商业模式,以 JSON 格式输出,包含:公司名称、核心业务、盈利模式、竞争优势、潜在风险".formatted(company))
.call()
.content();
}
测试效果
# 输入
curl "http://localhost:8000/prompt/analyze?company=特斯拉"
# 输出
{
"company": "Tesla Inc.",
"core_business": "电动汽车、清洁能源、自动驾驶技术",
"revenue_model": "车辆销售、FSD软件订阅、太阳能产品、充电服务",
"competitive_advantages": [
"领先的电池技术和超级充电网络",
"Autopilot 自动驾驶系统",
"强大的品牌影响力",
"垂直整合的供应链"
],
"risks": [
"产能瓶颈",
"激烈的市场竞争",
"监管政策变化"
]
}
结构化输出技巧
1. 明确指定格式
// JSON 格式
.user("请以 JSON 格式输出,包含以下字段:name, age, skills")
// Markdown 表格
.user("请以 Markdown 表格形式输出,包含列:姓名、年龄、技能")
// XML 格式
.user("请以 XML 格式输出,根节点为 person")
2. 提供 Schema 定义
.user("""
请以 JSON 格式输出,严格遵循以下 Schema:
{
"person": {
"name": "string",
"age": "number",
"skills": ["string"],
"experience_years": "number"
}
}
""")
3. 使用 JSON Mode(如果模型支持)
ChatOptions options = DashScopeChatOptions.builder()
.responseFormat(ResponseFormat.builder()
.type(ResponseFormat.Type.JSON_OBJECT)
.build())
.build();
return chatClient.prompt(message)
.options(options)
.call()
.content();
实战场景:信息抽取
@GetMapping("/extract")
public String extractInfo(String text) {
return chatClient.prompt()
.system("你是一个信息抽取专家")
.user("""
从以下文本中抽取关键信息,以 JSON 格式输出:
文本:%s
输出格式:
{
"persons": ["人名列表"],
"locations": ["地点列表"],
"organizations": ["组织机构列表"],
"dates": ["日期列表"],
"key_events": ["关键事件列表"]
}
""".formatted(text))
.call()
.content();
}
五、策略四:思维链推理(Chain of Thought)
核心思想
让 AI 按照特定的步骤逐步思考,而不是直接给出答案,提高复杂问题的准确率。
代码实现
@GetMapping("/story-analysis")
public Flux<String> storyAnalysis(String story, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return chatClient.prompt("""
请分析以下故事,并输出关键信息。请按照以下步骤逐步思考,最终只输出 JSON 即可:
step 1-用一句话概括故事主题
step 2-识别故事中的主要人物及其角色
step 3-分析故事的情感基调(正面/负面/中性)
step 4-提取故事的关键转折点
step 5-输出一个 JSON 对象,包含以下键:theme(主题), characters(人物列表), sentiment(情感), plot_twists(转折点列表)
最终输出格式:
{
"theme": "故事主题",
"characters": ["人物1", "人物2"],
"sentiment": "情感基调",
"plot_twists": ["转折点1", "转折点2"]
}
""")
.system("你是专业的文学分析师")
.user(story)
.stream()
.content();
}
测试效果
# 输入
curl "http://localhost:8000/prompt/story-analysis?story=小明一直梦想成为画家,但父亲希望他学医。高考那年,他偷偷报考了美术学院。成绩公布那天,父亲拿着他的录取通知书,沉默了很久后说:'去吧,别后悔。'"
# 输出(逐步思考过程,最终输出)
{
"theme": "追求梦想与家庭和解",
"characters": ["小明(追梦者)", "父亲(严厉但理解)"],
"sentiment": "positive",
"plot_twists": ["偷偷报考美术学院", "父亲最终支持"]
}
思维链的优势
1. 提高复杂任务的准确率
直接问:这篇文章有几个人名?
→ 可能漏掉或数错
分步问:
step 1-概括内容
step 2-翻译
step 3-列出人名
step 4-统计数量
→ 准确率高
2. 便于调试和验证
// 可以看到 AI 的每一步思考过程
step 1: 张三、李四和王五一起去旅游
step 2: Zhang San, Li Si and Wang Wu went traveling together
step 3: 人名:Zhang San, Li Si, Wang Wu
step 4: {"num_names": 3}
思维链设计模式
模式 1:分析 → 综合
.system("""
请按以下步骤分析代码:
step 1-识别代码的主要功能
step 2-分析代码的时间复杂度
step 3-指出潜在的 bug 或性能问题
step 4-给出优化建议
step 5-输出优化后的代码
最终输出一个 JSON:
{
"functionality": "功能描述",
"time_complexity": "时间复杂度",
"issues": ["问题列表"],
"suggestions": ["建议列表"],
"optimized_code": "优化后的代码"
}
""")
模式 2:分解 → 解决 → 合并
.system("""
解决数学问题的步骤:
step 1-理解问题,提取已知条件
step 2-确定解题思路和方法
step 3-分步计算,展示详细过程
step 4-验证答案是否正确
step 5-输出最终答案
最终输出:
{
"understanding": "问题理解",
"approach": "解题思路",
"steps": ["计算步骤"],
"verification": "验证过程",
"answer": "最终答案"
}
""")
模式 3:评估 → 对比 → 推荐
.system("""
技术选型分析步骤:
step 1-列出所有候选方案
step 2-评估每个方案的优缺点
step 3-对比关键指标(性能、易用性、生态)
step 4-根据使用场景给出推荐
最终输出:
{
"candidates": ["候选方案"],
"comparison": {
"方案1": {"pros": [], "cons": []},
"方案2": {"pros": [], "cons": []}
},
"recommendation": "推荐方案",
"reason": "推荐理由"
}
""")
六、组合策略:提示词工程的最佳实践
实战案例:智能代码审查助手
@GetMapping("/code-review")
public String codeReview(String code) {
return chatClient.prompt()
.system("""
你是一个拥有 15 年经验的资深 Java 架构师,
擅长代码审查、性能优化和设计模式。
""") // 角色设定
.user("""
请审查以下代码,并按照以下步骤输出:
step 1-分析代码的主要功能
step 2-识别代码中的问题(bug、性能、安全)
step 3-评估代码的可维护性和可读性
step 4-给出具体的优化建议
step 5-提供优化后的代码
参考示例:
Input:
public void process(List<String> data) {
for (int i = 0; i < data.size(); i++) {
String item = data.get(i);
if (item != null) {
System.out.println(item.toUpperCase());
}
}
}
Output:
{
"functionality": "遍历列表并打印大写字符串",
"issues": [
"使用传统 for 循环,不够简洁",
"没有处理空列表的情况",
"直接打印到控制台,不够灵活"
],
"suggestions": [
"使用增强 for 循环或 Stream API",
"添加空值检查",
"使用日志框架替代 System.out"
],
"optimized_code": "public void process(List<String> data) {\\n if (data == null || data.isEmpty()) return;\\n \\n data.stream()\\n .filter(Objects::nonNull)\\n .map(String::toUpperCase)\\n .forEach(log::info);\\n}"
}
现在请审查以下代码:
%s
最终以 JSON 格式输出。
""".formatted(code)) // 少样本 + 思维链 + 结构化输出
.call()
.content();
}
这个提示词组合了:
- ✅ 角色设定:资深 Java 架构师
- ✅ 思维链:5 步审查流程
- ✅ 少样本学习:提供了一个完整示例
- ✅ 结构化输出:要求 JSON 格式
七、提示词工程的常见陷阱
❌ 陷阱 1:提示词太长太啰嗦
// 错误示例
.system("""
你是一个助手,你应该回答问题,你应该帮助用户,
你要有礼貌,要专业,要准确,要详细,要全面,
不要说谎,不要编造,不要误导...(废话太多)
""")
// 正确示例
.system("你是一个专业的技术顾问,回答要准确、简洁、实用")
❌ 陷阱 2:示例不一致
// 错误示例:格式不统一
.system("""
示例1:好吃 -> 正面情感
示例2:{"sentiment": "负面"}
示例3:neutral
""")
// 正确示例:格式统一
.system("""
示例1:{"sentiment": "positive", "score": 0.95}
示例2:{"sentiment": "negative", "score": 0.15}
示例3:{"sentiment": "neutral", "score": 0.50}
""")
❌ 陷阱 3:缺少边界条件
// 错误示例:只给正常情况
.system("把数字乘以 2。示例:5 -> 10")
// 正确示例:覆盖边界
.system("""
把数字乘以 2。
示例:
5 -> 10
0 -> 0
-3 -> -6
1000000 -> 2000000
""")
八、完整 Controller 示例
package cn.hollis.llm.llmentor.controller;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/prompt")
public class PromptEngineeringController implements InitializingBean {
@Autowired
private ChatModel chatModel;
private ChatClient chatClient;
/**
* 策略一:角色设定 - 美食评论家
*/
@GetMapping("/food-review")
public String foodReview(String restaurant) {
return chatClient.prompt("评价一下" + restaurant).call().content();
}
/**
* 策略二:少样本学习 - 意图识别
*/
@GetMapping("/intent-recognition")
public String recognizeIntent(String message) {
return chatClient.prompt()
.system("""
请识别用户输入的真实意图,并返回对应的操作类型。
参考示例:
Input:明天北京天气怎么样?
Output:{"intent": "weather_query", "location": "北京", "time": "明天"}
Input:帮我订一张后天去上海的高铁票
Output:{"intent": "ticket_book", "type": "train", "from": "当前城市", "to": "上海", "date": "后天"}
Input:推荐几部好看的电影
Output:{"intent": "recommendation", "category": "movie"}
""")
.user(message)
.call()
.content();
}
/**
* 策略三:结构化输出 - 商业分析
*/
@GetMapping("/analyze")
public String structuredAnalyze(String company) {
return chatClient.prompt()
.system("你是一个资深的商业分析师")
.user("请分析%s公司的商业模式,以 JSON 格式输出,包含:公司名称、核心业务、盈利模式、竞争优势、潜在风险".formatted(company))
.call()
.content();
}
/**
* 策略四:思维链推理 - 故事分析
*/
@GetMapping("/story-analysis")
public Flux<String> storyAnalysis(@RequestParam(value = "story") String story,
HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return chatClient.prompt("""
请分析以下故事,并输出关键信息。请按照以下步骤逐步思考,最终只输出 JSON 即可:
step 1-用一句话概括故事主题
step 2-识别故事中的主要人物及其角色
step 3-分析故事的情感基调(正面/负面/中性)
step 4-提取故事的关键转折点
step 5-输出一个 JSON 对象,包含以下键:theme(主题), characters(人物列表), sentiment(情感), plot_twists(转折点列表)
最终输出格式:
{
"theme": "故事主题",
"characters": ["人物1", "人物2"],
"sentiment": "情感基调",
"plot_twists": ["转折点1", "转折点2"]
}
""")
.system("你是专业的文学分析师")
.user(story)
.stream()
.content();
}
@Override
public void afterPropertiesSet() throws Exception {
// 默认角色:美食评论家
chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是一个专业的美食评论家,擅长用生动的语言描述菜品,评价要客观但带有个人风格")
.build();
}
}
九、提示词工程检查清单
在实际开发中,使用以下检查清单优化你的提示词:
✅ 角色设定检查
✅ 少样本学习检查
✅ 结构化输出检查
✅ 思维链检查
十、总结
提示词工程是发挥大模型潜力的关键技术。在 Spring AI 中,我们可以通过 ChatClient 优雅地实现四大核心策略:
| 策略 | 核心思想 | 适用场景 | 代码方法 |
|---|---|---|---|
| 角色设定 | 定义 AI 身份 | 统一回答风格 | defaultSystem() |
| 少样本学习 | 提供示例 | 模式识别任务 | .system("示例...") |
| 结构化输出 | 规定格式 | 程序解析 | "以 JSON 格式输出" |
| 思维链推理 | 分步思考 | 复杂问题 | "step 1...step 2..." |
最佳实践:
- 组合使用多种策略,效果更佳
- 示例要典型、覆盖边界情况
- 输出格式要明确、统一
- 复杂任务分解为多步骤
掌握提示词工程,让大模型真正成为你的得力助手!
参考资源:
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!
浙公网安备 33010602011771号