一、为什么需要提示词模板硬编码提示词的痛点在实际开发中我们经常看到这样的代码// ❌ 错误示范提示词硬编码在业务逻辑中 GetMapping(/recommend) public String recommend(String topic) { String prompt 你是一个专业的技术顾问请推荐 5 个关于%s的开源项目。 要求 1. 项目必须是活跃的最近 3 个月有更新 2. GitHub Star 数大于 1000 3. 提供项目简介和适用场景 4. 用 Markdown 表格形式输出 .formatted(topic); return chatClient.prompt(prompt).call().content(); }问题 提示词与业务代码耦合难以维护 调整提示词需要修改 Java 代码并重新编译 无法复用相似的提示词结构 长提示词影响代码可读性提示词模板的价值✅关注点分离提示词与业务逻辑解耦✅动态替换通过变量实现提示词复用✅外部管理支持从文件加载便于版本控制✅团队协作非技术人员也能参与提示词优化回到顶部二、PromptTemplate 核心概念什么是 PromptTemplatePromptTemplate是 Spring AI 提供的提示词模板类类似于 Java 的String.format()或前端模板引擎支持变量占位符使用{variable}语法动态赋值运行时替换变量内容模板复用同一模板适配不同场景核心 API// 1. 创建模板 PromptTemplate template new PromptTemplate(请介绍{topic}的基本概念); // 2. 添加变量 template.add(topic, Spring AI); // 3. 生成 Prompt Prompt prompt template.create(); // 4. 或使用链式调用 Prompt prompt PromptTemplate.builder() .resource(templateResource) .variables(Map.of(topic, Spring AI)) .build() .create();回到顶部三、实战一内联模板与变量替换基础用法RestController RequestMapping(/prompt/template) public class PromptTemplateController implements InitializingBean { Autowired private ChatModel dashScopeChatModel; private ChatClient chatClient; GetMapping(stream) public FluxString stream(String topic, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); // 1. 定义模板使用 {topic} 占位符 String template 请给我推荐几个关于{topic}的开源项目 ; // 2. 创建模板对象 PromptTemplate promptTemplate new PromptTemplate(template); // 3. 替换变量 promptTemplate.add(topic, topic); // 4. 发起对话SSE 流式输出 return chatClient.prompt(promptTemplate.create()).stream().content(); } Override public void afterPropertiesSet() throws Exception { chatClient ChatClient.builder(dashScopeChatModel) .defaultOptions( DashScopeChatOptions.builder() .temperature(0.7) .build() ) .build(); } }测试效果# 请求 1推荐 Java 相关项目 curl http://localhost:8080/prompt/template/stream?topicJava%20Web框架 # 请求 2推荐 Python 相关项目 curl http://localhost:8080/prompt/template/stream?topicPython%20机器学习同一模板不同变量输出完全不同简化写法Spring AI 还支持更简洁的一行代码写法GetMapping(stream1) public FluxString stream1(String topic, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); String template 请给我推荐几个关于{topic}的开源项目 ; // 一行代码创建模板 替换变量 生成 Prompt return chatClient.prompt( new PromptTemplate(template).create(Map.of(topic, topic)) ).stream().content(); }对比写法优点适用场景add()分步赋值逻辑清晰便于调试变量多、需要条件判断create(Map)一步到位代码简洁变量少、直接传递回到顶部四、实战二外部文件加载模板为什么需要外部文件当提示词变得非常长时如包含角色设定、少样本示例、输出格式要求硬编码在代码中会严重影响可读性。示例一个完整的提示词可能包含你是一个专业的 GitHub 项目收集专家具备以下能力 1. 熟悉主流编程语言生态 2. 了解开源项目的评价标准 3. 能够根据技术栈推荐合适的项目 请按照以下格式输出 | 项目名称 | GitHub 地址 | Star 数 | 简介 | 适用场景 | |---------|------------|---------|------|---------| 参考示例 InputJava Web 框架 Output| Spring Boot | github.com/spring-projects/spring-boot | 75k | ... | ... | 现在请推荐关于{topic}的开源项目编程语言限定为{language}。解决方案使用外部模板文件步骤 1创建模板文件在resources/templates/目录下创建open_source_system_prompt.st请给我推荐几个关于{topic}的开源项目,要求是和编程语言{language}相关的。文件命名建议.st后缀String Template 缩写语义化命名open_source_system_prompt.st按功能分类存放步骤 2在 Controller 中加载RestController RequestMapping(/prompt/template) public class PromptTemplateController implements InitializingBean { Autowired private ChatModel dashScopeChatModel; private ChatClient chatClient; // 1. 使用 Value 注解加载资源文件 Value(classpath:/templates/open_source_system_prompt.st) private Resource systemPrompt; GetMapping(/file) public FluxString file(RequestParam(value message) String message, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); // 2. 定义多个变量 HashMap variables new HashMap(); variables.put(language, Java); variables.put(topic, message); // 3. 使用 Builder 模式加载文件模板 PromptTemplate promptTemplate PromptTemplate.builder() .resource(systemPrompt) // 加载外部文件 .variables(variables) // 替换变量 .build(); // 4. 发起对话可同时设置 system 角色 return chatClient.prompt(promptTemplate.create()) .system(你是一个专业的的github项目收集人员) .stream() .content(); } Override public void afterPropertiesSet() throws Exception { chatClient ChatClient.builder(dashScopeChatModel) .defaultOptions( DashScopeChatOptions.builder() .temperature(0.7) .build() ) .build(); } }测试效果# 请求推荐 Java 相关的微服务框架 curl http://localhost:8080/prompt/template/file?message微服务框架 # 输出示例 | 项目名称 | GitHub 地址 | Star 数 | 简介 | 适用场景 | |---------|------------|---------|------|---------| | Spring Cloud | github.com/spring-cloud/spring-cloud | 15k | 基于 Spring Boot 的微服务框架集合 | 企业级 Java 微服务 | | Apache Dubbo | github.com/apache/dubbo | 40k | 高性能 RPC 框架 | 分布式服务治理 | | ... | ... | ... | ... | ... |Builder 模式的优势// ❌ 传统方式功能有限 PromptTemplate template new PromptTemplate(模板内容); template.add(key, value); // ✅ Builder 模式功能强大 PromptTemplate template PromptTemplate.builder() .resource(systemPrompt) // 支持文件加载 .variables(Map.of(key, value)) // 支持批量变量 .build();Builder 模式支持✅ 从Resource加载模板✅ 批量设置变量Map✅ 链式调用代码优雅回到顶部五、PromptTemplate 高级用法1. 多变量复杂模板String template 你是一个{role}请用{tone}的语气回答。 请分析以下{subject} {content} 输出要求 1. 字数不超过{max_words}字 2. 使用{language}语言 3. 包含{num_examples}个具体示例 ; PromptTemplate promptTemplate PromptTemplate.builder() .resource(new ByteArrayResource(template.getBytes())) .variables(Map.of( role, 技术专家, tone, 专业但通俗易懂, subject, Spring AI 的优势, content, 用户提供的技术文档..., max_words, 500, language, 中文, num_examples, 3 )) .build();2. 条件渲染模板// 根据条件动态选择模板 Resource template userLevel.equals(beginner) ? new ClassPathResource(templates/beginner_prompt.st) : new ClassPathResource(templates/advanced_prompt.st); PromptTemplate promptTemplate PromptTemplate.builder() .resource(template) .variables(Map.of(topic, topic)) .build();3. 模板继承与组合// 基础模板 String baseTemplate 你是一个{role}; // 扩展模板 String extendedTemplate baseTemplate 请完成以下任务 1. 分析{topic} 2. 提供{num}个示例 3. 输出格式{format} ; PromptTemplate template new PromptTemplate(extendedTemplate); template.add(role, 数据分析师); template.add(topic, 用户行为数据); template.add(num, 5); template.add(format, Markdown 表格);回到顶部六、模板文件管理的最佳实践1. 目录结构规范src/main/resources/ └── templates/ ├── system_prompts/ # 系统角色提示词 │ ├── tech_expert.st │ ├── teacher.st │ └── analyst.st ├── user_prompts/ # 用户任务提示词 │ ├── code_review.st │ ├── bug_fix.st │ └── feature_design.st └── output_formats/ # 输出格式模板 ├── json_output.st ├── markdown_table.st └── report.st2. 模板文件命名规范命名模式示例说明{场景}_{角色}_prompt.stcode_review_expert_prompt.st语义清晰{功能}_{语言}.strecommendation_java.st按功能语言分类{版本}_{描述}.stv2_detailed_analysis.st版本化管理3. 模板文件版本控制// 在文件名中体现版本 Value(classpath:/templates/v2_recommendation_prompt.st) private Resource promptV2; // 或使用配置文件管理版本 ConfigurationProperties(prefix prompt) public class PromptConfig { private String version v2; private String basePath templates/; }回到顶部七、常见陷阱与解决方案❌ 陷阱 1变量名拼写错误// 模板中{topc} 拼写错误 // 代码中template.add(topic, Java) // 结果变量不会被替换输出原始占位符解决方案// 1. 使用常量定义变量名 public class PromptVariables { public static final String TOPIC topic; public static final String LANGUAGE language; } // 2. 使用 Map 批量替换IDE 可检查拼写 MapString, String variables Map.of( PromptVariables.TOPIC, Java, PromptVariables.LANGUAGE, Java );❌ 陷阱 2模板文件未找到// 错误路径 Value(classpath:/template/open_source.st) // 少了一个 s private Resource systemPrompt; // 启动时报错Resource not found解决方案// 1. 添加空值检查 if (!systemPrompt.exists()) { throw new IllegalStateException(模板文件不存在open_source_system_prompt.st); } // 2. 使用 PostConstruct 验证 PostConstruct public void validateTemplates() { assert systemPrompt.exists() : 系统提示词模板缺失; }❌ 陷阱 3特殊字符未转义// 模板中包含 JSON 格式 String template 输出格式{topic: {topic}, count: 5} ; // 大括号可能与变量占位符冲突解决方案// 1. 使用双大括号转义如果模板引擎支持 String template 输出格式{{topic: {topic}, count: 5}} ; // 2. 将格式说明放在变量中 template.add(format, {\topic\: \topic\, \count\: 5});回到顶部八、完整 Controller 示例package cn.hollis.llm.llmentor.controller; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import jakarta.servlet.http.HttpServletResponse; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/prompt/template) public class PromptTemplateController implements InitializingBean { Autowired private ChatModel dashScopeChatModel; private ChatClient chatClient; /** * 方式一内联模板 add() 分步赋值 */ GetMapping(stream) public FluxString stream(String topic, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); String template 请给我推荐几个关于{topic}的开源项目 ; PromptTemplate promptTemplate new PromptTemplate(template); promptTemplate.add(topic, topic); return chatClient.prompt(promptTemplate.create()).stream().content(); } /** * 方式二内联模板 create(Map) 一步到位 */ GetMapping(stream1) public FluxString stream1(String topic, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); String template 请给我推荐几个关于{topic}的开源项目 ; return chatClient.prompt( new PromptTemplate(template).create(Map.of(topic, topic)) ).stream().content(); } /** * 方式三外部文件加载模板 */ Value(classpath:/templates/open_source_system_prompt.st) private Resource systemPrompt; GetMapping(/file) public FluxString file(RequestParam(value message) String message, HttpServletResponse response) { response.setCharacterEncoding(UTF-8); HashMap variables new HashMap(); variables.put(language, Java); variables.put(topic, message); PromptTemplate promptTemplate PromptTemplate.builder() .resource(systemPrompt) .variables(variables) .build(); return chatClient.prompt(promptTemplate.create()) .system(你是一个专业的的github项目收集人员) .stream() .content(); } Override public void afterPropertiesSet() throws Exception { chatClient ChatClient.builder(dashScopeChatModel) .defaultOptions( DashScopeChatOptions.builder() .temperature(0.7) .build() ) .build(); } }回到顶部九、PromptTemplate 对比传统方式维度硬编码String.format()PromptTemplate外部文件 Builder可读性❌ 提示词与代码混杂✅ 模板独立✅ 完全分离维护性❌ 修改需重新编译⚠️ 修改需重新编译✅ 修改无需编译复用性⚠️ 需手动封装✅ 原生支持✅ 原生支持团队协作❌ 仅开发人员可改⚠️ 需了解 Java✅ 非技术也可改版本控制✅ Git 管理✅ Git 管理✅ Git 管理适用场景简单提示词中等复杂度复杂提示词回到顶部十、总结Spring AI 的PromptTemplate提供了从简单到复杂的完整提示词管理方案三种方式的选择指南简单1-2个变量中等需要调试复杂超过50行提示词复杂度内联模板 create Map内联模板 add 分步外部文件 Builder一行代码搞定逻辑清晰便于调试团队协作独立管理最佳实践清单✅ 简单场景用内联模板复杂场景用外部文件✅ 变量名使用常量定义避免拼写错误✅ 模板文件按功能分类语义化命名✅ 使用 Builder 模式加载文件代码更优雅✅ 添加模板文件存在性检查避免运行时错误✅ 模板文件纳入 Git 版本控制便于追踪变更核心 API 速查// 1. 内联模板 分步赋值 PromptTemplate template new PromptTemplate(模板{var}); template.add(var, value); // 2. 内联模板 一步到位 Prompt prompt new PromptTemplate(模板{var}) .create(Map.of(var, value)); // 3. 外部文件 Builder PromptTemplate template PromptTemplate.builder() .resource(resource) .variables(Map.of(key, value)) .build();