提示工程模式
基于全面的《提示工程指南》的提示工程技术的实际实现。该指南涵盖了有效提示工程的理论、原则和模式,而在这里我们演示如何将这些概念转化为使用 Spring AI 的流式 ChatClient API 的可运行 Java 代码。本文使用的演示源代码可在以下位置获取:提示工程模式示例。
配置
配置部分概述了如何使用 Spring AI 设置和调优大型语言模型(LLM)。它涵盖了为您的使用场景选择合适的 LLM 提供商,以及配置控制模型输出质量、风格和格式的重要生成参数。
LLM 提供商选择
在进行提示工程时,您首先需要选择一个模型。Spring AI 支持 多个 LLM 提供商(例如 OpenAI、Anthropic、Google Vertex AI、AWS Bedrock、Ollama 等),允许您在不更改应用代码的情况下切换提供商——只需更新配置即可。只需添加所选的启动依赖项 spring-ai-starter-model-<MODEL-PROVIDER-NAME>。例如,以下是启用 Anthropic Claude API 的方式:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
您可以像这样指定 LLM 模型名称:
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest") // 使用 Anthropic 的 Claude 模型
.build())
有关启用每个模型的详细信息,请参阅 参考文档。
LLM 输出配置
在深入提示工程技术之前,首先需要了解如何配置 LLM 的输出行为。Spring AI 提供了一系列配置选项,可通过 ChatOptions 构建器控制模型生成的各个方面。
所有配置既可以像下面示例一样通过代码进行设置,也可以在应用启动时通过 Spring 配置属性进行设置。
Temperature(温度)
Temperature 控制模型回复的随机性或 “创造性”。
- 低值(0.0–0.3):更确定、专注的回答。适用于事实性问题、分类任务或需要高度一致性的场景。
- 中值(0.4–0.7):在确定性与创造性之间取得平衡,适用于大多数通用场景。
- 高值(0.8–1.0):更具创造性、变化更大、甚至可能产生一些出人意料的答案。适用于写作、头脑风暴等任务。
.options(ChatOptions.builder()
.temperature(0.1) // 非常确定性的输出
.build())
在提示工程中理解温度非常关键,因为不同技术往往需要不同的温度设置。
输出长度(MaxTokens)
maxTokens 限制模型可以生成的 token(词片段)数量。
- 低值(5–25):适合单词、短语或分类标签。
- 中值(50–500):适合段落或简短说明。
- 高值(1000+):适合长文本、故事或复杂解释。
.options(ChatOptions.builder()
.maxTokens(250) // 中等长度输出
.build())
合理设置输出长度可以避免答复过短或过于冗长。
采样控制(Top-K 和 Top-P)
这些参数为生成过程提供更细粒度的控制。
- Top-K:限制模型只从概率最高的 K 个候选 token 中选择。较高的值(如 40–50)会带来更多多样性。
- Top-P(核采样):动态选择累计概率超过 P 的最小 token 集合。常用范围为 0.8–0.95。
.options(ChatOptions.builder()
.topK(40) // 只考虑概率最高的 40 个 token
.topP(0.8) // 从覆盖 80% 概率质量的 token 中采样
.build())
这些采样参数通常需要与温度一起搭配使用,以塑造回复风格。
Top-K 和 Top-P 参数是一个通过调整采样策略来平衡生成文本的 随机性(多样性)和连贯性(准确性) 的过程。
Top-K 参数的配置
参数范围:K 必须是大于或等于 1 的整数。
| K 值 | 效果 | 应用场景 | 建议值 |
|---|---|---|---|
| K = 1 | 贪婪解码(Greedy Decoding):模型始终选择概率最高的词,没有随机性,输出稳定且可预测。 | 适用于翻译、代码生成等需要高确定性的任务。 | —— |
| K ≥ 50 | 高随机性:候选词数量多,文本多样性高,但更容易跑题或出现不合语法的内容。 | 适用于创意写作、头脑风暴、诗歌等。 | —— |
| K = 0 | 禁用 Top-K 采样。 | 推荐在主要使用 Top-P 时禁用 Top-K。 | 建议在 Top-P 主导时使用。 |
Top-P 参数的配置 (核采样)
参数范围:P 是一个介于 0.0 到 1.0 之间的浮点数。
| P 值 | 效果 | 应用场景 | 建议值 |
|---|---|---|---|
| P = 1.0 | 禁用 Top-P 采样:保留所有词汇,等同于完全不限制的随机采样。 | 不推荐,除非用于特定研究目的。 | —— |
| P = 0.9 ~ 0.95 | 平衡与标准设置:保留大部分高概率词,同时加入适度随机性。 | 最常用、通用、推荐设置,适用于对话、摘要、普通文本生成。 | 强烈推荐 |
| P < 0.8 | 低随机性:仅保留少数高概率词,生成内容更保守。 | 适用于需要较高连贯性的任务,但比 K=1 更具灵活性。 | 中度推荐 |
| P = 0 | 禁用 Top-P 采样。 | 当主要依赖 Top-K 时使用。 | 建议在主要用 Top-K 时设置 |
组合使用 Top-K 和 Top-P
在许多模型库中,您可以同时设置 Top-K 和 Top-P。在这种情况下,模型将 同时应用这两个限制:
- 模型首先根据
Top-K筛选出 K 个概率最高的词汇。 - 然后,在剩下的这 K 个词汇中,模型再应用 Top-P 规则,只保留累积概率和达到 P 的最小子集。
- 即使 P=0.9 的集合特别大,也不会超过 K=50 个词的上限。
- 即使 K=50 的集合中包含了一些概率极低的词,这些词也会被 P=0.9 的限制剔除。
结构化响应格式
除了使用 .content() 获取纯文本响应,Spring AI 还能通过 .entity() 直接将 LLM 回复映射到 Java 对象。
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentiment result = chatClient.prompt("...")
.call()
.entity(Sentiment.class);
结合系统提示让模型返回结构化数据时,该功能尤其强大。
模型特定选项(Model-Specific Options)
虽然统一的 ChatOptions 可在不同 LLM 提供商间保持一致性,但 Spring AI 也提供了 “模型特定的选项类”,用于访问各提供商的独有功能。
这些选项类在保留共同接口的同时,暴露提供商特有的高级配置。
// 示例:OpenAI 的模型特定选项
OpenAiChatOptions openAiOptions = OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.2)
.frequencyPenalty(0.5) // OpenAI 特有参数
.presencePenalty(0.3) // OpenAI 特有参数
.responseFormat(new ResponseFormat("json_object")) // OpenAI JSON 模式
.seed(42) // OpenAI 特有的确定性生成
.build();
String result = chatClient.prompt("...")
.options(openAiOptions)
.call()
.content();
// 例:Anthropic 的模型特定选项
AnthropicChatOptions anthropicOptions = AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.2)
.topK(40) // Anthropic 特有参数
.thinking(AnthropicApi.ThinkingType.ENABLED, 1000) // Anthropic 思维链配置
.build();
String result = chatClient.prompt("...")
.options(anthropicOptions)
.call()
.content();
每个模型提供商都有自己的选项类(例如 OpenAiChatOptions、AnthropicChatOptions、MistralAiChatOptions),用于暴露其特有参数。
这种设计使你可以:
- 使用可移植的
ChatOptions实现跨平台兼容 - 或使用模型特定选项以充分利用特定模型的高级能力
但请注意:使用模型特定选项会使代码与该提供商绑定,从而降低可移植性。 这是一种在 “访问高级功能” 与 “保持跨平台独立性” 之间的权衡。
提示工程技术
下面的每个部分都实现了指南中的一种特定提示工程技术。通过同时参考《提示工程》指南和这些实现示例,您将不仅能够深入理解可用的提示工程技术,还能掌握如何在实际的 Java 生产应用中高效落地这些技术。
零样本提示(Zero-Shot Prompting)
零样本提示(Zero-shot prompting)指的是在不提供任何示例的情况下,让 AI 执行任务。这种方法测试模型从零开始理解并执行指令的能力。大型语言模型基于海量文本语料进行训练,使其能够理解诸如 “翻译”、“摘要”、“分类” 等任务的含义,即使没有显式示例也能执行。
零样本方法非常适合模型在训练中可能看过类似任务、且你希望最小化提示长度的简单任务。然而,其表现可能因任务复杂度和指令表达方式的不同而有所差异。
// 第 2.1 节:通用提示 / 零样本(第 15 页)
public void pt_zero_shot(ChatClient chatClient) {
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentiment reviewSentiment = chatClient.prompt("""
Classify movie reviews as POSITIVE, NEUTRAL or NEGATIVE.
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. I wish there were more movies like this masterpiece.
Sentiment:
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.1)
.maxTokens(5)
.build())
.call()
.entity(Sentiment.class);
System.out.println("Output: " + reviewSentiment);
}
本示例展示了如何在不提供任何示例的情况下对电影评论进行情感分类。注意这里使用了较低的温度(0.1)以获得更确定的结果,以及通过 .entity(Sentiment.class) 将模型输出直接映射到 Java enum 的方式。
参考文献:Brown, T. B., et al. (2020). Language Models are Few-Shot Learners. arXiv:2005.14165. https://arxiv.org/abs/2005.14165
一个样本与少样本提示(One-Shot & Few-Shot Prompting)
少样本提示(Few-shot prompting)通过向模型提供一个或多个示例来引导其回答,这在需要特定输出格式的任务中特别有用。通过给出期望的输入-输出示例对,模型能够理解模式并将其应用到新的输入上,而无需进行任何参数更新。
One-shot(一样本) 仅提供一个示例,适用于示例成本较高或任务模式相对简单的情况。 Few-shot(少样本) 提供多个示例(通常 3–5 个),用于更复杂的任务,或用于展示多种正确输出的可能变化,从而帮助模型更好地掌握规则。
// 第 2.2 节:一样本 & 少样本(第 16 页)
public void pt_one_shot_few_shots(ChatClient chatClient) {
String pizzaOrder = chatClient.prompt("""
Parse a customer's pizza order into valid JSON
EXAMPLE 1:
I want a small pizza with cheese, tomato sauce, and pepperoni.
JSON Response:
```
{
"size": "small",
"type": "normal",
"ingredients": ["cheese", "tomato sauce", "pepperoni"]
}
```
EXAMPLE 2:
Can I get a large pizza with tomato sauce, basil and mozzarella.
JSON Response:
```
{
"size": "large",
"type": "normal",
"ingredients": ["tomato sauce", "basil", "mozzarella"]
}
```
Now, I would like a large pizza, with the first half cheese and mozzarella.
And the other tomato sauce, ham and pineapple.
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.1)
.maxTokens(250)
.build())
.call()
.content();
}
少样本提示对于需要特定格式、处理边缘情况,或在没有示例时任务定义会显得模糊的场景特别有效。示例的质量和多样性会显著影响模型表现。
参考文献:Brown, T. B., et al. (2020). Language Models are Few-Shot Learners. arXiv:2005.14165. https://arxiv.org/abs/2005.14165
系统、上下文与角色提示(System, Contextual and Role Prompting)
系统提示(System Prompting)
系统提示用于为语言模型设定整体上下文和目的,定义模型应该做什么的 “大方向”。它建立行为框架、约束条件以及高层目标,与具体的用户查询相分离。
系统提示扮演一种持久的 “使命宣言” 角色,使你能够为整个对话设置全局参数,例如输出格式、语气、伦理边界或角色定义。与专注于具体任务的用户提示不同,系统提示决定了模型应如何解释所有用户输入。
// 第 2.3.1 节实现:系统提示
public void pt_system_prompting_1(ChatClient chatClient) {
String movieReview = chatClient
.prompt()
.system("Classify movie reviews as positive, neutral or negative. Only return the label in uppercase.")
.user("""
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. It's so disturbing I couldn't watch it.
Sentiment:
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(5)
.build())
.call()
.content();
}
当系统提示与 Spring AI 的实体映射功能结合时效果更佳:
// 第 2.3.1 节实现:带 JSON 输出的系统提示
record MovieReviews(Movie[] movie_reviews) {
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
record Movie(Sentiment sentiment, String name) {
}
}
MovieReviews movieReviews = chatClient
.prompt()
.system("""
Classify movie reviews as positive, neutral or negative. Return
valid JSON.
""")
.user("""
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. It's so disturbing I couldn't watch it.
JSON Response:
""")
.call()
.entity(MovieReviews.class);
系统提示在多轮对话中尤其重要,可以确保多个查询之间响应行为保持一致,也适用于设定如 JSON 等全局输出格式约束。
参考文献:OpenAI. (2022). “System Message.” https://platform.openai.com/docs/guides/chat/introduction
角色提示(Role Prompting)
角色提示指示模型采用特定角色或人格,这会影响其内容生成方式。通过赋予模型某个身份、专业或视角,可以影响输出的风格、语气、深度以及表达方式。
角色提示利用了模型模拟不同专业领域和沟通风格的能力。常见角色包括专家(如 “你是一位资深数据科学家”)、专业人士(“扮演旅行指南”)、或风格化角色(“像莎士比亚一样解释”)。
// 第 2.3.2 节实现:角色提示
public void pt_role_prompting_1(ChatClient chatClient) {
String travelSuggestions = chatClient
.prompt()
.system("""
I want you to act as a travel guide. I will write to you
about my location and you will suggest 3 places to visit near
me. In some cases, I will also give you the type of places I
will visit.
""")
.user("""
My suggestion: "I am in Amsterdam and I want to visit only museums."
Travel Suggestions:
""")
.call()
.content();
}
角色提示可以结合风格指令进一步强化效果:
// 第 2.3.2 节实现:带风格指令的角色提示
public void pt_role_prompting_2(ChatClient chatClient) {
String humorousTravelSuggestions = chatClient
.prompt()
.system("""
I want you to act as a travel guide. I will write to you about
my location and you will suggest 3 places to visit near me in
a humorous style.
""")
.user("""
My suggestion: "I am in Amsterdam and I want to visit only museums."
Travel Suggestions:
""")
.call()
.content();
}
此技术特别适用于需要专业领域知识、保持一致语气,以及为用户创建更具吸引力和个性化的交互体验。
参考文献:Shanahan, M., et al. (2023). “Role-Play with Large Language Models.” arXiv:2305.16367. https://arxiv.org/abs/2305.16367
上下文提示(Contextual Prompting)
上下文提示通过传入上下文参数,为模型提供额外背景信息。此技术丰富了模型对当前情境的理解,使响应更相关、更贴合需求,同时避免把背景内容塞进主指令造成冗余。
通过供应上下文信息,你可以帮助模型理解特定领域、受众、约束或相关事实,从而得到更准确、相关且符合期望的响应。
// 第 2.3.3 节实现:上下文提示
public void pt_contextual_prompting(ChatClient chatClient) {
String articleSuggestions = chatClient
.prompt()
.user(u -> u.text("""
Suggest 3 topics to write an article about with a few lines of
description of what this article should contain.
Context: {context}
""")
.param("context", "You are writing for a blog about retro 80's arcade video games."))
.call()
.content();
}
Spring AI 的 param() 方法让上下文注入非常简洁。此技术在以下情况尤其有价值:
- 模型需要特定领域知识
- 响应需要针对特定受众或场景
- 需要确保响应符合指定的约束或要求
参考文献:Liu, P., et al. (2021). “What Makes Good In-Context Examples for GPT-3?” arXiv:2101.06804. https://arxiv.org/abs/2101.06804
后退提示(Step-Back Prompting)
后退提示将复杂请求拆解为更简单的步骤,首先获取背景知识。这种技术鼓励模型先从即时问题中 “后退一步”,考虑与问题相关的更广泛背景、基本原理或通用知识,再去处理具体的提问。
通过将复杂问题分解为更易管理的部分,并先建立基础性的认识,模型可以在应对困难问题时提供更准确的回答。
// 实现第 2.4 节:Step-back prompting
public void pt_step_back_prompting(ChatClient.Builder chatClientBuilder) {
// 为 chat client 设置通用选项
var chatClient = chatClientBuilder
.defaultOptions(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(1024)
.build())
.build();
// 首先获取高层概念
String stepBack = chatClient
.prompt("""
基于流行的第一人称射击动作游戏,列出
5 个虚构的关键场景,这些场景有助于构建
一个具有挑战性且引人入胜的关卡故事线。
""")
.call()
.content();
// 然后在主要任务中使用这些概念
String story = chatClient
.prompt()
.user(u -> u.text("""
为一个新的第一人称射击游戏关卡写一段
一段式的故事线,使其具有挑战性且引人入胜。
上下文:{step-back}
""")
.param("step-back", stepBack))
.call()
.content();
}
后退提示在复杂推理任务、需要专业领域知识的问题上,尤其是在需要更全面、更深思熟虑而非立即回答的场景中,非常有效。
参考文献:Zheng, Z., et al. (2023). “Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models.” arXiv:2310.06117. https://arxiv.org/abs/2310.06117
思路链(Chain of Thought, CoT)
思路链鼓励模型逐步推理,从而提升复杂推理任务的准确性。通过明确要求模型 “展示推理过程” 或 “逐步思考”,可以显著提升模型在多步骤推理任务上的表现。
CoT 的工作方式是让模型在给出最终答案前生成中间推理步骤,就像人类解决复杂问题一样。这让模型的思考过程变得显式,也帮助它得出更准确的结论。
// 实现第 2.5 节:Chain of Thought(CoT)——零样本方法
public void pt_chain_of_thought_zero_shot(ChatClient chatClient) {
String output = chatClient
.prompt("""
当我 3 岁时,我的伴侣是我年龄的 3 倍。现在,
我 20 岁了。我的伴侣现在几岁?
让我们一步一步来思考。
""")
.call()
.content();
}
// 实现第 2.5 节:Chain of Thought(CoT)——少样本方法
public void pt_chain_of_thought_singleshot_fewshots(ChatClient chatClient) {
String output = chatClient
.prompt("""
Q: 当我弟弟 2 岁时,我的年龄是他的两倍。现在
我 40 岁了。我弟弟现在几岁?让我们一步一步思考。
A: 当我弟弟 2 岁时,我是 2 * 2 = 4 岁。
这是一个 2 岁的年龄差,我比他大。现在我 40 岁,
所以我弟弟是 40 - 2 = 38 岁。答案是 38。
Q: 当我 3 岁时,我的伴侣是我年龄的 3 倍。现在,
我 20 岁了。我的伴侣现在几岁?让我们一步一步思考。
A:
""")
.call()
.content();
}
关键短语 “让我们一步一步来思考” 会触发模型展示其推理过程。CoT 尤其适用于数学问题、逻辑推理任务,以及任何需要多步骤推理的问题。通过让中间推理显式化,它能有效减少错误。
参考文献:Wei, J., et al. (2022). “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.” arXiv:2201.11903. https://arxiv.org/abs/2201.11903
自洽(Self-Consistency)
Self-consistency 通过多次运行模型并聚合结果来获得更可靠的答案。这项技术通过为同一问题采样多条不同的推理路径,并通过多数投票选择最一致的答案,从而解决大模型输出的可变性问题。
通过在不同温度或采样设置下生成多条推理路径,并汇总最终答案,self-consistency 能显著提升复杂推理任务的准确性。本质上,它是一种对 LLM 输出进行集成的方法。
// 实现第 2.6 节:Self-consistency
public void pt_self_consistency(ChatClient chatClient) {
String email = """
Hi,
I have seen you use Wordpress for your website. A great open
source content management system. I have used it in the past
too. It comes with lots of great user plugins. And it's pretty
easy to set up.
I did notice a bug in the contact form, which happens when
you select the name field. See the attached screenshot of me
entering text in the name field. Notice the JavaScript alert
box that I inv0k3d.
But for the rest it's a great website. I enjoy reading it. Feel
free to leave the bug in the website, because it gives me more
interesting things to read.
Cheers,
Harry the Hacker.
""";
record EmailClassification(Classification classification, String reasoning) {
enum Classification {
IMPORTANT, NOT_IMPORTANT
}
}
int importantCount = 0;
int notImportantCount = 0;
// 以相同输入运行模型 5 次
for (int i = 0; i < 5; i++) {
EmailClassification output = chatClient
.prompt()
.user(u -> u.text("""
Email: {email}
将上述邮件分类为 IMPORTANT 或 NOT IMPORTANT。
让我们一步一步思考并解释原因。
""")
.param("email", email))
.options(ChatOptions.builder()
.temperature(1.0) // 更高温度以增加多样性
.build())
.call()
.entity(EmailClassification.class);
// 统计结果
if (output.classification() == EmailClassification.Classification.IMPORTANT) {
importantCount++;
} else {
notImportantCount++;
}
}
// 通过多数投票确定最终分类
String finalClassification = importantCount > notImportantCount ?
"IMPORTANT" : "NOT IMPORTANT";
}
Self-consistency 尤其适用于高风险决策、复杂推理任务,以及当你需要比单次响应更高可信度的答案时。其代价是计算成本和延迟增加,因为需要多次 API 调用。
参考文献:Wang, X., et al. (2022). “Self-Consistency Improves Chain of Thought Reasoning in Language Models.” arXiv:2203.11171. https://arxiv.org/abs/2203.11171
思维树(Tree of Thoughts, ToT)
Tree of Thoughts(ToT)是一种高级推理框架,它在 Chain of Thought 的基础上扩展,通过同时探索多条推理路径来解决问题。它将问题解决视为一个搜索过程,模型生成不同的中间步骤,评估其潜力,并探索最有前景的路径。
这种技术对于具有多种可能方法的复杂问题尤其有效,或者当解决方案需要在找到最优路径前探索各种备选方案时特别有用。
棋局求解 ToT 示例:
// 实现第 2.7 节:Tree of Thoughts(ToT)——棋局求解示例
public void pt_tree_of_thoughts_game(ChatClient chatClient) {
// 步骤 1:生成多个初始走法
String initialMoves = chatClient
.prompt("""
你正在下国际象棋。棋盘处于起始位置。
生成 3 个不同的开局走法。对于每个走法:
1. 用代数符号描述该走法
2. 解释该走法背后的战略思路
3. 对走法强度进行 1-10 评分
""")
.options(ChatOptions.builder()
.temperature(0.7)
.build())
.call()
.content();
// 步骤 2:评估并选择最有前景的走法
String bestMove = chatClient
.prompt()
.user(u -> u.text("""
分析这些开局走法并选择最强的一步:
{moves}
逐步解释你的推理,考虑:
1. 棋盘控制
2. 发展潜力
3. 长期战略优势
然后选择唯一的最佳走法。
""").param("moves", initialMoves))
.call()
.content();
// 步骤 3:从最佳走法探索未来棋局
String gameProjection = chatClient
.prompt()
.user(u -> u.text("""
基于所选开局走法:
{best_move}
预测双方接下来的 3 步走法。对于每个可能的分支:
1. 描述走法及应对走法
2. 评估 resulting 棋局
3. 确定最有前景的延续
最后,确定最有利的走法序列。
""").param("best_move", bestMove))
.call()
.content();
}
参考文献:Yao, S., et al. (2023). “Tree of Thoughts: Deliberate Problem Solving with Large Language Models.” arXiv:2305.10601. https://arxiv.org/abs/2305.10601
自动提示工程(Automatic Prompt Engineering)
Automatic Prompt Engineering(APE)使用 AI 来生成和评估不同的提示。这种元技术利用语言模型自身创建、优化和评估各种提示变体,从而为特定任务找到最优的提示表达方式。
通过系统地生成和评估提示变体,APE 能找到比人工设计更有效的提示,尤其适用于复杂任务。它是一种利用 AI 来提升自身性能的方法。
// 实现第 2.8 节:Automatic Prompt Engineering
public void pt_automatic_prompt_engineering(ChatClient chatClient) {
// 生成同一请求的不同变体
String orderVariants = chatClient
.prompt("""
我们有一个乐队周边 T 恤网店,为训练聊天机器人,
我们需要多种下单方式,例如:“一件 Metallica S 码 T 恤”。
生成 10 个变体,语义相同,但保持原意。
""")
.options(ChatOptions.builder()
.temperature(1.0) // 高温度以增加创造性
.build())
.call()
.content();
// 评估并选择最佳变体
String output = chatClient
.prompt()
.user(u -> u.text("""
请对以下变体进行 BLEU(双语评估)评分:
----
{variants}
----
选择评分最高的指令候选。
""").param("variants", orderVariants))
.call()
.content();
}
APE 对于优化生产系统的提示、处理人工提示工程已达到极限的复杂任务,以及系统性地提升大规模提示质量尤其有价值。
参考文献:Zhou, Y., et al. (2022). “Large Language Models Are Human-Level Prompt Engineers.” arXiv:2211.01910. https://arxiv.org/abs/2211.01910
代码提示(Code Prompting)
Code prompting 指的是针对代码相关任务的专门提示技术。这些技术利用大语言模型理解和生成编程语言的能力,使其能够编写新代码、解释已有代码、调试问题以及在不同语言之间进行翻译。
有效的 code prompting 通常包括明确的规格说明、适当的上下文(如库、框架、编码风格指南),有时还会提供类似代码示例。温度设置通常较低(0.1-0.3),以生成更确定性的输出。
// 实现第 2.9.1 节:编写代码的提示
public void pt_code_prompting_writing_code(ChatClient chatClient) {
String bashScript = chatClient
.prompt("""
编写一个 Bash 代码片段,提示输入文件夹名称。
然后对该文件夹中的所有文件进行重命名,在文件名前加上 "draft" 前缀。
""")
.options(ChatOptions.builder()
.temperature(0.1) // 低温度以生成确定性代码
.build())
.call()
.content();
}
// 实现第 2.9.2 节:解释代码的提示
public void pt_code_prompting_explaining_code(ChatClient chatClient) {
String code = """
#!/bin/bash
echo "Enter the folder name: "
read folder_name
if [ ! -d "$folder_name" ]; then
echo "Folder does not exist."
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "Files renamed successfully."
""";
String explanation = chatClient
.prompt()
.user(u -> u.text("""
请解释下面的 Bash 代码:
```
{code}
```
""").param("code", code))
.call()
.content();
}
// 实现第 2.9.3 节:代码翻译的提示
public void pt_code_prompting_translating_code(ChatClient chatClient) {
String bashCode = """
#!/bin/bash
echo "Enter the folder name: "
read folder_name
if [ ! -d "$folder_name" ]; then
echo "Folder does not exist."
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "Files renamed successfully."
""";
String pythonCode = chatClient
.prompt()
.user(u -> u.text("""
将下面的 Bash 代码翻译为 Python 代码片段:
{code}
""").param("code", bashCode))
.call()
.content();
}
Code prompting 对于自动化代码文档生成、原型开发、学习编程概念以及在编程语言之间进行翻译尤其有价值。结合少样本提示或 Chain of Thought 等技术,可以进一步提升其效果。
参考文献:Chen, M., et al. (2021). “Evaluating Large Language Models Trained on Code.” arXiv:2107.03374. https://arxiv.org/abs/2107.03374
结论
Spring AI 提供了一个优雅的 Java API,用于实现所有主要的提示工程技术。通过将这些技术与 Spring 强大的实体映射和流式 API 相结合,开发者可以构建功能复杂、可维护性高的 AI 驱动应用程序。
最有效的方法通常是结合多种技术——例如,将系统提示与少样本示例结合,或将 Chain-of-Thought 与角色提示结合。Spring AI 灵活的 API 使这些组合的实现变得非常简单。
对于生产环境的应用,请注意:
- 使用不同参数(temperature、top-k、top-p)测试提示
- 对关键决策考虑使用 self-consistency
- 利用 Spring AI 的实体映射实现类型安全的响应
- 使用上下文提示提供特定于应用程序的知识
通过这些技术以及 Spring AI 强大的抽象能力,你可以创建稳健的 AI 驱动应用程序,提供一致且高质量的结果。
参考文献
- Brown, T. B., 等. (2020). “Language Models are Few-Shot Learners.” arXiv:2005.14165.
- Wei, J., 等. (2022). “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.” arXiv:2201.11903.
- Wang, X., 等. (2022). “Self-Consistency Improves Chain of Thought Reasoning in Language Models.” arXiv:2203.11171.
- Yao, S., 等. (2023). “Tree of Thoughts: Deliberate Problem Solving with Large Language Models.” arXiv:2305.10601.
- Zhou, Y., 等. (2022). “Large Language Models Are Human-Level Prompt Engineers.” arXiv:2211.01910.
- Zheng, Z., 等. (2023). “Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models.” arXiv:2310.06117.
- Liu, P., 等. (2021). “What Makes Good In-Context Examples for GPT-3?” arXiv:2101.06804.
- Shanahan, M., 等. (2023). “Role-Play with Large Language Models.” arXiv:2305.16367.
- Chen, M., 等. (2021). “Evaluating Large Language Models Trained on Code.” arXiv:2107.03374.
- Spring AI 文档
- ChatClient API 参考
- Google 提示工程指南