前置配置:选好模型、调好参数
在正式进入提示词工程技术之前,有两件事必须先搞定:选一个 LLM 提供商,以及理解影响模型输出的关键参数。
LLM 提供商选择
Spring AI 支持多个 LLM 提供商(OpenAI、Anthropic、Google GenAI、AWS Bedrock、Ollama 等),切换提供商不需要改代码,只需更新配置。添加对应的 starter 依赖即可:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>指定模型名称:
.options(ChatOptions.builder()
.model("claude-sonnet-4-6") // 使用 Anthropic Claude 模型
.build())生成参数配置

温度(Temperature)
温度控制模型响应的随机性。不同提示工程技术适合不同的温度设置:
.options(ChatOptions.builder()
.temperature(0.1) // 高确定性输出
.build())输出长度(MaxTokens)
maxTokens 限制模型生成的 token 数量(单词片段):
.options(ChatOptions.builder()
.maxTokens(250) // 中等长度响应
.build())采样控制(Top-K 和 Top-P)
精细控制生成过程中的 token 选择:
• Top-K:限制 token 选择为 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())结构化响应格式
除了 .content() 获取纯文本,Spring AI 还可以通过 .entity() 将 LLM 响应直接映射为 Java 对象:
enumSentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentimentresult= chatClient.prompt("...")
.call()
.entity(Sentiment.class);模型专有选项
Spring AI 提供了通用的 ChatOptions,同时也暴露了各提供商的专有配置类:
// OpenAI 专有选项
OpenAiChatOptionsopenAiOptions= OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.2)
.frequencyPenalty(0.5) // OpenAI 专有参数
.presencePenalty(0.3) // OpenAI 专有参数
.responseFormat(newResponseFormat("json_object")) // OpenAI 专有 JSON 模式
.seed(42) // OpenAI 专有确定性生成
.build();
// Anthropic 专有选项
AnthropicChatOptionsanthropicOptions= AnthropicChatOptions.builder()
.model("claude-sonnet-4-6")
.temperature(0.2)
.topK(40) // Anthropic 专有参数
.thinkingEnabled(1000) // Anthropic 专有思考配置
.build();使用模型专有选项时,代码会绑定到特定提供商,降低了可移植性。这是访问高级功能与保持提供商独立性之间的权衡。

零样本提示(Zero-Shot Prompting)
零样本提示是最基础的方式:直接让模型执行任务,不提供任何示例。 大语言模型在海量文本语料上训练,能够理解"翻译"、"摘要"、"分类"等任务指令,无需显式演示。

适用场景:模型在训练期间大概率见过类似样本的简单任务,以及需要最小化 prompt 长度的场景。
publicvoidpt_zero_shot(ChatClient chatClient) {
enumSentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
SentimentreviewSentiment= 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-sonnet-4-6")
.temperature(0.1)
.maxTokens(5)
.build())
.call()
.entity(Sentiment.class);
System.out.println("Output: " + reviewSentiment);
}关键点:低温度(0.1)保证分类结果的确定性;.entity(Sentiment.class) 直接将输出映射为 Java 枚举——不需要手动解析字符串。
单样本与少样本提示(One-Shot & Few-Shot Prompting)
当零样本不够用时,给模型看例子。
One-Shot:提供 1 个示例,适合模式简单的场景。
Few-Shot:提供 3-5 个示例,帮助模型理解更复杂任务中的模式和输出变化。

Few-Shot 的核心机制:模型从示例中学习 input-output 对的模式,然后将其应用于新输入,无需更新参数。
publicvoidpt_one_shot_few_shots(ChatClient chatClient) {
StringpizzaOrder= 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-sonnet-4-6")
.temperature(0.1)
.maxTokens(250)
.build())
.call()
.content();
}这个例子通过两个示例教模型如何将披萨订单解析为特定 JSON 格式。示例的质量和多样性对输出效果影响显著。
系统提示、角色提示与上下文提示
这三种技术从不同维度控制 LLM 的行为框架。

