Spring AI提示词模板PromptTemplate:从基础构建到动态对话场景实战

张开发
2026/6/5 1:16:14 15 分钟阅读
Spring AI提示词模板PromptTemplate:从基础构建到动态对话场景实战
1. 初识Spring AI提示词模板第一次接触Spring AI的PromptTemplate时我正为一个儿童教育项目犯愁。我们需要根据孩子的年龄、兴趣自动生成不同难度的故事但直接调用大模型API返回的内容总是参差不齐。直到发现PromptTemplate这个神器才真正体会到什么叫做四两拨千斤。简单来说PromptTemplate就像是个智能填空题模板。你提前设计好故事框架比如请给{age}岁的小朋友讲一个关于{topic}的{length}故事实际调用时只需传入具体参数系统就会自动拼接成完整提示词。这比手动拼接字符串优雅多了既避免了引号嵌套的噩梦又保证了提示词结构的规范性。在Spring AI框架中PromptTemplate的核心价值在于参数化输入用{变量名}占位符动态替换内容类型安全自动校验参数类型避免运行时错误模板复用一次编写多次调用提升开发效率易维护性修改模板内容无需改动业务代码举个例子我们来看个基础模板String template 作为一名{role}请用{style}风格回答{question}; PromptTemplate promptTemplate new PromptTemplate(template);当我们需要生成客服场景的提示词时MapString, Object params Map.of( role, 资深技术顾问, style, 专业且友好, question, 如何重置路由器密码 ); Prompt prompt promptTemplate.create(params);这种写法比直接拼接作为一名资深技术顾问...的字符串要清爽得多特别是当模板内容复杂时优势更加明显。我曾在项目中处理过包含15个变量的模板如果用传统字符串拼接光转义字符就能让人抓狂。2. 构建动态对话系统去年开发智能客服系统时我深刻体会到多轮对话的复杂性。用户的问题往往需要结合上下文才能准确回答比如当用户先问你们有哪些云服务器接着问最便宜的配置是什么时AI需要记住前文提到的产品线才能给出准确报价。Spring AI通过SystemPromptTemplate和Message的组合完美解决了这个问题。下面通过故事创作助手的案例演示如何实现带角色设定的动态对话2.1 系统角色设定首先用SystemPromptTemplate定义AI的角色属性String systemTemplate 你是一位精通{genre}的文学创作助手具备以下特征 1. 语言风格{style} 2. 擅长领域{expertise} 3. 禁止事项{taboos} 请严格遵循这些设定进行创作; SystemPromptTemplate systemTemplate new SystemPromptTemplate(systemTemplate); Message systemMessage systemTemplate.createMessage(Map.of( genre, 奇幻文学, style, 生动形象且富有诗意, expertise, 世界观构建和人物刻画, taboos, 暴力血腥内容 ));这个系统消息会被注入到每次对话的上下文中确保AI始终保持特定人设。实测发现合理的系统提示能减少70%以上的跑题情况。2.2 用户对话处理用户输入通过普通PromptTemplate处理GetMapping(/generate) public String generateStory( RequestParam String theme, RequestParam String character) { String userTemplate 以{character}为主角创作一个关于{theme}的短篇故事; PromptTemplate userTemplate new PromptTemplate(userTemplate); Message userMessage userTemplate.createMessage(Map.of( character, character, theme, theme )); Prompt prompt new Prompt(List.of(systemMessage, userMessage)); return chatClient.prompt(prompt).call().content(); }2.3 上下文保持技巧要实现多轮对话的记忆功能只需在每次请求时携带历史消息ListMessage messages new ArrayList(); messages.add(systemMessage); // 保持系统设定 messages.addAll(historyMessages); // 添加历史对话 messages.add(userMessage); // 加入最新输入 Prompt prompt new Prompt(messages);我在项目中会使用Redis缓存最近3轮对话这样既能维持上下文连贯性又避免过长的提示词消耗太多token。记住一个原则大模型的短期记忆完全依赖于你提供的对话历史。3. 高级模板技巧经过多个项目的实战我总结出这些提升Prompt效果的经验3.1 模板嵌套技术复杂的业务场景往往需要多层模板嵌套。比如电商客服系统中String answerTemplate {greeting} 关于您咨询的{product}问题 {solution} {ending}; String solutionTemplate {#if isRefund}根据我们的退款政策{policy}{/if} {#if isExchange}可前往{location}办理换货{/if}; PromptTemplate solutionTmpl new PromptTemplate(solutionTemplate); String solution solutionTmpl.create(Map.of( isRefund, true, policy, 7天无理由退货 )); PromptTemplate answerTmpl new PromptTemplate(answerTemplate); Prompt prompt answerTmpl.create(Map.of( greeting, 尊敬的VIP用户您好, product, 智能手表, solution, solution, ending, 祝您购物愉快 ));这种嵌套结构特别适合需要条件判断的场景。注意要控制嵌套层级超过三层就会影响可读性。3.2 模板组合策略对于长文本生成我常用分块组合策略。比如生成产品评测报告String introTemplate 概述{product}的核心特性; String prosTemplate 列出{product}的{count}个优点; String consTemplate 客观评价{product}的不足; // 分别生成各部分 PromptTemplate introTmpl new PromptTemplate(introTemplate); String intro introTmpl.create(...); PromptTemplate prosTmpl new PromptTemplate(prosTemplate); String pros prosTmpl.create(...); // 组合最终提示词 String report String.join(\n\n, intro, pros, cons); Prompt finalPrompt new Prompt(report);这种方式比单一长模板更灵活各部分可以独立调整。有次客户临时要求增加竞品对比模块我只用新增一个模板块就实现了需求变更。3.3 异常处理机制模板渲染可能遇到参数缺失等异常推荐使用try-catch包装try { Prompt prompt promptTemplate.create(params); } catch (IllegalArgumentException e) { log.error(缺少必要参数: {}, e.getMessage()); // 回退到默认模板 Prompt prompt promptTemplate.create(defaultParams); }我在项目中会为每个模板配置fallback参数确保即使部分参数缺失也能生成基本可用的提示词。比如故事生成中缺少风格参数时默认使用通俗易懂风格。4. 实战故事创作助手让我们用完整案例串联所学知识。假设要开发一个支持动态设定的故事生成器需求如下可指定故事类型童话/科幻/悬疑能自定义角色特征支持多轮续写功能4.1 基础架构设计创建Spring Boot项目后先定义核心接口RestController RequestMapping(/story) public class StoryController { private final ChatClient chatClient; // 初始化系统角色设定 private final SystemPromptTemplate systemTemplate new SystemPromptTemplate( 你是一位{type}故事专家擅长 1. 使用{tone}风格叙事 2. 塑造{characterTraits}的角色 3. 创作包含{elements}的情节 请严格保持人设); PostMapping(/generate) public String generate( RequestBody StoryRequest request, RequestHeader(required false) String conversationId) { // 实现步骤见下文 } } record StoryRequest( String type, String tone, String characterTraits, String elements, String plotOutline) {}4.2 提示词组装逻辑在generate方法中实现核心逻辑// 1. 创建系统消息 Message systemMessage systemTemplate.createMessage(Map.of( type, request.type(), tone, request.tone(), characterTraits, request.characterTraits(), elements, request.elements() )); // 2. 处理用户输入 String userTemplate {#if outline}基于以下大纲继续创作{outline} {#else}请创作一个新的{type}故事{/if}; PromptTemplate userTemplate new PromptTemplate(userTemplate); Message userMessage userTemplate.createMessage(Map.of( outline, request.plotOutline(), type, request.type() )); // 3. 如果有历史对话从缓存加载 ListMessage messages new ArrayList(); messages.add(systemMessage); if (conversationId ! null) { messages.addAll(cache.get(conversationId)); } messages.add(userMessage); // 4. 调用大模型 Prompt prompt new Prompt(messages); String result chatClient.prompt(prompt).call().content(); // 5. 更新对话历史保留最近3轮 if (messages.size() 4) { messages.remove(1); // 移除最早的用户消息 } cache.put(conversationId, messages.subList(1, messages.size()));4.3 效果优化技巧通过以下策略提升生成质量温度系数调节创造性故事设temperature0.7严谨内容设0.3chatClient.prompt(prompt) .options(ChatOptions.builder() .withTemperature(0.7) .build()) .call();停止序列避免故事无限延续.options(ChatOptions.builder() .withStopWords(List.of(\n###, 完)) .build())重试机制当返回内容不符合要求时自动重试int retry 0; while (retry 3) { String content chatClient.prompt(prompt).call().content(); if (isValid(content)) { return content; } retry; }4.4 完整调用示例请求示例POST /story/generate Content-Type: application/json X-Conversation-Id: abc123 { type: 科幻, tone: 赛博朋克风格, characterTraits: 反叛的AI工程师, elements: 神经接口、地下黑客组织, plotOutline: 主角发现公司正在开发意识控制技术 }响应示例{ content: 在霓虹闪烁的新东京艾琳敲击着全息键盘她的神经接口闪烁着危险的蓝光。作为天网科技的首席工程师她刚刚破解了公司最深层的加密协议..., conversationId: abc123 }续写时只需携带相同的conversationId系统会自动保持故事连贯性。这种设计模式已经在我们多个内容生成项目中验证平均用户满意度达到4.8/5分。

更多文章