Spring AI 之前的文章列表
Spring AI:对接DeepSeek实战
Spring AI:对接官方 DeepSeek-R1 模型 ------ 实现推理效果
Spring AI:ChatClient实现对话效果
Spring AI:使用 Advisor 组件 - 打印请求大模型出入参日志
Spring AI:ChatMemory 实现聊天记忆功能
Spring AI:本地安装 Ollama 并运行 Qwen3 模型
Spring AI:提示词工程
Prompt 角色分类
Prompt(提示词)
目前,我们知道通过 ChatModel 的 call() 方法 ,或则 steam() 方法,调用 AI 大模型,该方法入参接收一个 Prompt 实例,并返回 ChatResponse 响应。
Prompt 类可以看做一个容器,用于组织一系列 Message 消息对象和可选的 ChatOptions 请求配置。
以下是 Prompt 类的源码简化版(省略了构造器和工具方法):

java
public class Prompt implements ModelRequest<List<Message>> {
private final List<Message> messages; // 消息列表
private ChatOptions chatOptions; // 聊天配置选项
}
每个 Message 消息,在提示词中扮演不同的角色,其作用各不相同,涵盖用户消息、AI 生成的响应等等。这种结构支持与 AI 模型进行复杂精细的交互,因为提示词由多条消息构建而成,每条消息在对话中被赋予特定功能。
Message(消息)

查看 Message 消息接口源码,它封装了提示词的文本内容、元数据属性集合以及分类标识 MessageType。
接口定义如下:
java
// 内容接口
public interface Content {
String getContent(); // 获取文本内容
Map<String, Object> getMetadata(); // 获取元数据
}
// 消息接口
public interface Message extends Content {
MessageType getMessageType(); // 获取消息类型,如用户消息、系统消息、AI 回复等
}
另外,对于多模态消息类型 (指模型支持多种类型的数据输入或输出,如文本,图片等等),Spring AI 额外定义了 MediaContent 接口,提供媒体内容对象列表:
java
public interface MediaContent extends Content {
Collection<Media> getMedia(); // 支持图像/音频/视频等多媒体内容
}
Message 接口的多种实现,对应着 AI 模型可处理的不同类别的消息。
角色分类 (Roles)
每条消息都被赋予一个特定的角色 (role)。这些角色用于对消息进行分类,向 AI 大模型阐明在提示词中,每个部分的上下文和目的。这种结构化的方法增强了与 AI 沟通的细微差别和有效性,因为提示词中的每个部分,在交互中都扮演着独特且定义明确的角色。
主要角色包括:
-
系统角色 (System Role): 指导 AI 的行为和响应风格,设定 AI 如何解释和回复输入的参数或规则,比如让 AI 大模型扮演一个客服。类似于在开始对话之前,向 AI 大模型提供指令。
-
用户角色 (User Role): 即用户的输入------ 如向 AI 提出的问题、发出的命令或陈述。这个角色是基础性的,因为它构成了 AI 响应的依据。
-
助手角色 (Assistant Role):
即 AI 大模型对用户输入的响应。
它不仅仅是一个回答,对于维持对话的流畅性至关重要。通过跟踪 AI 之前的响应(即其 '助手角色' 消息),系统确保聊天是连贯的。
-
工具/函数角色 (Tool/Function Role): 调用外部服务,比如获取天气数据、查询数据库等等。这里仅做了解,后面会单独学习这块。
在 Spring AI 中,以上角色通过 MessageType 枚举类型来表示,源码如下:

java
public enum MessageType {
USER("user"), // 用户消息
ASSISTANT("assistant"), // 助手消息
SYSTEM("system"), // 系统消息
TOOL("tool"); // 工具/函数消息
}
ChatClient 指定系统角色消息
接下来,我们在代码中,实际感受一下,如何对消息设定角色。其实,在之前的文章中,就体验过,在初始化 ChatClient 对象时,为其指定一个系统角色,如下:

除了上面这种以 "全局" 的方式,设置系统角色外,也可以在接口中,动态、更灵活的指定系统角色消息,如下图所示:
java
@RestController
@RequestMapping("/v2/ai")
public class ChatClientController {
@Resource
private ChatClient chatClient;
// 省略...
/**
* 流式对话
* @param message
* @return
*/
@GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message,
@RequestParam(value = "chatId") String chatId
) {
// 流式输出
return chatClient.prompt()
.system("请你扮演一个智能客服")
.user(message) // 提示词
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
}
}
ChatModel 指定系统角色、用户角色消息
那么,如果是通过 ChatModel 这种方式来调用 AI 大模型,要如何指定 "系统角色" 的消息呢?也很简单,代码如下:
java
@RestController
@RequestMapping("/v6/ai")
public class PController {
@Resource
private DeepSeekChatModel chatModel;
// 省略...
/**
* 流式对话
* @param message
* @return
*/
@GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<AIResponse> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message) {
// 系统角色消息
SystemMessage systemMessage = new SystemMessage("请你扮演一个智能客服");
// 用户角色消息
UserMessage userMessage = new UserMessage(message);
// 构建提示词
Prompt prompt = new Prompt(Arrays.asList(systemMessage, userMessage));
// 流式输出
return chatModel.stream(prompt)
.mapNotNull(chatResponse -> {
Generation generation = chatResponse.getResult();
String text = generation.getOutput().getText();
return AIResponse.builder().v(text).build();
});
}
}
解释一下上面的代码:
- new SystemMessag() : 设置一个系统角色的消息,如指定 AI 大模型扮演一个客服;
- new UserMessage() : 初始化一个用户角色消息,设置用户提出的问题;
- Prompt 作为一个容器,将这些消息包装一下,扔给 AI 大模型;