系统提示(System Prompting)
系统提示为模型设定全局的行为框架、约束和高级目标,与具体用户查询分离。它在整个对话过程中充当持久的"使命宣言",规定所有用户提示应如何被解释。
publicvoidpt_system_prompting_1(ChatClient chatClient) {
StringmovieReview= 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-sonnet-4-6")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(5)
.build())
.call()
.content();
}系统提示与 .entity() 结合使用时尤其强大——直接获得结构化的 Java 对象:
recordMovieReviews(Movie[] movie_reviews) {
enumSentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
recordMovie(Sentiment sentiment, String name) {
}
}
MovieReviewsmovieReviews= 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);角色提示(Role Prompting)
角色提示指示模型采用特定身份或角色,从而影响输出的风格、语气、深度和框架。常见角色包括专家("你是一位经验丰富的数据科学家")、专业人士("充当旅行指南")或风格角色("像莎士比亚一样解释")。
// 基础角色提示
publicvoidpt_role_prompting_1(ChatClient chatClient) {
StringtravelSuggestions= 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();
}
// 增强版:角色 + 风格指令
publicvoidpt_role_prompting_2(ChatClient chatClient) {
StringhumorousTravelSuggestions= 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();
}第二个例子在角色基础上增加了"幽默风格"的约束——同一个角色,不同的输出风格。
上下文提示(Contextual Prompting)
上下文提示通过 param() 方法注入额外的背景信息,让模型在不干扰主指令的情况下理解特定领域、受众或约束。
publicvoidpt_contextual_prompting(ChatClient chatClient) {
StringarticleSuggestions= 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() 方法让上下文注入非常干净——{context} 占位符会被自动替换。当模型需要特定领域知识、需要针对特定受众调整响应时,这个技术特别有价值。
后退提示(Step-Back Prompting)
后退提示的核心思路:先退一步获取背景知识,再解决具体问题。 它鼓励模型先考虑更广泛的上下文和基本原则,而不是直接跳进答案。

实现方式是两次调用 LLM:第一次生成高层概念,第二次将这些概念作为上下文注入具体任务。
publicvoidpt_step_back_prompting(ChatClient.Builder chatClientBuilder) {
varchatClient= chatClientBuilder
.defaultOptions(ChatOptions.builder()
.model("claude-sonnet-4-6")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(1024)
.build())
.build();
// 第一步:获取高层概念
StringstepBack= chatClient
.prompt("""
Based on popular first-person shooter action games, what are
5 fictional key settings that contribute to a challenging and
engaging level storyline in a first-person shooter video game?
""")
.call()
.content();
// 第二步:将概念作为上下文,完成具体任务
Stringstory= chatClient
.prompt()
.user(u -> u.text("""
Write a one paragraph storyline for a new level of a first-
person shooter video game that is challenging and engaging.
Context: {step-back}
""")
.param("step-back", stepBack))
.call()
.content();
}对于复杂推理任务、需要专业领域知识的问题,后退提示能产生比直接回答更全面、更深思熟虑的响应。
思维链(Chain of Thought, CoT)
思维链通过要求模型逐步推理来提高复杂推理任务的准确性。关键触发语是:"Let's think step by step."

CoT 有两种实现方式:零样本和少样本。
零样本 CoT
只需在问题末尾加上 "Let's think step by step":
publicvoidpt_chain_of_thought_zero_shot(ChatClient chatClient) {
Stringoutput= chatClient
.prompt("""
When I was 3 years old, my partner was 3 times my age. Now,
I am 20 years old. How old is my partner?
Let's think step by step.
""")
.call()
.content();
}少样本 CoT
先给出一个完整的推理示例,让模型按照相同的推理模式回答新问题:
publicvoidpt_chain_of_thought_singleshot_fewshots(ChatClient chatClient) {
Stringoutput= chatClient
.prompt("""
Q: When my brother was 2 years old, I was double his age. Now
I am 40 years old. How old is my brother? Let's think step
by step.
A: When my brother was 2 years, I was 2 * 2 = 4 years old.
That's an age difference of 2 years and I am older. Now I am 40
years old, so my brother is 40 - 2 = 38 years old. The answer
is 38.
Q: When I was 3 years old, my partner was 3 times my age. Now,
I am 20 years old. How old is my partner? Let's think step
by step.
A:
""")
.call()
.content();
}CoT 对数学问题、逻辑推理任务和任何需要多步推理的问题特别有价值。它通过显式化中间推理过程来减少错误。
自洽性(Self-Consistency)
自洽性本质上是 LLM 输出的集成学习方法:对同一个问题运行多次,然后通过多数投票选择最一致的答案。

通过设置较高的温度(1.0)生成多样化的推理路径,然后聚合结果。
publicvoidpt_self_consistency(ChatClient chatClient) {
Stringemail="""
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.
""";
recordEmailClassification(Classification classification, String reasoning) {
enumClassification {
IMPORTANT, NOT_IMPORTANT
}
}
intimportantCount=0;
intnotImportantCount=0;
// 对同一输入运行 5 次
for (inti=0; i < 5; i++) {
EmailClassificationoutput= chatClient
.prompt()
.user(u -> u.text("""
Email: {email}
Classify the above email as IMPORTANT or NOT IMPORTANT. Let's
think step by step and explain why.
""")
.param("email", email))
.options(ChatOptions.builder()
.temperature(1.0) // 高温度产生更多变化
.build())
.call()
.entity(EmailClassification.class);
if (output.classification() == EmailClassification.Classification.IMPORTANT) {
importantCount++;
} else {
notImportantCount++;
}
}
// 多数投票决定最终分类
StringfinalClassification= importantCount > notImportantCount ?
"IMPORTANT" : "NOT IMPORTANT";
}这封邮件表面上是友好的反馈,实际上暗含了安全漏洞(XSS 攻击)的线索。单次调用可能误判为"不重要",但自洽性通过多次采样和投票显著提高了分类的可靠性。
权衡:多次 API 调用带来的计算成本和延迟增加。对高风险决策和复杂推理任务来说,这个代价是值得的。
思维树(Tree of Thoughts, ToT)
思维树是思维链的高级扩展——同时探索多条推理路径,评估每条路径的前景,然后选择最有希望的路径继续深入。它把问题解决视为一个搜索过程。

原文的 Prompt Engineering Guide 没有提供 ToT 的实现示例(可能因为复杂度),以下是一个演示核心概念的国际象棋解题示例:
publicvoidpt_tree_of_thoughts_game(ChatClient chatClient) {
// 第一步:生成多个初始走法
StringinitialMoves= chatClient
.prompt("""
You are playing a game of chess. The board is in the starting position.
Generate 3 different possible opening moves. For each move:
1. Describe the move in algebraic notation
2. Explain the strategic thinking behind this move
3. Rate the move's strength from 1-10
""")
.options(ChatOptions.builder()
.temperature(0.7)
.build())
.call()
.content();
// 第二步:评估并选择最有前景的走法
StringbestMove= chatClient
.prompt()
.user(u -> u.text("""
Analyze these opening moves and select the strongest one:
{moves}
Explain your reasoning step by step, considering:
1. Position control
2. Development potential
3. Long-term strategic advantage
Then select the single best move.
""").param("moves", initialMoves))
.call()
.content();
// 第三步:从最优走法出发,推演未来局面
StringgameProjection= chatClient
.prompt()
.user(u -> u.text("""
Based on this selected opening move:
{best_move}
Project the next 3 moves for both players. For each potential branch:
1. Describe the move and counter-move
2. Evaluate the resulting position
3. Identify the most promising continuation
Finally, determine the most advantageous sequence of moves.
""").param("best_move", bestMove))
.call()
.content();
}三步流程:生成候选 → 评估筛选 → 深入推演。对于具有多种可能方法的复杂问题,或者需要在找到最优路径之前探索各种替代方案的问题,ToT 特别强大。
自动提示工程(Automatic Prompt Engineering)
自动提示工程(APE)是一种元技术:用 AI 来生成、优化和评估提示变体,找到特定任务的最优提示公式。

publicvoidpt_automatic_prompt_engineering(ChatClient chatClient) {
// 第一步:生成语义等价的提示变体
StringorderVariants= chatClient
.prompt("""
We have a band merchandise t-shirt webshop, and to train a
chatbot we need various ways to order: "One Metallica t-shirt
size S". Generate 10 variants, with the same semantics but keep
the same meaning.
""")
.options(ChatOptions.builder()
.temperature(1.0) // 高温度激发创造力
.build())
.call()
.content();
// 第二步:评估并选择最优变体
Stringoutput= chatClient
.prompt()
.user(u -> u.text("""
Please perform BLEU (Bilingual Evaluation Understudy) evaluation on the following variants:
----
{variants}
----
Select the instruction candidate with the highest evaluation score.
""").param("variants", orderVariants))
.call()
.content();
}APE 对以下场景特别有价值:
• 优化生产系统的提示 • 解决手动提示工程已达极限的复杂任务 • 大规模系统性提升提示质量
代码提示(Code Prompting)
代码提示是针对编程任务的专用技术,覆盖三个核心场景:写代码、解释代码、翻译代码。

写代码
明确规范 + 低温度 = 确定性输出:
publicvoidpt_code_prompting_writing_code(ChatClient chatClient) {
StringbashScript= chatClient
.prompt("""
Write a code snippet in Bash, which asks for a folder name.
Then it takes the contents of the folder and renames all the
files inside by prepending the name draft to the file name.
""")
.options(ChatOptions.builder()
.temperature(0.1) // 低温度保证代码确定性
.build())
.call()
.content();
}解释代码
通过 param() 注入代码片段,让模型解释其逻辑:
publicvoidpt_code_prompting_explaining_code(ChatClient chatClient) {
Stringcode="""
#!/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."
""";
Stringexplanation= chatClient
.prompt()
.user(u -> u.text("""
Explain to me the below Bash code:
```
{code}
```
""").param("code", code))
.call()
.content();
}翻译代码
在编程语言之间转换:
publicvoidpt_code_prompting_translating_code(ChatClient chatClient) {
StringbashCode="""
#!/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."
""";
StringpythonCode= chatClient
.prompt()
.user(u -> u.text("""
Translate the below Bash code to a Python snippet:
{code}
""").param("code", bashCode))
.call()
.content();
}将代码提示与少样本提示或思维链结合使用,可以进一步提升效果。
技术组合:生产环境的最佳实践
最有效的做法通常不是单独使用某一种技术,而是组合多种技术。Spring AI 的 Fluent API 让这些组合实现起来非常自然。

生产环境关键实践:
.entity() 做类型安全的响应映射,避免手动解析 | |
param() 注入应用特定的知识,提升响应的相关性 |
参考文献
1. Brown, T. B., et al. (2020). "Language Models are Few-Shot Learners." arXiv:2005.14165. 2. Wei, J., et al. (2022). "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." arXiv:2201.11903. 3. Wang, X., et al. (2022). "Self-Consistency Improves Chain of Thought Reasoning in Language Models." arXiv:2203.11171. 4. Yao, S., et al. (2023). "Tree of Thoughts: Deliberate Problem Solving with Large Language Models." arXiv:2305.10601. 5. Zhou, Y., et al. (2022). "Large Language Models Are Human-Level Prompt Engineers." arXiv:2211.01910. 6. Zheng, Z., et al. (2023). "Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models." arXiv:2310.06117. 7. Liu, P., et al. (2021). "What Makes Good In-Context Examples for GPT-3?" arXiv:2101.06804. 8. Shanahan, M., et al. (2023). "Role-Play with Large Language Models." arXiv:2305.16367. 9. Chen, M., et al. (2021). "Evaluating Large Language Models Trained on Code." arXiv:2107.03374. 10. Spring AI Documentation: https://docs.spring.io/spring-ai/reference/ 11. Google's Prompt Engineering Guide
夜雨聆风