统一环境: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 智能体、多场景问答等高阶开发需求。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。