随着大语言模型(LLM)的飞速发展,如何有效地设计提示词(Prompt)成为了开发人员必须掌握的一项关键技能。Spring AI 框架为 Java 开发者提供了简洁而强大的 API,使提示工程技术的实现变得简单高效。本文将详细介绍如何使用 Spring AI 实现各种提示工程技巧,帮助您构建更智能、更可靠的 AI 应用程序。
文中的示例和模式基于全面的 提示工程指南,展示了如何将理论概念转化为实际可用的 Java 代码实现。
1. 配置与初始化
在深入了解提示工程技术之前,我们先看看如何设置和调整 Spring AI 中的大语言模型配置。
选择 LLM 提供商
Spring AI 的优势之一是支持多种 LLM 提供商(如 OpenAI、Anthropic、Google Vertex AI、AWS Bedrock、Ollama 等),让您可以在不改变应用代码的情况下切换不同的模型提供商。只需添加所需的启动依赖 spring-ai-starter-model-<模型提供商名称>
即可。
例如,添加 Open AI 协议的支持:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
配置连接属性:
spring.ai.openai.api-key=sk-b30f6476fa9645ce8cf7e922073bf499
spring.ai.openai.chat.options.model=deepseek-chat
spring.ai.openai.base-url=https://api.deepseek.com/v1```
您可以指定具体的模型版本:
.options(ChatOptions.builder()
.model("deepseek-chat") // 请求过程中修改模型
.build())
LLM 输出配置
Spring AI 提供了多种配置选项,让您可以通过 ChatOptions 构建器控制模型输出的各个方面。
Temperature(温度)
Temperature 控制模型回答的随机性或 “创造性 “:
-
• 较低值 (0.0-0.3):更确定性、更集中的回答,适合事实性问题和分类任务 -
• 中等值 (0.4-0.7):在确定性和创造性之间取得平衡,适合一般用例 -
• 较高值 (0.8-1.0):更具创造性、多样性和潜在的惊喜性,适合创意写作和头脑风暴
.options(ChatOptions.builder()
.temperature(0.1) // 非常确定性的输出
.build())
输出长度(MaxTokens)
maxTokens
参数限制模型在回答中可以生成的标记(词片段)数量:
-
• 低值 (5-25):适合单个词、短语或分类标签 -
• 中等值 (50-500):适合段落或简短解释 -
• 高值 (1000+):适合长篇内容、故事或复杂解释
.options(ChatOptions.builder()
.maxTokens(250) // 中等长度回答
.build())
采样控制(Top-K 和 Top-P)
这些参数提供了对生成过程中标记选择的精细控制:
-
• Top-K:将标记选择限制在 K 个最可能的下一个标记中 -
• Top-P(核采样):动态选择累积概率超过 P 的最小标记集
.options(ChatOptions.builder()
.topK(40) // 只考虑前 40 个最可能的标记
.topP(0.8) // 从覆盖 80% 概率质量的标记中采样
.build())
结构化响应格式
除了使用 .content()
获取纯文本响应外,Spring AI 还提供了将 LLM 响应直接映射到 Java 对象的简便方法:
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentiment result = chatClient.prompt("...")
.call()
.entity(Sentiment.class);
模型特定选项
虽然通用的 ChatOptions
提供了跨不同 LLM 提供商的一致接口,但 Spring AI 也提供了模型特定的选项类,以便访问提供商特有的功能:
// 使用 OpenAI 特定选项
OpenAiChatOptions openAiOptions = OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.2)
.responseFormat(new ResponseFormat("json_object")) // ollama 为 json_schema
.build();
2. 提示工程技术实战
2.1 零样本提示(Zero-Shot Prompting)
零样本提示是指在不提供任何示例的情况下要求 AI 执行任务,测试模型从零开始理解和执行指令的能力。
public voidpt_zero_shot_prompting(ChatClient chatClient) {
// 简单的零样本翻译
Stringtranslation= chatClient
.prompt("将以下英文文本翻译成中文:'Spring AI makes prompt engineering easy'")
.call()
.content();
// 零样本分类
Stringclassification= chatClient
.prompt("判断以下评论的情感是正面、中性还是负面:'这家餐厅的服务太差了,但是食物还不错 '")
.options(ChatOptions.builder()
.temperature(0.1) // 低温度,更确定性的回答
.build())
.call()
.content();
}
零样本提示适用于直接的任务,尤其是当模型在训练中可能已经见过类似示例时。优点是提示简短,但复杂任务的表现可能不稳定。
2.2 少样本提示(Few-Shot Prompting)
少样本提示通过提供几个示例来指导模型理解任务模式,尤其适用于需要特定格式或风格的任务。
public void pt_few_shot_prompting(ChatClient chatClient) {
String fewShotClassification = chatClient
.prompt("""
将以下产品评论分类为正面或负面:
评论:"这款手机太棒了,拍照效果非常好!"
分类:正面
评论:"送货慢,包装破损,很失望。"
分类:负面
评论:"使用一周后电池开始出现问题,不推荐购买。"
分类:
""")
.call()
.content();
}
少样本提示在需要一致性和特定输出格式的场景中特别有效,例如文本分类、实体提取和结构化数据生成。通过精心选择典型示例,可以显著提高模型性能。
2.3 思维链提示(Chain-of-Thought Prompting)
思维链提示技术鼓励模型逐步思考问题,特别适用于需要推理的复杂任务。
public voidpt_chain_of_thought_prompting(ChatClient chatClient) {
Stringsolution= chatClient
.prompt("""
小明有 5 个苹果,小红给了他 2 个苹果,然后他吃掉了 3 个,又送给了小华 1 个。
小明现在有多少个苹果?请一步一步思考。
""")
.call()
.content();
// 少样本思维链
StringfewShotCoT= chatClient
.prompt("""
问题:如果 8 个人分享 4 块比萨,每人可以得到多少块?
思考:总共有 4 块比萨,需要分给 8 个人。每块比萨分成 2 份,8 个人就需要 8 份。
4 块比萨可以分成 4 * 2 = 8 份。所以每个人可以得到 8/8 = 1 份,也就是半块比萨。
答案:1/2 块比萨
问题:火车以每小时 120 公里的速度行驶。2.5 小时后,火车行驶了多少公里?
思考:
""")
.call()
.content();
}
思维链提示通过展示模型的推理过程,不仅提高了解决复杂问题的准确性,还增强了答案的可解释性和可验证性。研究表明,这种方法在数学问题、逻辑推理和多步骤任务中特别有效。
2.4 自一致性提示(Self-Consistency Prompting)
自一致性提示技术通过生成多个独立解决方案并选择最常见答案来提高准确性,特别适用于关键决策场景。
public voidpt_self_consistency_prompting(ChatClient chatClient) {
// 使用思维链生成多个解决方案路径
List<String> solutions = newArrayList<>();
for (inti=0; i < 5; i++) {
Stringsolution= chatClient
.prompt("""
一个商店正在进行 "买 2 送 1" 的促销活动。如果每件商品原价 15 元,
买 7 件需要多少钱?请逐步思考。
""")
.options(ChatOptions.builder()
.temperature(0.7) // 较高温度增加多样性
.build())
.call()
.content();
solutions.add(solution);
}
// 选择最一致的答案
StringfinalSolution= chatClient
.prompt()
.user(u -> u.text("""
分析以下解决方案,选择最准确的一个:
{solutions}
""").param("solutions", String.join("\n---\n", solutions)))
.options(ChatOptions.builder()
.temperature(0.1) // 低温度保证确定性
.build())
.call()
.content();
}
自一致性提示在需要高准确性的场景中非常有价值,例如数学计算、代码生成和关键决策支持。通过综合多个思考路径,它能有效减少单一推理链中的错误积累。
2.5 思想树提示(Tree-of-Thoughts Prompting)
思想树提示是思维链的扩展,允许模型探索多个推理分支,适用于需要前瞻性思考的复杂问题。
public void pt_tree_of_thoughts_prompting(ChatClient chatClient) {
String solution = chatClient
.prompt("""
你正在玩数独游戏,需要填写 3x3 网格的最后一行。
已知数据:
第一行:1 5 9
第二行:6 7 3
第三行:? ? ?
请探索不同可能性,考虑每种选择带来的约束,找出符合数独规则的第三行数字组合。
""")
.call()
.content();
}
思想树提示在解决需要探索多种可能性的问题时尤为有效,例如游戏策略、复杂决策和算法设计。通过系统化地追踪不同解决路径,它可以找到更优的解决方案。
2.6 抽象提示(Abstractions in Prompting)
抽象提示技术鼓励模型首先 “退一步 “,从更高层次理解问题,然后再提供具体解决方案。
public void pt_abstractions_in_prompting(ChatClient chatClient) {
String solution = chatClient
.prompt("""
解决这个编程问题:实现一个函数,找出数组中所有和为特定值的数字对。
在回答之前,先思考这个问题的核心是什么,可以用什么算法解决,
有哪些边界情况需要考虑,然后再提供解决方案。
""")
.call()
.content();
}
抽象提示通过鼓励模型进行高层次思考,帮助解决复杂问题,特别适用于算法设计、系统架构和战略规划等需要全局视角的任务。
2.7 角色提示(Role Prompting)
角色提示让 AI 扮演特定角色,激发其基于角色背景知识的特定能力。
public voidpt_role_prompting(ChatClient chatClient) {
// 系统消息角色
StringroleWithSystem= chatClient
.prompt()
.system("你是一位经验丰富的 Java 开发专家,尤其擅长 Spring 框架 ")
.user("解释一下 Spring AOP 的原理和使用场景 ")
.call()
.content();
// 直接在用户提示中定义角色
StringroleInPrompt= chatClient
.prompt("""
作为一名资深数据库架构师,请评估将传统关系型数据库迁移到 NoSQL 数据库的优缺点,
特别是对于一个高流量的电子商务网站。考虑性能、可扩展性和一致性等方面。
""")
.call()
.content();
}
角色提示通过赋予模型特定 “身份 “,能够激发其在相关领域的专业知识,特别适用于需要特定专业背景、风格或视角的场景。通过精心设计角色,可以获得更专业、更多样化的回答。
2.8 自动提示工程(Automatic Prompt Engineering)
自动提示工程(APE)是一种元级别方法,使用 AI 来优化提示本身。
public voidpt_automatic_prompt_engineering(ChatClient chatClient) {
// 生成指令变体
StringorderVariants= chatClient
.prompt("""
我们有一个乐队周边 T 恤网店,为了训练聊天机器人,我们需要多种下单方式的表述:
"一件 S 码的 Metallica T 恤 "。请生成 10 个语义相同但表达不同的变体。
""")
.options(ChatOptions.builder()
.temperature(1.0) // 高温度增加创造性
.build())
.call()
.content();
// 评估并选择最佳变体
Stringoutput= chatClient
.prompt()
.user(u -> u.text("""
请对以下变体进行 BLEU (Bilingual Evaluation Understudy) 评估:
----
{variants}
----
选择评分最高的指令候选。
""").param("variants", orderVariants))
.call()
.content();
}
APE 在优化生产系统的提示、解决手动提示工程难以应对的挑战性任务,以及系统性地提高提示质量方面特别有价值。
2.9 代码提示(Code Prompting)
代码提示专门针对代码相关任务,利用 LLM 理解和生成编程语言的能力。
// 编写代码
publicvoidpt_code_prompting_writing_code(ChatClient chatClient) {
StringbashScript= chatClient
.prompt("""
用 Bash 编写一个脚本,询问用户输入文件夹名称,
然后将该文件夹中的所有文件重命名,在文件名前加上 "draft_" 前缀。
""")
.options(ChatOptions.builder()
.temperature(0.1) // 低温度保证确定性代码
.build())
.call()
.content();
}
// 解释代码
publicvoidpt_code_prompting_explaining_code(ChatClient chatClient) {
Stringcode="""
#!/bin/bash
echo "输入文件夹名称:"
read folder_name
if [ ! -d "$folder_name" ]; then
echo "文件夹不存在。"
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "文件重命名成功。"
""";
Stringexplanation= chatClient
.prompt()
.user(u -> u.text("""
解释以下 Bash 代码的功能:
```
{code}
```
""").param("code", code))
.call()
.content();
}
// 翻译代码
publicvoidpt_code_prompting_translating_code(ChatClient chatClient) {
StringbashCode="""
#!/bin/bash
echo "输入文件夹名称:"
read folder_name
if [ ! -d "$folder_name" ]; then
echo "文件夹不存在。"
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "文件重命名成功。"
""";
StringpythonCode= chatClient
.prompt()
.user(u -> u.text("""
将以下 Bash 代码翻译成 Python:
{code}
""").param("code", bashCode))
.call()
.content();
}
代码提示在自动代码文档生成、原型设计、学习编程概念和编程语言转换方面特别有价值。结合少样本提示或思维链等技术可以进一步提高效果。
结语
Spring AI 为实现各种提示工程技术提供了优雅的 Java API。通过结合这些技术与 Spring 强大的实体映射和流畅的 API,开发人员可以构建复杂而高效的 AI 应用程序。
最有效的方法往往是结合多种技术——例如,将系统提示与少样本示例结合,或将思维链与角色提示结合。Spring AI 灵活的 API 使这些组合变得直观且易于实现。
对于生产应用,请记住:
-
测试不同参数(温度、top-k、top-p)下的提示效果 -
考虑对关键决策使用自一致性技术 -
利用 Spring AI 的实体映射实现类型安全的响应 -
使用上下文提示提供应用程序特定知识
通过这些技术和 Spring AI 强大的抽象,您可以创建稳健的 AI 应用,提供一致且高质量的结果。
本文来源:https://mp.weixin.qq.com/s/qWIZHNkYi-8pJGPdOvecRQ , 翻译自 https://spring.io/blog/2025/04/14/spring-ai-prompt-engineering-patterns
没有回复内容