首页 / Spring AI 入门教程 / Prompt 模板与提示词管理

Spring AI 入门教程

Prompt 模板与提示词管理

本教程共 45 篇 · 第 7 篇 · 更新于 2026-08-16 · 约 7 分钟阅读

Spring AIPromptTemplate提示词SystemPromptTemplateStringTemplate占位符Java

本节目标:掌握 PromptTemplate 的占位符语法,学会从 classpath 加载模板、注入动态参数、管理系统提示词。

第 4 章用过 .user(u -> u.text("...{actor}...").param("actor", ...))。这层语法糖的底层就是 PromptTemplate。本章把它拆开看。

先理解 Spring AI 对提示词的处理方式。官方文档打了个比方:提示词类似于 Spring MVC 里的 View。View 是带占位符的模板,运行时填充数据。提示词也一样,先写文本和占位符,再根据请求替换。另一个类比是带表达式的 SQL 语句。模板把”提示词长什么样”和”提示词填什么值”分开,代码会干净很多。

文档里还有一层比喻:ChatModel 相当于 JDBC,ChatClient 相当于 JdbcClient。前者是基础能力,后者在它之上提供高级构造,比如用 Advisor 考虑历史交互、增强上下文。提示词类属于 JDBC 这一层的基石。理解这层关系,就知道为什么先讲模板,再讲更上层的 API。

7.1 Prompt 与 Message

Prompt 是消息的容器。它装着一组有序的 Message,外加请求选项 ChatOptions。每条消息承担一个角色。

角色由 MessageType 枚举定义:

public enum MessageType {
    USER("user"),
    ASSISTANT("assistant"),
    SYSTEM("system"),
    TOOL("tool");
}

四个角色的分工:

  • SYSTEM:设定 AI 的行为和回答风格,相当于开场白指令
  • USER:用户的输入,是对话的基础
  • ASSISTANT:AI 的回复,可能携带工具调用请求
  • TOOL:工具调用的结果,响应 ASSISTANT 里的调用请求

多轮对话里,消息按顺序排成一列,模型靠角色区分谁说了什么。角色不只是标签,它决定模型如何理解上下文。系统消息设定规则,用户消息提出请求,助手消息延续对话,工具消息补充数据。

Message 接口本身很简单,只要求提供内容和元数据:

public interface Content {
    String getContent();
    Map<String, Object> getMetadata();
}

public interface Message extends Content {
    MessageType getMessageType();
}

多模态消息还实现了 MediaContent 接口,额外携带媒体列表,第 9 章会用到。

Prompt 还提供了一些便捷方法:

  • getUserMessage():取最后一条用户消息
  • getSystemMessage():取第一条系统消息
  • getUserMessages():取所有用户消息,保持顺序
  • getLastUserOrToolResponseMessage():取最后一条用户或工具响应消息

最后一个方法在多轮对话续接时很常用。

7.2 PromptTemplate:占位符语法

动态内容用占位符解决。PromptTemplate 的默认渲染引擎是 StTemplateRenderer,基于开源项目 StringTemplate。占位符写法是 {名称}

PromptTemplate promptTemplate = new PromptTemplate("给我讲一个关于 {topic} 的 {adjective} 笑话");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
return chatModel.call(prompt).getResult();

create() 返回 PromptPromptTemplate 还有两组方法:

  • render():返回渲染后的字符串
  • createMessage():返回单条 Message

三个接口分别对应这三种产物:PromptTemplateStringActionsPromptTemplateMessageActionsPromptTemplateActions。日常开发用 create() 就够,另外两个接口在需要手动组装时有用。

渲染本身走 TemplateRenderer 接口。它接收模板字符串和变量 Map,返回渲染结果。想换渲染逻辑,实现这个接口替换默认实现即可。

7.3 系统提示词模板

系统提示词也经常需要动态内容。SystemPromptTemplate 是专门的变体,创建 system 角色的消息:

String systemText = """
    你是一个乐于助人的 AI 助手。
    你的名字是 {name}。
    请用 {voice} 的风格回答。
    """;

SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));

Prompt prompt = new Prompt(List.of(userMessage, systemMessage));

用户消息和系统消息组装成一个 Prompt,交给 ChatModel 调用。系统消息负责设定人设和规则,用户消息负责实际请求,两者职责清晰。人设变了只改模板,业务代码不用动。消息顺序也有讲究,系统消息通常放在前面,模型先读到规则再读请求。

7.4 从 classpath 加载模板

提示词写死在代码里不好维护。Spring 的 Resource 抽象可以直接用于 PromptTemplate

@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);

模板文件放在 src/main/resources/prompts/ 下,后缀用 .st 是 StringTemplate 的惯例。改提示词不用重新编译,还能和其他人共享文件。项目里提示词多起来以后,文件管理比字符串管理省心得多。模板内容还能在测试里直接断言,比断言拼接好的字符串稳当。

7.5 自定义渲染器与分隔符

默认的 {} 占位符有个坑:JSON 也大量使用花括号。模板里包含 JSON 示例时,解析会冲突。解决办法是换分隔符:

PromptTemplate promptTemplate = PromptTemplate.builder()
    .renderer(StTemplateRenderer.builder()
        .startDelimiterToken('<')
        .endDelimiterToken('>')
        .build())
    .template("""
        列举 <composer> 配乐的 5 部电影。
        """)
    .build();

String prompt = promptTemplate.render(Map.of("composer", "John Williams"));
Tip

提示词含 JSON 结构时,用 < > 作占位符分隔符。
渲染逻辑需要完全自定义时,实现 TemplateRenderer 接口即可。不需要渲染的静态模板,可以用 NoOpTemplateRenderer

7.6 提示词工程要点

提示词质量直接决定输出质量。官方文档建议包含四类内容:

  1. 指令:清晰告诉 AI 要做什么
  2. 外部上下文:背景信息和限定条件
  3. 用户输入:用户的实际问题
  4. 输出指示器:期望的输出格式

输出格式最容易翻车。模型可能在 JSON 前面加一句”这是您的 JSON”,或者生成一个看起来像 JSON 但不合法的东西。所以第 8 章的结构化输出转换器才那么重要。

基础技巧包括文本摘要、问答、文本分类、对话和代码生成。进阶技巧有零样本、小样本学习、思维链(Chain-of-Thought)和 ReAct。零样本不给例子直接问,小样本先给几个例子再问,思维链让模型分步推理。一个知名研究表明,提示词以”深呼吸,逐步解决这个问题”开头,能显著提升解题效果。社区里分享和讨论提示词是常态,好的提示词值得反复打磨。

7.7 Token 与成本

Token 是模型处理文本的最小单位。一个 token 大约对应 0.75 个英文单词。计费按 token 算,输入输出都算。每个模型有上下文窗口上限,超出的输入不会处理。

实际含义很简单:提示词越短越省钱,只发送必要的信息。上下文窗口也是约束:查询《哈姆雷特》时,不需要把莎士比亚全集都塞进去。响应元数据里包含 token 用量,可以做成本监控。模板加占位符还有个好处:token 开销可视化,模板里哪段是固定开销、哪段是动态开销,一眼就能看出来。

7.8 小结

PromptTemplate 把动态内容从代码里解放出来。占位符负责注入,Resource 负责文件管理,SystemPromptTemplate 负责角色消息。模板和提示词工程配合,是稳定输出的基础。下一章讲结构化输出,模板里的 {format} 占位符会再次出现。