Spring AI 学习指南(七)提示词模板化
统一环境:IDEA2026.1.1、JDK21、SpringBoot3.5.14、SpringAI1.1.6、智谱glm-4-flash、MySQL8.0
前置阅读:已掌握 Spring AI ChatClient 链式调用、Advisor 拦截机制、上下文参数透传核心能力
注意:本次代码基于前面几次指南开发,确保前面代码已经调试通过,pom.xml、application.yml与之前一致。
一、前言:从“随意硬编码”到“标准化模板引导”
在 AI 应用开发中,提示词(Prompt)是大模型交互的核心入口,提示词的精准度、规范性、完整性,直接决定 AI 输出结果的准确性、逻辑性和可用性,其重要性等同于传统开发中的核心业务 SQL。
但在初学开发阶段,绝大多数开发者都会采用 硬编码字符串拼接 的方式编写提示词,这种写法在简单demo中可以快速运行,一旦落地生产,会暴露大量致命问题:
-
提示词与业务代码强耦合,修改提示词需要改动 Java 代码、重新编译部署
-
重复提示词大量冗余,无法统一复用,维护成本极高
-
动态场景只能通过字符串拼接实现,代码臃肿、极易出现语法错误
-
系统角色、用户指令、上下文逻辑混杂,无结构化规范,排查问题困难
为解决以上工程化痛点,Spring AI 提供了两套核心模板组件:PromptTemplate、SystemPromptTemplate。将提示词实现模板与数据解耦,像使用 JDBC 占位符一样优雅、标准化的管理 AI 提示词,彻底告别野蛮硬编码。
本篇将拆解 Spring AI Prompt 体系、模板核心原理、基础用法、高阶技巧与生产避坑方案,带你完成 AI 开发的工程化升级。
二、Spring AI 核心:结构化 Prompt 体系
2.1 重新认识 Prompt:不止是一段文本
很多初学者误以为 Prompt 就是简单的用户提问文本,这是典型的认知误区。在 Spring AI 标准设计中,Prompt 是结构化容器,并非单一字符串,核心包含两大模块:
-
多角色 Message 集合:承载系统指令、用户提问、AI 回复、工具返回等多类型消息
-
ChatOptions 配置参数:控制模型生成逻辑(温度、最大 Token、采样策略等)
核心源码结构直观体现其设计思想:
public class Prompt implements ModelRequest<List<Message>> {
private final List<Message> messages;
private ChatOptions chatOptions;
}
这种设计和 JDBC 占位符思想高度契合:PromptTemplate 之于 ChatModel、ChatClient,等同于 SQL 占位符之于 JDBC。模板固定结构,参数动态注入,实现解耦复用。
2.2 Message 四大核心角色(核心基础)
Spring AI 通过 MessageType 枚举区分消息角色,不同角色各司其职,共同构建完整对话上下文,这是模板化提示词的底层基础。四大角色分工明确,是构建高质量 AI 对话的核心:
|
角色类型 |
核心作用 |
通俗类比 |
|---|---|---|
|
System |
设定 AI 人格、行为规则、能力边界、回答规范,用户不可见,全局生效 |
AI 的专属操作手册、身份定位 |
|
User |
承载用户真实提问、业务指令、需求描述 |
触发 AI 响应的触发器 |
|
Assistant |
存储 AI 历史回复、工具调用请求结果,用于拼接多轮上下文 |
AI 的思考与输出记录 |
|
Tool |
承载外部工具、接口、数据库执行后的返回数据 |
外部世界反馈给 AI 的回声 |
核心设计启示:角色分离的结构化设计,天然支撑 ReAct 智能体、工具调用、RAG 检索增强等高阶能力,让 AI 不止会文本对话,更具备思考与行动能力。
三、PromptTemplate:通用提示词模板核心实战
3.1 为什么必须用模板化?
在没有模板的开发模式下,不同业务场景需要重复编写大量相似提示词,代码冗余严重、完全无法复用,维护极其困难,典型反面示例如下:
// 硬编码写法:重复冗余、无法复用、维护困难
@GetMapping("/chat/java")
public String chatJava() {
return chatClient.prompt().user("介绍下Java编程语言的核心特点").call().content();
}
@GetMapping("/chat/python")
public String chatPython() {
return chatClient.prompt().user("介绍下Python编程语言的核心特点").call().content();
}
每新增一个场景,就需要新增一个接口、一段硬编码提示词,业务越多,代码越混乱。
而 PromptTemplate 彻底解决该问题:一套通用模板,动态注入参数,适配全场景,从根源消除代码冗余。
3.2 PromptTemplate 基础语法与实战
Spring AI 默认基于 StTemplateRenderer 模板引擎,采用 {参数名} 作为占位符,支持动态参数替换、模板渲染、快速生成结构化 Prompt。
核心开发流程:定义模板占位符 → 封装动态参数 → 渲染生成 Prompt → 调用模型
/**
* PromptTemplate 接口实战
* 模板复用 + 动态参数注入 + 会话上下文透传
* 访问地址:http://localhost:8080/tmpl/template?topic=程序员&adjective=搞笑
*/
@GetMapping("/template")
public String promptTemplate(String topic, String adjective){
// 1. 定义带占位符的通用模板
final String TEMPLATE = "给我讲一个关于{topic}的{adjective}笑话";
PromptTemplate promptTemplate = new PromptTemplate(TEMPLATE);
// 2. 传入动态参数,渲染生成结构化Prompt
Prompt prompt = promptTemplate.create(Map.of("topic", topic, "adjective", adjective));
// 3. 结合ChatClient调用模型,透传会话ID保证上下文隔离
return chatClient.prompt(prompt)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "test"))
.call()
.content();
}
该写法完美实现模板与数据解耦,后续只需修改传入参数,即可适配不同业务场景,无需改动模板结构。
3.3 分层接口设计(适配多场景开发)
PromptTemplate 采用分层接口设计,提供三种不同粒度的操作能力,开发者可按需选择,适配简单文本、消息构建、模型调用等不同场景:
-
PromptTemplateStringActions:基础能力,专注纯字符串模板渲染,适用于简单提示词场景
-
PromptTemplateMessageActions:进阶能力,支持构建、操作 Message 消息对象,适配多角色对话
-
PromptTemplateActions:高阶能力,直接生成可被 ChatModel 调用的 Prompt 对象,开箱即用
四、SystemPromptTemplate:系统提示词专属模板化
4.1 系统提示词的核心价值
System 系统提示词是 AI 对话的顶层规则,用于固定 AI 角色、约束回答规范、划定能力边界,全局贯穿整个对话流程,优先级高于用户提问。
Spring AI 单独提供 SystemPromptTemplate 专属组件,专门用于系统消息的模板化管理,区别于普通用户提示词,让角色配置、规则约束更规范、更易复用。
4.2 基础实战:动态系统提示词模板
通过 SystemPromptTemplate 实现动态角色配置,根据参数灵活切换 AI 身份与回答风格:
/**
* SystemPromptTemplate 系统提示词模板接口实战
* 动态配置AI角色、名称、回答风格
* 访问地址:http://localhost:8080/tmpl/sys
*/
@GetMapping("/sys")
public String systemTemplate(){
// 1. 定义用户固定提问消息
String userContent = "介绍三位黄金时代的海盗,分别说明他们的事迹,每人至少一句话介绍";
UserMessage userMessage = new UserMessage(userContent);
// 2. 定义带占位符的系统提示模板
String systemContent = """
你是一名专业的科普助手,名字叫{name}。
你的回答风格为{style},语言通俗易懂、逻辑清晰。
""";
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(systemContent);
// 3. 动态注入参数,生成系统角色消息
Message systemMessage = systemTemplate.createMessage(Map.of("name", "小科普助手", "style", "简洁严谨"));
// 4. 组合系统消息 + 用户消息,生成完整Prompt
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
// 5. 调用模型并透传会话上下文
return chatClient.prompt(prompt)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "test"))
.call()
.content();
}
4.3 生产核心:外部文件加载模板(代码与提示词解耦)
生产环境中,长文本系统提示词、复杂规则模板禁止硬编码在代码中。Spring AI 支持从 resources 资源文件加载 .st 模板文件,实现提示词与 Java 代码完全分离,修改提示词无需重启服务、无需编译代码。
步骤1:创建模板文件
在 resources/prompts/ 目录下新建 expert-system.st 模板文件:
扮演深耕{field}领域的资深专家,回答专业、简洁、易懂。
你的核心职责:解答用户关于{type}的相关问题,给出可落地的建议。
如果用户问题模糊,引导用户补充详细场景、需求与问题细节。
步骤2:代码加载并动态渲染
// 注入外部模板文件
@Value("classpath:prompts/expert-system.st")
private Resource expertTemplate;
/**
* 外部文件加载模板实战接口
* 实现提示词与代码完全解耦
* 访问地址:http://localhost:8080/tmpl/file
*/
@GetMapping("/file")
public String fileSystemTemplate(){
// 1. 加载外部.st模板文件,初始化系统模板
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(expertTemplate);
// 2. 动态参数渲染,替换模板占位符
Message systemMessage = systemTemplate.createMessage(Map.of("field", "Java后端开发", "type", "技术学习、项目实战"));
// 3. 输出渲染后的完整系统提示词
return systemMessage.getText();
}
适用场景:RAG 检索增强、AI Agent 指令、路由决策、结构化输出、复杂业务问答等需要长提示词的场景。
五、高阶进阶技巧(生产必备)
5.1 自定义模板分隔符,解决 JSON 语法冲突
模板默认使用 {} 作为占位符,但如果提示词中包含 JSON 格式内容,会与占位符语法冲突,导致渲染异常。Spring AI 支持自定义分隔符,彻底解决该问题:
/**
* 自定义模板分隔符接口实战
* 规避JSON {} 占位符语法冲突问题
* 访问地址:http://localhost:8080/tmpl/customDelimiter
*/
@GetMapping("/customDelimiter")
public String customDelimiter() {
// 构建自定义模板渲染器,修改占位符为 < >
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>')
.build())
// 自定义带新占位符的模板文本
.template("推荐5部由<composer>配乐的经典电影,简洁说明推荐理由")
.build();
// 动态参数渲染并返回结果
return promptTemplate.render(Map.of("composer", "久石让"));
}
5.2 多消息组合,构建复杂业务 Prompt
真实生产场景中,Prompt 往往需要整合系统规则、用户提问、历史对话上下文,通过多消息组合实现复杂业务对话:
5.3 结合 Advisor 实现 AOP 增强
结合前文讲解的 Advisor 拦截机制(类比 Spring MVC 拦截器),可以对模板渲染后的 Prompt 实现全局 AOP 增强:
-
统一打印模板渲染前后日志,方便调试排查
-
全局过滤敏感词、修正不规范提示词
-
统一追加全局通用规则、上下文参数
实现提示词模板渲染与业务增强解耦,进一步提升工程规范性。
六、完整整合:ChatTemplateController 全量源码 + 统一测试
前面我们拆分演示了模板化的各类核心用法,为方便大家直接复制运行、统一调试,这里给出 ChatTemplateController 完整可投产源码,整合本文所有案例:基础模板、系统模板、外部文件模板、自定义分隔符,自带会话上下文透传。
需要在 resources/prompts/ 目录下新建 expert-system.st 模板文件:
扮演深耕{field}领域的资深专家,回答专业、简洁、易懂。
你的核心职责:解答用户关于{type}的相关问题,给出可落地的建议。
如果用户问题模糊,引导用户补充详细场景、需求与问题细节。
package demo.ai.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.template.st.StTemplateRenderer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* Prompt模板化完整实战控制器
* 包含:基础PromptTemplate、SystemPromptTemplate、外部文件模板、自定义占位符分隔符
* 所有接口可直接浏览器访问测试,自带会话上下文透传
*/
@RestController
@RequestMapping("/tmpl")
public class ChatTemplateController {
@Resource
private ChatClient chatClient;
// 加载外部系统提示词模板文件
@Value("classpath:prompts/expert-system.st")
private org.springframework.core.io.Resource expertTemplate;
/**
* 基础 PromptTemplate 动态模板测试
* 访问地址:http://localhost:8080/tmpl/template?topic=程序员&adjective=搞笑
*/
@GetMapping("/template")
public String promptTemplate(String topic, String adjective){
// 1. 定义带占位符的通用文本模板
final String TEMPLATE = "给我讲一个关于{topic}的{adjective}笑话";
PromptTemplate promptTemplate = new PromptTemplate(TEMPLATE);
// 2. 动态参数渲染生成Prompt
Prompt prompt = promptTemplate.create(Map.of("topic", topic, "adjective", adjective));
// 3. 调用大模型并透传会话ID,支持多轮记忆上下文
return chatClient.prompt(prompt)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "test"))
.call()
.content();
}
/**
* SystemPromptTemplate 系统角色模板测试
* 访问地址:http://localhost:8080/tmpl/sys
*/
@GetMapping("/sys")
public String systemTemplate(){
// 1. 构建用户提问消息
String userContent = "介绍三位黄金时代的海盗,分别说明他们的事迹,每人至少一句话介绍";
UserMessage userMessage = new UserMessage(userContent);
// 2. 构建带占位符的系统提示模板
String systemContent = """
你是一名专业的科普助手,名字叫{name}。
你的回答风格为{style},语言通俗易懂、逻辑清晰。
""";
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(systemContent);
// 3. 动态注入角色、风格参数
Message systemMessage = systemTemplate.createMessage(Map.of("name", "小科普助手", "style", "简洁严谨"));
// 4. 组合系统消息+用户消息,生成完整对话Prompt
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
// 5. 调用模型获取带角色约束的回答
return chatClient.prompt(prompt)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "test"))
.call()
.content();
}
/**
* 外部.st模板文件加载测试
* 访问地址:http://localhost:8080/tmpl/file
*/
@GetMapping("/file")
public String fileSystemTemplate(){
// 1. 读取外部资源模板文件
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(expertTemplate);
// 2. 动态替换领域、问题类型占位符
Message systemMessage = systemTemplate.createMessage(Map.of("field", "Java后端开发", "type", "技术学习、项目实战"));
// 3. 输出渲染后的完整系统提示词
return systemMessage.getText();
}
/**
* 自定义占位符分隔符(< >)解决JSON冲突
* 访问地址:http://localhost:8080/tmpl/customDelimiter
*/
@GetMapping("/customDelimiter")
public String customDelimiter() {
// 自定义模板渲染器,修改占位符符号
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>')
.build())
.template("推荐5部由<composer>配乐的经典电影,简洁说明推荐理由")
.build();
// 动态参数渲染并返回模板文本
return promptTemplate.render(Map.of("composer", "久石让"));
}
}
统一测试说明
启动项目后,可直接通过浏览器访问以下地址,逐条验证所有模板功能:
-
基础动态模板测试:
http://localhost:8080/tmpl/template?topic=程序员&adjective=搞笑 -
系统角色模板测试:
http://localhost:8080/tmpl/sys -
外部文件模板渲染测试:
http://localhost:8080/tmpl/file -
自定义占位符分隔符测试:
http://localhost:8080/tmpl/customDelimiter
测试效果:所有接口均可动态渲染模板、参数自动替换、会话上下文正常透传,完全实现提示词与业务代码解耦,符合生产工程化规范。
七、生产最佳实践 & 避坑指南
✅ 推荐生产写法
-
系统提示词统一模板化:优先使用 SystemPromptTemplate 替代硬编码字符串,固定 AI 角色与规则,全局复用
-
长提示词外置文件:复杂系统规则、RAG、Agent 模板统一放在 .st 资源文件,实现代码与配置解耦
-
所有动态内容参数化:用户输入、业务变量、场景参数全部通过 param 注入,杜绝字符串拼接
-
模型参数统一托管:temperature、maxTokens、topP 等模型配置统一放在 ChatOptions,不混入提示词文本
-
短提示词简化开发:简单一次性对话,使用 ChatClient 流式 Lambda 写法,无需手动创建模板类
❌ 常见开发误区
-
语法冲突:模板包含 JSON 内容时,未修改占位符分隔符,导致模板渲染解析失败
-
角色混杂:将系统规则、用户提问、上下文全部拼接为一段 User 文本,无角色分层,AI 识别逻辑混乱
-
硬编码超长Prompt:复杂规则写死在代码中,迭代维护成本极高
八、本篇学习总结
本篇完成 Spring AI 提示词工程化的核心进阶,彻底告别野蛮硬编码开发模式,核心知识点总结如下:
-
理解 Spring AI 结构化 Prompt 体系:Prompt 是多角色消息+模型参数的容器,并非简单文本
-
掌握 PromptTemplate 通用模板,实现普通提示词参数化、复用化
-
精通 SystemPromptTemplate 专属系统模板,规范 AI 角色与行为规则
-
掌握外部模板文件加载、自定义分隔符、ChatClient 流式调用等高阶技巧
-
建立标准化提示词开发规范,适配企业级生产迭代
正如 Spring AI 官方设计理念:优质的提示词是提升 AI 输出质量、稳定性、可用性的最低成本、最高收益的优化手段。模板化开发,是 AI 应用从 Demo 走向生产的必经之路。
九、结语
优秀的 AI 应用开发,从来不是简单调用模型接口,而是工程化、标准化、可维护的体系搭建。
Prompt 模板化看似是基础能力,却是区分“初级调参玩家”和“高级工程开发”的核心分水岭。掌握这套体系,你可以轻松应对复杂业务场景、RAG 检索、Agent 智能体、多场景问答等高阶开发需求。
所有评论(0)