Spring AI Prompt 实战:System Prompt、User Prompt 与 PromptTemplate
Spring AI 从 0 到 1 系列教程 · 第 03 章
上一章我们完成了 Spring Boot + Spring AI + DeepSeek 的第一次整合,并成功实现了一个 AI 聊天接口。
我们已经可以这样调用 AI:
text
用户问题
↓
ChatClient
↓
DeepSeek
↓
AI 回答
但是很快就会发现一个问题:
如果只是把用户的问题原封不动地交给大模型,我们很难控制 AI 应该"怎么回答"。
例如用户输入:
text
什么是 Redis?
我们可能希望 AI:
- 使用中文回答
- 面向 Java 初学者
- 先解释概念
- 再解释原理
- 最后给出 Java 示例
- 不要回答与 Redis 无关的问题
这时候,仅仅使用:
java
.user(message)
显然不够。
我们需要告诉 AI:
你是谁、应该做什么、应该怎么做。
这就是 Prompt 的作用。
本章我们将系统学习 Spring AI 中的 Prompt,并重点掌握:
text
Prompt
↓
System Prompt
↓
User Prompt
↓
Message
↓
PromptTemplate
↓
动态参数
↓
企业级 Prompt 设计
最终,我们会把上一章简单的聊天接口升级成一个真正可控的 AI 聊天接口。
一、什么是 Prompt?
Prompt 可以简单理解成:
我们发送给大模型的指令和上下文。
比如:
text
请介绍一下 Spring AI。
这就是一个最简单的 Prompt。
再复杂一点:
text
你是一名 Java 高级工程师。
请介绍一下 Spring AI。
要求:
1. 使用中文回答
2. 面向 Java 初学者
3. 先介绍概念
4. 再介绍核心原理
5. 最后给出一个简单代码示例
这同样是 Prompt。
所以:
text
Prompt ≈ 给 AI 的任务说明 + 上下文 + 约束
二、为什么需要 Prompt?
上一章我们实现的是:
java
return chatClient
.prompt()
.user(message)
.call()
.content();
如果用户输入:
text
介绍一下 Spring AI
Spring AI 基本就是把用户的问题交给模型。
但是在实际业务中,我们往往需要对 AI 进行控制。
例如:
text
你是一名 Java 技术专家。
这是角色定义。
又比如:
text
必须使用中文回答。
这是输出约束。
再比如:
text
回答必须包含:
1. 概念
2. 原理
3. 示例
4. 注意事项
这是输出格式要求。
最终:
text
角色
+
任务
+
上下文
+
约束
=
Prompt
因此:
Prompt 是 AI 应用开发中非常基础,也非常重要的一层。
三、Prompt 不只是"给 AI 的一句话"
很多刚接触大模型的开发者会把 Prompt 理解成:
text
prompt = "你好"
其实在实际 AI 应用中,一个完整的 Prompt 通常包含多个部分。
可以简单理解为:
text
Prompt
│
├── System Message
│
├── User Message
│
├── Assistant Message
│
└── Context
其中最重要的是:
text
System
User
我们先重点理解这两个。
四、System Prompt 是什么?
System Prompt 可以理解为:
告诉 AI "你是谁、你的职责是什么、你应该遵守什么规则"。
例如:
text
你是一名 Java 高级工程师。
你需要帮助用户解决 Java 后端开发问题。
请使用中文回答。
如果问题与 Java 无关,请告诉用户你主要负责 Java 技术问题。
这就是一个典型的 System Prompt。
它相当于给 AI 设置一个:
角色 + 行为规范。
五、User Prompt 是什么?
User Prompt 就是:
用户当前真正想让 AI 完成的任务。
例如:
text
什么是 Spring AI?
或者:
text
帮我解释一下 Spring AI 中 ChatClient 的作用。
所以可以简单理解:
text
System Prompt
↓
规定"你是谁"
User Prompt
↓
告诉"你现在要做什么"
例如:
text
System:
你是一名 Java 高级工程师。
请使用中文回答技术问题。
User:
什么是 Spring AI?
模型综合这两部分内容之后生成最终答案。
六、System Prompt 和 User Prompt 的区别
可以通过一个非常简单的例子理解。
假设我们创建一个:
Java 技术助手
System Prompt:
text
你是一名拥有多年经验的 Java 高级工程师。
你的职责是帮助用户解决:
- Java
- Spring Boot
- Spring AI
- MySQL
- Redis
相关技术问题。
回答要求:
1. 使用中文
2. 先解释概念
3. 再解释原理
4. 最后给出代码示例
用户输入:
text
什么是 Redis?
那么:
text
System Prompt
+
User Prompt
↓
LLM
↓
最终回答
这时候 AI 的回答风格就会受到 System Prompt 的影响。
七、在 Spring AI 中如何设置 System Prompt?
回到上一章的代码。
我们之前是:
java
return chatClient
.prompt()
.user(message)
.call()
.content();
现在可以增加:
java
return chatClient
.prompt()
.system("""
你是一名 Java 高级工程师。
请使用中文回答用户的问题。
回答需要尽可能准确、清晰。
""")
.user(message)
.call()
.content();
完整代码:
java
public String chat(String message) {
return chatClient
.prompt()
.system("""
你是一名 Java 高级工程师。
请使用中文回答用户的问题。
回答需要准确、清晰、易懂。
""")
.user(message)
.call()
.content();
}
现在请求:
text
介绍一下 Spring AI。
AI 就不再只是简单回答,而会根据我们设置的角色和规则生成答案。
八、为什么 System Prompt 要和 User Prompt 分开?
这是一个非常重要的设计思想。
我们可以把:
text
System Prompt
理解成:
应用开发者定义的规则。
而:
text
User Prompt
理解成:
用户动态输入的问题。
例如:
text
System:
你是一名 Java 技术专家。
必须使用中文回答。
User:
什么是 Spring AI?
下一次:
text
System:
你是一名 Java 技术专家。
必须使用中文回答。
User:
什么是 RAG?
System 不变,User 不断变化。
所以:
text
System
↓
相对稳定
User
↓
动态变化
这也是实际项目中非常常见的 Prompt 设计方式。
九、一个更加真实的 AI 技术助手
现在我们来设计一个真正有一点业务意义的 Prompt。
System Prompt:
text
你是一名资深 Java 后端工程师,同时熟悉 Spring AI。
你的主要职责是帮助 Java 开发者解决后端和 AI 应用开发问题。
回答要求:
1. 使用中文。
2. 优先给出准确的技术结论。
3. 对复杂概念进行分层解释。
4. 涉及代码时,优先使用 Java。
5. 如果涉及 Spring Boot,请给出完整的实现思路。
6. 如果问题存在多种解决方案,请说明各自优缺点。
7. 不要编造不存在的 API。
用户:
text
什么是 RAG?
这样生成出来的答案通常会比单纯:
text
RAG 是什么?
更加符合我们的业务需求。
十、Prompt 也可以直接写在配置中吗?
当然可以。
如果 System Prompt 很长,而且项目中需要统一维护,不建议全部写在 Java 方法里面。
例如:
text
application.yml
可以保存一些配置。
但对于比较复杂的 Prompt,更推荐建立独立的 Prompt 管理方式。
例如:
text
src/main/resources
├── prompts
│ ├── system.txt
│ ├── chat.txt
│ └── summary.txt
这样做的好处是:
text
Java 代码
↓
业务逻辑
Prompt 文件
↓
AI 行为规则
两者可以进行分离。
十一、什么是 PromptTemplate?
如果每次 Prompt 都写死:
text
请介绍 Java。
显然不够灵活。
实际项目中,我们往往需要动态参数。
例如:
text
请介绍一下 {technology}。
当:
text
technology = Java
最终变成:
text
请介绍一下 Java。
如果:
text
technology = Redis
最终变成:
text
请介绍一下 Redis。
这就是:
Prompt Template(Prompt 模板)
核心思想:
text
固定 Prompt
+
动态参数
↓
最终 Prompt
十二、为什么需要 PromptTemplate?
假设我们有一个 AI 技术解释器。
每次用户传入一个技术名称:
text
Java
Redis
MySQL
Spring Boot
Spring AI
我们都希望 AI 按照固定格式回答。
Prompt 可以设计成:
text
你是一名 Java 技术专家。
请介绍技术:
{technology}
回答必须包含:
1. 技术是什么
2. 解决什么问题
3. 核心原理
4. 使用场景
5. Java 示例
6. 常见问题
这里:
text
{technology}
就是动态变量。
十三、使用 PromptTemplate 构建动态 Prompt
Spring AI 提供了 Prompt 相关抽象,可以使用模板动态生成 Prompt。
例如:
java
PromptTemplate template = new PromptTemplate("""
你是一名 Java 技术专家。
请介绍一下 {technology}。
回答要求:
1. 解释基本概念
2. 介绍核心原理
3. 给出使用场景
4. 给出 Java 示例
""");
然后传入参数:
java
Prompt prompt = template.create(
Map.of("technology", "Redis")
);
最终得到的 Prompt 相当于:
text
你是一名 Java 技术专家。
请介绍一下 Redis。
回答要求:
1. 解释基本概念
2. 介绍核心原理
3. 给出使用场景
4. 给出 Java 示例
这样就实现了:
text
模板
+
参数
↓
Prompt
说明:不同 Spring AI 版本中 PromptTemplate 的具体 API 可能会有所调整,实际编码时应以当前项目所使用版本的 API 为准。
十四、PromptTemplate 和普通 String 有什么区别?
有人可能会问:
"我直接 String.format() 不就行了吗?"
例如:
java
String prompt = """
请介绍一下 %s。
""".formatted("Redis");
当然可以。
但 AI 应用中的 Prompt 往往会越来越复杂。
例如:
text
角色
+
任务
+
用户问题
+
上下文
+
格式要求
+
知识库内容
如果全部使用字符串拼接:
java
String prompt =
"你是一名专家..."
+ "用户问题..."
+ "知识库..."
+ "...";
代码很容易变得难以维护。
PromptTemplate 的意义就在于:
把 Prompt 的固定结构和动态数据进行分离。
十五、一个更加真实的 PromptTemplate
假设我们开发:
AI Java 面试助手
Prompt:
text
你是一名资深 Java 面试官。
请针对以下技术生成一道面试题:
技术名称:
{technology}
候选人级别:
{level}
要求:
1. 给出面试题
2. 给出标准答案
3. 给出面试官追问
4. 给出评分重点
传入:
text
technology = Redis
level = 高级工程师
最终:
text
你是一名资深 Java 面试官。
请针对以下技术生成一道面试题:
技术名称:
Redis
候选人级别:
高级工程师
要求:
1. 给出面试题
2. 给出标准答案
3. 给出面试官追问
4. 给出评分重点
这就已经非常接近真实 AI 产品了。
十六、把 PromptTemplate 接入 Service
我们可以设计一个:
java
public String interviewQuestion(
String technology,
String level
)
然后构造 Prompt:
java
PromptTemplate template = new PromptTemplate("""
你是一名资深 Java 面试官。
请针对以下技术生成一道面试题。
技术名称:
{technology}
候选人级别:
{level}
要求:
1. 给出面试题
2. 给出标准答案
3. 给出面试官追问
4. 给出评分重点
""");
传入参数:
java
Prompt prompt = template.create(
Map.of(
"technology", technology,
"level", level
)
);
然后交给模型处理。
如果使用 ChatClient,在不同 Spring AI 版本中也可以直接利用其 Prompt 构建能力完成类似的模板化设计。
核心思想始终不变:
text
Prompt Template
↓
动态参数
↓
最终 Prompt
↓
ChatClient
↓
LLM
十七、System Prompt + User Prompt + Template
现在我们把三个概念组合起来。
假设我们开发:
AI Java 面试官
整体结构:
text
ChatClient
│
┌─────────┴─────────┐
↓ ↓
System Prompt User Prompt
│ │
↓ ↓
角色和规则 用户问题
│ │
└─────────┬─────────┘
↓
最终 Prompt
↓
LLM
↓
AI Answer
例如:
System Prompt
text
你是一名资深 Java 面试官。
请针对候选人的技术问题进行专业回答。
User Prompt
text
请帮我准备 Redis 高级面试题。
Template
text
技术:
{technology}
级别:
{level}
最终组合起来:
text
System
+
Template
+
User
↓
LLM
这就是实际项目中比较常见的 Prompt 架构。
十八、不要把所有内容都塞进 System Prompt
这是一个非常容易犯的错误。
例如:
text
System Prompt:
你是 Java 专家。
用户问题:
什么是 Redis?
用户又问:
什么是 MySQL?
用户还问:
什么是 Spring AI?
......
这样会让 System Prompt 越来越大。
正确思路是:
text
System
↓
稳定规则
User
↓
动态问题
Context
↓
动态业务数据
也就是:
text
稳定内容 → System
动态问题 → User
动态业务数据 → Context
这是一个非常重要的 Prompt 工程思想。
十九、Prompt 不是越长越好
很多初学者认为:
Prompt 写得越详细,AI 就越聪明。
不一定。
例如:
text
你是一名非常优秀、非常专业、经验极其丰富......
写了一大堆形容词。
真正有价值的可能只有:
text
你是一名 Java 高级工程师。
Prompt 的重点不是:
字数多。
而是:
目标清晰、约束明确、上下文准确。
二十、一个优秀的 System Prompt 应该包含什么?
可以采用下面这个结构:
text
角色
↓
任务
↓
上下文
↓
约束
↓
输出格式
↓
异常处理
例如:
text
# 角色
你是一名 Java 高级工程师。
# 任务
帮助用户解决 Java 后端开发问题。
# 上下文
用户主要使用:
Spring Boot、MySQL、Redis。
# 约束
1. 使用中文。
2. 不要编造不存在的 API。
3. 不确定的信息明确说明。
4. 优先给出可运行代码。
# 输出格式
1. 结论
2. 原理
3. 示例
4. 注意事项
这种结构比简单写:
text
你是一个很厉害的 Java 专家。
更加可控。
二十一、Prompt 中加入"输出格式"
这是实际 AI 开发非常重要的一点。
例如我们希望 AI 每次回答都按照:
text
【结论】
【原理】
【代码】
【注意事项】
那么可以在 Prompt 中明确告诉模型:
text
回答必须严格按照以下格式:
【结论】
...
【原理】
...
【代码】
...
【注意事项】
...
这样 AI 的输出会更加稳定。
但需要注意:
如果业务系统真正依赖结构化数据,不应该只依赖 Prompt 要求模型输出 JSON。
更可靠的方式是进一步学习:
Structured Output
这也是我们下一阶段会重点介绍的内容。
二十二、Prompt 设计中的一个重要原则:少让 AI 猜
例如:
text
帮我写一个 Redis 的介绍。
AI 需要自己猜:
text
介绍给谁?
介绍多深?
要不要代码?
什么语言?
什么格式?
如果改成:
text
你是一名 Java 高级工程师。
请为一名有 2 年 Java 开发经验的程序员介绍 Redis。
要求:
1. 使用中文。
2. 先解释 Redis 是什么。
3. 再解释核心数据结构。
4. 介绍常见应用场景。
5. 给出 Java 使用示例。
6. 最后总结常见面试问题。
任务就清晰很多。
所以:
Prompt 的核心价值之一,就是减少模型对任务的猜测。
二十三、Prompt 也可以加入业务上下文
例如开发公司内部 AI 助手。
用户问:
text
退款需要多长时间?
单独把这句话交给 LLM:
text
用户
↓
退款需要多长时间?
↓
LLM
模型并不知道公司的退款规则。
我们可以加入业务上下文:
text
公司退款规则:
普通订单:
支付后 7 天内可以申请退款。
特殊订单:
根据商品具体规则处理。
退款审核:
一般在 1~3 个工作日完成。
最终:
text
System
+
User
+
Business Context
↓
LLM
这已经开始接近 RAG 的思想了。
后面我们会看到:
RAG 本质上就是把外部知识动态检索出来,再作为上下文交给模型。
二十四、Prompt 和 RAG 是什么关系?
可以先建立一个非常重要的认知:
text
Prompt
↓
告诉 AI 怎么回答
RAG
↓
给 AI 提供它不知道的知识
例如:
text
Prompt:
你是一名公司内部技术助手。
请根据提供的公司知识回答问题。
如果知识库中没有答案,请明确告诉用户。
然后 RAG 提供:
text
公司内部文档
↓
检索
↓
相关内容
最终:
text
System Prompt
+
User Prompt
+
Retrieved Context
↓
LLM
↓
最终回答
这就是后面学习 RAG 必须理解的基础。
二十五、一个完整的 AI 技术助手 Prompt
现在把本章学到的内容组合起来。
可以设计成:
text
# 角色
你是一名资深 Java 后端与 AI 应用开发工程师。
# 任务
帮助用户解决 Java、Spring Boot、Spring AI
以及 AI 应用开发相关的问题。
# 回答原则
1. 使用中文。
2. 优先给出明确结论。
3. 复杂问题分步骤解释。
4. 代码优先使用 Java。
5. 如果涉及 Spring AI,给出完整实现思路。
6. 不确定的信息不要编造。
7. 如果存在多个方案,比较方案优缺点。
# 输出格式
## 结论
## 原理
## 示例
## 注意事项
用户只需要:
text
什么是 Spring AI 的 RAG?
AI 就会根据这套规则进行回答。
这时候我们已经从:
text
简单聊天
进阶到了:
text
AI 技术助手
二十六、把本章知识串起来
现在重新看整个 Spring AI Prompt 体系。
text
ChatClient
│
↓
Prompt
│
┌──────────────┼──────────────┐
↓ ↓ ↓
System User Context
│ │ │
↓ ↓ ↓
角色规则 用户问题 业务数据
│ │ │
└──────────────┼──────────────┘
↓
最终 Prompt
↓
LLM
↓
AI Response
而 PromptTemplate 负责:
text
固定模板
+
动态参数
↓
动态 Prompt
所以整个关系可以记成:
text
Prompt
├── System
├── User
├── Context
└── Template
二十七、本章实战:把聊天接口升级成 AI 技术助手
上一章我们的接口是:
java
public String chat(String message) {
return chatClient
.prompt()
.user(message)
.call()
.content();
}
现在升级成:
java
public String chat(String message) {
return chatClient
.prompt()
.system("""
你是一名资深 Java 后端与 AI 应用开发工程师。
你主要帮助用户解决:
Java、Spring Boot、Spring AI、
MySQL、Redis 以及 AI 应用开发问题。
回答要求:
1. 使用中文。
2. 先给出结论。
3. 再解释原理。
4. 涉及代码时使用 Java。
5. 不确定的信息不要编造。
""")
.user(message)
.call()
.content();
}
现在我们的 AI 已经拥有了:
text
角色
+
职责
+
回答规范
+
用户问题
这就是一个最简单的:
AI Persona / AI Assistant
二十八、进一步思考:Prompt 应该放在哪里?
随着项目发展,Prompt 可能越来越多:
text
聊天 Prompt
翻译 Prompt
摘要 Prompt
RAG Prompt
面试 Prompt
代码分析 Prompt
客服 Prompt
如果全部写在 Service:
java
@Service
public class AiService {
// 几百行 Prompt
}
最终会非常难维护。
因此后面的企业级 AI 项目通常需要考虑:
text
Prompt 管理
版本控制
Prompt 测试
Prompt 评估
Prompt 灰度
Prompt 监控
这也是从:
AI Demo
走向:
企业级 AI 应用
必须考虑的问题。
二十九、本章常见问题
问题 1:System Prompt 一定比 User Prompt 更重要吗?
不能简单理解成"谁更重要"。
它们承担的职责不同:
text
System
↓
应用规则、角色、行为约束
User
↓
当前任务
真正的效果取决于:
text
Prompt 设计
+
模型能力
+
上下文
+
任务本身
问题 2:PromptTemplate 是不是必须使用?
不是。
简单场景:
java
.user(message)
就够了。
当 Prompt 出现大量动态参数时,模板化会更容易维护。
问题 3:Prompt 越长越好吗?
不是。
Prompt 应该:
text
清晰
准确
结构化
可验证
而不是单纯追求长度。
问题 4:Prompt 能解决 AI 幻觉吗?
不能完全解决。
Prompt 可以:
text
降低部分错误
约束回答方式
要求模型不要编造
但如果业务要求:
必须基于企业真实知识回答
那么通常还需要:
text
RAG
+
知识库
+
检索
+
结构化输出
+
业务校验
三十、本章总结
这一章我们从一个最简单的问题:
text
什么是 Prompt?
逐渐深入到了:
text
Prompt
↓
System Prompt
↓
User Prompt
↓
PromptTemplate
↓
动态参数
↓
Context
↓
AI Assistant
最重要的几个概念可以记住:
System Prompt
告诉 AI 你是谁、应该做什么、遵守什么规则。
User Prompt
告诉 AI 用户当前想完成什么任务。
PromptTemplate
把固定 Prompt 结构 和动态参数分离。
Context
给 AI 提供完成任务所需要的额外信息。
最终:
text
System
+
User
+
Context
↓
Prompt
↓
LLM
↓
Answer
这就是最基础的 AI 应用交互模型。
三十一、下一章:Structured Output
现在我们遇到了一个新的问题。
假设用户问:
text
帮我分析一下 Redis。
AI 返回:
text
Redis 是一个......
对于聊天应用来说,这没有问题。
但是如果我们正在开发一个 Java 系统,程序真正想要的可能是:
json
{
"name": "Redis",
"category": "缓存",
"difficulty": "高级",
"features": [
"内存存储",
"高性能",
"丰富的数据结构"
]
}
因为 Java 程序需要继续处理这些数据。
那么问题来了:
如何让大模型稳定地返回 Java 程序可以直接使用的结构化数据?
这就进入 Spring AI 非常重要的能力:
《Spring AI Structured Output 实战:让 AI 返回 Java 对象》
下一章我们将学习:
text
LLM
↓
结构化输出
↓
JSON
↓
Java Object
↓
业务系统
从这一章开始,Spring AI 将真正从:
"AI 聊天"
逐渐进入:
"AI 业务应用开发"。