Spring AI PromptTemplate插入JSON时花括号冲突怎么办?三种可靠解决方案

Spring AI PromptTemplate插入JSON时花括号冲突怎么办?三种可靠解决方案 文章摘要Spring AI默认使用花括号识别Prompt模板变量。当Prompt中包含JSON Schema、示例对象或代码时JSON自身的{}可能被误当成模板变量导致渲染报错、变量缺失或内容被错误替换。本文通过错误示例介绍自定义 分隔符、NoOpTemplateRenderer和外部Resource模板三种解决方式并说明ChatClient模板与Advisor内部模板的作用范围差异。一、典型问题PromptStringtemplate 请根据用户输入返回JSON { name: {name}, age: 18 } ;这里存在两类花括号JSON对象花括号 模板变量{name}Spring AI默认模板渲染器使用{variable}识别变量。复杂JSON中可能出现把JSON字段误当变量提示缺少变量Schema渲染失败示例对象被修改Advisor模板报错。二、方案一修改模板变量分隔符这是最推荐的方法。使用variable作为变量语法。importorg.springframework.ai.chat.prompt.PromptTemplate;importorg.springframework.ai.template.st.StTemplateRenderer;PromptTemplatepromptTemplatePromptTemplate.builder().renderer(StTemplateRenderer.builder().startDelimiterToken().endDelimiterToken().build()).template( 请根据用户信息返回JSON { name: name, department: department, enabled: true } ).build();StringresultpromptTemplate.render(Map.of(name,张三,department,产品部));JSON花括号保持原样只有name和department会被替换。三、在ChatClient中配置自定义RendererBeanChatClientjsonFriendlyChatClient(ChatModelchatModel){returnChatClient.builder(chatModel).templateRenderer(StTemplateRenderer.builder().startDelimiterToken().endDelimiterToken().build()).build();}调用StringresponsechatClient.prompt().user(user-user.text( 把以下信息转换为JSON { customer: customer, requirement: requirement } ).param(customer,某白酒企业).param(requirement,渠道动销分析)).call().content();注意ChatClient配置的TemplateRenderer只影响直接在ChatClient中定义的user和system模板。它不会自动修改Advisor内部使用的模板。四、方案二不需要模板时使用NoOpTemplateRenderer如果Prompt已经完整不需要变量替换Stringprompt 请严格返回以下JSON结构 { status: SUCCESS, data: { value: 100 } } ;可以关闭模板渲染BeanChatClientnoTemplateChatClient(ChatModelchatModel){returnChatClient.builder(chatModel).templateRenderer(NoOpTemplateRenderer.INSTANCE).build();}适合动态字符串已经提前生成Prompt中大量JSONPrompt由外部系统渲染不需要Spring AI变量替换。缺点是不能再使用.param(...)。因此是否使用NoOp应按客户端用途拆分不要全局关闭后又期待模板变量生效。五、方案三外部Resource模板将Prompt放到src/main/resources/prompts/customer-analysis.st模板你是企业经营分析助手。 客户customer 分析周期period 请返回 { summary: 分析结论, risks: [ { name: 风险名称, level: HIGH } ] }加载Value(classpath:/prompts/customer-analysis.st)privateResourcepromptResource;构建PromptTemplatetemplatePromptTemplate.builder().resource(promptResource).renderer(StTemplateRenderer.builder().startDelimiterToken().endDelimiterToken().build()).build();Stringprompttemplate.render(Map.of(customer,某渠道客户,period,2026年7月));外部文件便于版本控制代码审查单独测试多语言Prompt复用减少Java字符串噪声。六、为什么不推荐手工转义所有花括号问题是不同模板引擎转义规则不同JSON Schema非常长可读性差容易漏掉嵌套对象模板升级后可能失效开发者难以区分JSON与变量。改变变量分隔符通常更清晰。七、JSON Schema场景复杂Schema中花括号密集推荐模板变量使用 JSON继续使用{ }例如Stringtemplate 任务task 输出必须符合以下Schema { type: object, properties: { result: { type: string } } } ;八、Advisor内部模板要单独配置假设你配置了ChatClient.builder(chatModel).templateRenderer(customRenderer)QuestionAnswerAdvisor仍然可能使用自己的模板。原因是ChatClient模板 Advisor内部模板属于不同作用范围。自定义RAG模板时需要在Advisor Builder中设置对应PromptTemplate。排查方法普通user模板是否正常 RAG开启后是否报错 错误是否来自QuestionAnswerAdvisor Advisor是否有独立模板配置九、模板变量缺失如何提前发现不要等到运行时才发现。可以写单元测试classPromptTemplateTest{TestvoidshouldRenderJsonTemplate(){PromptTemplatetemplatecreateTemplate();Stringresulttemplate.render(Map.of(name,张三));assertThat(result).contains(\name\: \张三\);assertThat(result).contains(\enabled\: true);}}测试所有变量可以渲染JSON格式保留没有未替换变量中文和换行正确示例代码没有被破坏。十、检测未替换变量privatestaticfinalPatternUNRESOLVEDPattern.compile(var_[a-zA-Z0-9_]);publicstaticvoidvalidateRenderedPrompt(Stringprompt){MatchermatcherUNRESOLVED.matcher(prompt);if(matcher.find()){thrownewIllegalArgumentException(存在未替换变量matcher.group());}}推荐变量统一命名var_customer var_period避免与普通XML标签混淆。十一、用户输入中包含花括号怎么办变量值可能是请解释Java中的MapString, Object和JSON { }。正常模板替换不会再次递归解析变量值。不要对渲染结果重复执行模板渲染否则用户输入中的花括号可能被第二次解释。十二、模板安全PromptTemplate不是安全过滤器。用户变量仍可能包含忽略之前的指令需要独立处理Prompt Injection输入长度敏感信息HTML日志脱敏数据权限。模板渲染只负责变量替换不负责内容可信度。十三、选择建议Prompt包含JSON并需要变量使用自定义 分隔符Prompt不需要变量使用NoOpTemplateRendererPrompt较长、需要版本控制使用Resource外部模板 自定义分隔符Advisor内部Prompt在Advisor中单独配置模板总结Spring AI PromptTemplate与JSON冲突的根因是模板变量和JSON共用花括号最可靠的解决方式是将变量改为variable并通过单元测试确认JSON结构和变量替换都正确。