第六章 PromptTemplate 提示词模板与变量替换
版本标注
- Spring AI:
1.1.2- Spring AI Alibaba:
1.1.2.0章节定位
- 在官方示例中,模板不仅用于普通问答,也常用于 RAG 的 Prepare 阶段、Agent 指令模板、Routing 决策与结构化输出提示词设计。
s01 > s02 > s03 > s04 > s05 > [ s06 ] s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18
"把提示词参数化, 才能从演示代码走向可复用代码" -- 模板解决的是复用、维护和动态拼装。
一、为什么需要 PromptTemplate?
1.1 不使用模板的问题
在日常开发中,我们经常需要根据不同的输入去问 AI:
- "介绍下 Java"
- "介绍下 Python"
- "介绍下 Go"
如果不用模板,你会这么写:
// 每个问题都要写一个新的请求
@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();
}
@GetMapping("/chat/go")
public String chatGo() {
return chatClient.prompt().user("介绍下Go").call().content();
}
这种代码:
- 大量重复
- 难以维护
- 无法复用
1.2 模板的价值
使用 PromptTemplate,你可以这样写:
// 定义一个模板,使用 {topic} 作为占位符
// "介绍下{topic}"
// 运行时传入不同的 topic 值,就可以生成不同的请求
// 调用1:topic = Java → "介绍下Java"
// 调用2:topic = Python → "介绍下Python"
// 调用3:topic = Go → "介绍下Go"
模板的优势:
- 一套模板,复用多个场景
- 代码简洁,易维护
- 动态参数替换,灵活性强
二、核心概念详解
2.1 PromptTemplate
PromptTemplate 是 Spring AI 中用于创建带变量模板的 Prompt 的类。
基本语法:
// 1. 创建模板(使用 {} 包裹占位符)
PromptTemplate template = new PromptTemplate(
"你好,我叫{name},今年{age}岁"
);
// 2. 填充变量(Map 的 key 对应模板中的占位符)
Prompt prompt = template.create(Map.of(
"name", "张三",
"age", "25"
));
// 3. 调用 AI
chatClient.prompt(prompt).call().content();
// 输出:你好,我叫张三,今年25岁
2.2 SystemPromptTemplate
SystemPromptTemplate 是专门用于创建系统消息的模板,用法几乎一样:
// 创建系统消息模板
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(
"你是一个{profession}专家,擅长{skill}"
);
// 填充变量
Message systemMessage = systemTemplate.createMessage(Map.of(
"profession", "Java",
"skill", "后端开发"
));
// 输出:你要是一个Java专家,擅长后端开发
2.3 模板文件支持
Spring AI 还支持从外部文件加载模板:
# 在 resources/prompttemplate/atguigu-template.txt 中
# 内容如下:
讲一个关于{topic}的故事,并以{output_format}格式输出,字数在{wordCount}左右
# Java 代码中读取
@Value("classpath:/prompttemplate/atguigu-template.txt")
private Resource userTemplate;
PromptTemplate promptTemplate = new PromptTemplate(userTemplate);
三、项目代码详解
3.1 模板文件
首先在 src/main/resources/prompttemplate/ 目录下创建模板文件 atguigu-template.txt:
讲一个关于{topic}的故事,并以{output_format}格式输出,字数在{wordCount}左右
3.2 控制器代码
package com.atguigu.study.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.core.io.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.Map;
/**
* PromptTemplate 演示控制器
* 展示提示词模板的多种使用方式
*/
@RestController
public class PromptTemplateController
{
@Resource(name = "deepseek")
private ChatModel deepseekChatModel;
@Resource(name = "qwen")
private ChatModel qwenChatModel;
@Resource(name = "deepseekChatClient")
private ChatClient deepseekChatClient;
@Resource(name = "qwenChatClient")
private ChatClient qwenChatClient;
/**
* 方式一:代码中直接定义模板
*
* 接口:http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200
*/
@GetMapping("/prompttemplate/chat")
public Flux<String> chat(
@RequestParam("topic") String topic,
@RequestParam("output_format") String output_format,
@RequestParam("wordCount") String wordCount)
{
// 1. 在代码中定义模板字符串
PromptTemplate promptTemplate = new PromptTemplate(
"讲一个关于{topic}的故事," +
"并以{output_format}格式输出," +
"字数在{wordCount}左右"
);
// 2. 创建 Prompt 并填充变量
// Map 中的 key 对应模板中的占位符 {topic}, {output_format}, {wordCount}
Prompt prompt = promptTemplate.create(Map.of(
"topic", topic, // 替换 {topic}
"output_format", output_format, // 替换 {output_format}
"wordCount", wordCount // 替换 {wordCount}
));
// 3. 调用 AI(服务端渲染模板后发送请求)
// 最终发送的消息是:"讲一个关于java的故事,并以html格式输出,字数在200左右"
return deepseekChatClient.prompt(prompt).stream().content();
}
/**
* 方式二:从外部模板文件加载
*
* 文件位置:src/main/resources/prompttemplate/atguigu-template.txt
*
* 接口:http://localhost:8006/prompttemplate/chat2?topic=java&output_format=html
*/
@Value("classpath:/prompttemplate/atguigu-template.txt")
private org.springframework.core.io.Resource userTemplate;
@GetMapping("/prompttemplate/chat2")
public String chat2(
@RequestParam("topic") String topic,
@RequestParam("output_format") String output_format,
@RequestParam("wordCount") String wordCount)
{
// 1. 从外部文件创建模板
PromptTemplate promptTemplate = new PromptTemplate(userTemplate);
// 2. 填充变量
Prompt prompt = promptTemplate.create(Map.of(
"topic", topic,
"output_format", output_format,
"wordCount", wordCount
));
// 3. 调用 AI(非流式)
return deepseekChatClient.prompt(prompt).call().content();
}
/**
* 方式三:同时使用系统模板和用户模板
*
* SystemPromptTemplate:创建系统消息模板
* PromptTemplate:创建用户 Prompt 模板
*
* 接口:http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=知识产权法
*/
@GetMapping("/prompttemplate/chat3")
public String chat3(
@RequestParam("sysTopic") String sysTopic,
@RequestParam("userTopic") String userTopic)
{
// 1. 创建系统消息模板
// {systemTopic} 是占位符
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是{systemTopic}助手,只回答{systemTopic}其它无可奉告,以HTML格式的结果。"
);
Message sysMessage = systemPromptTemplate.createMessage(
Map.of("systemTopic", sysTopic) // 替换占位符
);
// 2. 创建用户 Prompt 模板
PromptTemplate userPromptTemplate = new PromptTemplate("解释一下{userTopic}");
Prompt userPrompt = userPromptTemplate.create(
Map.of("userTopic", userTopic)
);
Message userMessage = userPrompt.getInstructions().get(0);
// 3. 组合多个 Message 为一个 Prompt
// Prompt 可以包含多个 Message(系统消息 + 用户消息 + 历史消息等)
Prompt prompt = new Prompt(List.of(sysMessage, userMessage));
// 4. 调用 AI
return deepseekChatClient.prompt(prompt).call().content();
}
/**
* 方式四:使用 ChatModel 直接调用(底层API)
*
* 使用 SystemMessage + UserMessage + Prompt 的组合
*
* 接口:http://localhost:8006/prompttemplate/chat4?question=牡丹花
*/
@GetMapping("/prompttemplate/chat4")
public String chat4(@RequestParam("question") String question)
{
// 1. 创建系统消息(角色设定)
SystemMessage systemMessage = new SystemMessage(
"你是一个Java编程助手,拒绝回答非技术问题。"
);
// 2. 创建用户消息
UserMessage userMessage = new UserMessage(question);
// 3. 组合成 Prompt(可以用 List.of 传入可变数量的 Message)
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
// 4. 调用底层 ChatModel(需要手动提取结果)
// getResult().getOutput().getText() 是标准的提取路径
String result = deepseekChatModel.call(prompt)
.getResult()
.getOutput()
.getText();
System.out.println(result);
return result;
}
/**
* 方式五:使用 ChatClient 的简洁 API(推荐)
*
* 直接用链式调用设置系统消息和用户消息
*
* 接口:http://localhost:8006/prompttemplate/chat5?question=火锅
*/
@GetMapping("/prompttemplate/chat5")
public Flux<String> chat5(@RequestParam("question") String question)
{
return deepseekChatClient.prompt()
// .system() 方法直接设置系统消息
.system("你是一个Java编程助手,拒绝回答非技术问题。")
// .user() 方法设置用户消息
.user(question)
// 流式输出
.stream()
.content();
}
}
四、模板语法进阶
4.1 多变量模板
PromptTemplate template = new PromptTemplate(
"我是{name},来自{city},从事{profession},今年{age}岁"
);
Prompt prompt = template.create(Map.of(
"name", "李四",
"city", "北京",
"profession", "软件工程师",
"age", "28"
));
4.2 条件逻辑模板
// 可用三元运算符
PromptTemplate template = new PromptTemplate(
"用{style}风格的代码解释{concept}"
);
Prompt prompt = template.create(Map.of(
"style", "brief".equals(style) ? "简洁" : "详细",
"concept", "Java多线程"
));
4.3 循环模板(较少用)
// 如果需要处理列表,可以用辅助方法处理
StringBuilder sb = new StringBuilder();
for (Item item : items) {
sb.append(item.getName()).append(",");
}
PromptTemplate template = new PromptTemplate(
"推荐以下商品:" + sb.toString()
);
五、本章小结
5.1 核心类和方法
| 类/方法 | 说明 |
|---|---|
PromptTemplate | Prompt 模板,用于创建带变量的 Prompt |
SystemPromptTemplate | 系统消息模板,用于创建带变量的系统消息 |
PromptTemplate.create(Map) | 用 Map 填充变量并生成 Prompt |
SystemPromptTemplate.createMessage(Map) | 用 Map 填充变量并生成系统消息 |
Prompt(List.of(msgs)) | 组合多个消息为一个完整的 Prompt |
@Value("classpath:xxx") | 从外部文件加载模板 |
5.2 使用场景
| 场景 | 推荐方式 |
|---|---|
| 简单替换1-2个变量 | PromptTemplate 代码内定义 |
| 复杂模板/团队协作 | 外部模板文件 |
| 需要同时设置系统和用户消息 | SystemPromptTemplate + PromptTemplate |
| 快速开发 | ChatClient.prompt().system().user() |
本章重点:
- 理解 PromptTemplate 的核心原理(占位符替换)
- 掌握在代码中动态创建模板的方法
- 学会从外部文件加载模板
- 理解如何组合系统消息和用户消息
下章剧透(s07):
学会了动态Prompt,接下来我们将学习结构化输出——如何让AI返回我们想要的格式(如JSON、Java对象)。
📝 编辑者:Flittly
📅 更新时间:2026年4月
🔗 相关资源:Spring AI PromptTemplate API