Spring AI 1.x 系列【8】提示词、消息对象
这里直接传入了字符串,只适用于简单的文本生成任务,在稍加复杂的场景中,可以通过 Prompt、Message 构建多样的模型输入,实现各种功能。
文章目录
1. 前言
在之前的案例中,我们都是使用 ChatModel、ChatClient 和大模型进行交互:
String response = "你好";
// 使用 ChatClient 的 Fluent API 完成模型交互
String response = chatClient.prompt()
.user(userInput)
.call()
.content();
// 直接调用底层 ChatModel 的 call 方法
String text = deepSeekChatModel.call(userInput);
这里直接传入了字符串,只适用于简单的文本生成任务,在稍加复杂的场景中,可以通过 Prompt、Message 构建多样的模型输入,实现各种功能。
2. 提示词
2.1 对象创建
Prompt 提供了多个构造函数,其本质都是对消息对象列表属性进行赋值:
private final List<Message> messages;
public Prompt(String contents) {
this((Message)(new UserMessage(contents)));
}
public Prompt(Message message) {
this(Collections.singletonList(message));
}
public Prompt(List<Message> messages) {
this((List)messages, (ChatOptions)null);
}
可以使用构造函数进行创建:
Prompt prompt = new Prompt("你好");
可以使用 Prompt.builder() 基于建造者设计模式构建 Prompt 对象,相比直接通过 new Prompt(...) 构造函数创建,它支持链式调用、分步设置参数、灵活处理可选配置(如 ChatOptions、额外元数据),大幅提升代码可读性和扩展性,是构建 Prompt 的官方推荐方式。
示例 1 ,仅添加消息:
// 1. 先创建需要的 Message 对象
SystemMessage systemMsg = new SystemMessage("你是专业的Python助手,回答简洁明了");
UserMessage userMsg = new UserMessage("解释列表推导式,举1个例子");
// 2. 用 builder 链式构建 Prompt
Prompt prompt = Prompt.builder()
// 批量添加多个 Message
.messages(List.of(systemMsg, userMsg))
// 构建最终 Prompt 对象
.build();
// 验证结果
System.out.println(prompt.getMessages().size()); // 输出:2(包含system和user消息)
示例 2 ,添加消息并设置模型参数(如温度、最大 Token):
// 1. 创建 ChatOptions(模型参数)
ChatOptions options = ChatOptions.builder()
.temperature(0.1f) // 低随机性,保证精准
.maxTokens(200) // 限制回答长度
.build();
// 2. 用 Prompt.builder 组合消息 + 选项
Prompt prompt = Prompt.builder()
.messages(new UserMessage("解释列表推导式的底层原理")) // 添加用户消息
.chatOptions(options) // 绑定模型参数
.build();
// 验证结果
System.out.println(prompt.getOptions().getTemperature()); // 输出:0.1
2.2 对象方法
Prompt 也提供了一些简单方法:
get**:获取相关属性信息。augmentUserMessage:通过传入新的用户消息,生成一个经过更新 / 增强的Promptcopy:生成深拷贝副本。mutate:基于原对象属性生成可修改的临时对象。

3. 消息类型
3.1 用户消息
用户消息(User Message)就是用户的提问、指令、需求。比如,在腾讯元宝中,输入框中的文本、上传的文件都是用户消息:
使用 ChatModel 直接传入字符串时,内部都会封装为用户消息:
String text = deepSeekChatModel.call("你好");
ChatModel 源码中的 call(String) 方法:
default String call(String message) {
Prompt prompt = new Prompt(new UserMessage(message));
Generation generation = this.call(prompt).getResult();
return generation != null ? generation.getOutput().getText() : "";
}
3.1.1 对象创建
支持两种创建方式:
new:构造器直接创建。builder:Builder模式创建。
示例:
UserMessage message = new UserMessage("你好");
UserMessage userMessage = UserMessage.builder().text(" 你好 ").build();
3.1.2 属性和方法
用户消息包含四个核心属性:
String textContent:存放用户消息的纯文本内容。Resource resource:用于封装文件 / 网络资源(比如本地 PDF、远程图片、txt 文件、音频文件等)Media media:多媒体对象(专门用于图片、音频、视频等多媒体内容),用户多模态交互(图片 / 音频输入)。Map<String, Object> metadata:消息元数据,行为因模型提供商而异,有些用于请求、用户唯一标识符,有些则忽略它。一般都是业务侧的附加标记,不影响模型,仅用于系统追踪 / 处理。
传递 textContent、metadata 示例:
UserMessage userMessage = UserMessage.builder()
.metadata(Map.of("user_id", "1001", "request_id", "2026030315164748d00942f5b94de5"))
.text("你是谁?")
.build();
支持通过 get 方法获取属性信息:
String text = userMessage.getText();
Map<String, Object> metadata = userMessage.getMetadata();
MessageType messageType = userMessage.getMessageType();
System.out.println(text); // 你是谁?
System.out.println(metadata);// {messageType=USER, request_id=2026030315164748d00942f5b94de5, user_id=1001}
System.out.println(messageType); // USER
Message 对象创建后,它的 textContent、metadata、media 等属性无法直接修改,是一个不可变对象。
没有提供 set 方法:

copy() 方法会创建一个和原 UserMessage 完全一致的深拷贝副本:
// 1. 创建原 UserMessage 对象
UserMessage originalMsg = UserMessage.builder()
.text("你好")
.metadata(Map.of("userId", "1001"))
.build();
// 2. 复制出完全一致的副本
UserMessage copyMsg = originalMsg.copy();
// 3. 验证副本属性和原对象一致
System.out.println(copyMsg.getText()); // 输出:你好
System.out.println(copyMsg.getMetadata().get("userId")); // 输出:1001
// 4. 副本和原对象是不同实例(互不影响)
System.out.println(originalMsg == copyMsg); // 输出:false
mutate() 方法会基于原 UserMessage 的所有属性,创建一个 UserMessage.Builder 对象,继承原对象的所有配置,适用于增量修改部分属性的场景:
// 1. 创建原 UserMessage 对象
UserMessage originalMsg = UserMessage.builder()
.text("你好")
.metadata(Map.of("userId", "1001")) // 原对象的元数据
.build();
// 2. 基于原对象创建可修改的建造者
UserMessage.Builder mutateBuilder = originalMsg.mutate();
// 3. 增量修改:只改文本内容,保留元数据
mutateBuilder.text("你好 DeepSeek");
// 4. 构建新的 UserMessage 对象
UserMessage newMsg = mutateBuilder.build();
// 5. 验证结果:文本改了,元数据保留
System.out.println(newMsg.getText()); // 输出:你好 DeepSeek
System.out.println(newMsg.getMetadata().get("userId")); // 输出:1001
System.out.println(originalMsg == newMsg); // 输出:false
一次传递多个 UserMessage 时,会按传入顺序封装为对话上下文,模型会视为用户连续发出的完整指令,整合所有内容,最终生成一个统一的回复,而非分别回复每个 UserMessage。
代码示例:
UserMessage userMessage1 = UserMessage.builder()
.text("你是谁?")
.build();
UserMessage userMessage2 = UserMessage.builder()
.text("你能干什么?")
.build();
String content = zhiPuAiChatClient
.prompt()
.messages(userMessage1,userMessage2)
.call()
.content();
System.out.println(content);
模型输出:
3.2 系统消息
系统消息(System Message)核心目标是让模型按预设规则生成符合预期的回复,可以给模型定义前置规则,包括模型的角色、行为、回复格式,是 LLM 交互的底层上下文。它不会出现在用户可见的对话流中,但会全程约束模型的回复逻辑,确保输出符合业务预期。
SystemMessage 的核心属性、方法与 UserMessage 基本一致,主要封装系统指令的核心文本,所以没有多媒体 / 文件等相关操作。
核心行为特性:
- 优先级最高:例如,要求 “文案不超过 50 字”,但
User Message要求 “写 100 字文案”,模型会优先遵守System Message,生成50字以内的文案。 - 唯一性:若在同一次
messages数组中传入多个system消息,程序会直接报错,或只有最后一个会生效。 Token消耗规则:会占用Token额度,计入「输入Token」。- 消息列表中不能只包含系统消息或助手消息。
核心适用场景:
- 角色定义:指定模型的身份 / 定位,让模型有明确的人设。
- 行为约束:规定模型的回复风格、语气、长度、边界。
- 格式规范:强制模型按固定格式输出,方便前端 / 程序解析。
- 禁止行为:规避违规 / 无关回复。
- 上下文预设:为对话提供背景信息。
示例,一个短视频广告文案助手系统提示词:
你是短视频广告文案助手,擅长写:短视频标题、开头钩子、口播文案、片尾引导。
1. 开头3秒抓眼球,突出痛点/利益/反差。
2. 语言简短、节奏感强、适合口语朗读。
3. 结尾自然引导:关注、下单、咨询、参与活动。
4. 文案接地气、有传播力,不虚假、不违规。
3.2.1 全局默认
给 ChatClient 实例设置全局默认的系统消息,该实例的所有调用都会自动携带这套规则,无需重复配置。
配置类示例:
@Bean("zhiPuAiChatClient")
public ChatClient zhiPuAiChatClient(ZhiPuAiChatModel zhiPuAiChatModel) {
return ChatClient.builder(zhiPuAiChatModel)
// 核心:全局默认 System Message
.defaultSystem("""
你是资深电商营销文案助手,专注无线耳机的朋友圈文案创作。
规则:
1. 回复风格口语化、有网感,适合朋友圈传播;
2. 每条文案不超过80字,突出续航/降噪卖点;
3. 输出3条文案,分点列出。
""")
.build();
}
模型调用:
UserMessage userMessage = UserMessage.builder()
.text("你是谁?")
.build();
String content = zhiPuAiChatClient
.prompt()
.messages(userMessage)
.call()
.content();
System.out.println(content);
模型输出:
3.2.2 单次请求覆盖
若某一次请求需要临时改变模型规则,可通过 prompt().system() 覆盖全局默认的 System Message,仅对本次请求生效。
模型调用:
String content = zhiPuAiChatClient
.prompt()
.system("""
你是品牌营销顾问,回复风格正式、高级,输出1条品牌宣传语,不超过30字。
""")
.messages(userMessage)
.call()
.content();
System.out.println(content);
模型输出:
3.3 助手消息
大语言模型生成的响应内容(包括纯文本回复、工具调用、结构化输出等),都会被封装成 AssistantMessage 对象,它是多轮对话上下文的核心组成部分。
不同的模型厂商返回时可能会有少许差异,所以有不同实现:
3.3.1 接收模型回复
在调用模型后,可以使用 AssistantMessage 对象接受响应数据:
ChatClientResponse chatClientResponse = zhiPuAiChatClient
.prompt()
.user("你是?")
.call()
.chatClientResponse();
AssistantMessage assistantMessage = chatClientResponse.chatResponse().getResult().getOutput();
当模型需要工具调用时,还会返回需要的工具列表:
if (assistantMessage.hasToolCalls()) {
List<AssistantMessage.ToolCall> toolCalls = assistantMessage.getToolCalls();
for (AssistantMessage.ToolCall toolCall : toolCalls) {
System.out.println("Tool: " + toolCall.name()); // 工具名
System.out.println("Args: " + toolCall.arguments()); // 工具参数
System.out.println("ID: " + toolCall.id()); // 工具调用唯一 ID
}
}
3.3.2 多轮对话上下文构建
模型是无状态的,若要让模型 “记住” 上一轮的回复,必须传递历史 AssistantMessage 才能保证对话连贯:
// 第一轮对话:UserMessage → AssistantMessage
UserMessage userMsg1 = new UserMessage("为无线耳机写朋友圈文案");
AssistantMessage assistantMsg1 = chatClient.prompt().message(userMsg1).call().getResult().getOutput();
// 第二轮对话:传递历史 UserMessage + AssistantMessage + 新 UserMessage
UserMessage userMsg2 = new UserMessage("把刚才的文案改成更活泼的风格");
String resp2 = chatClient.prompt()
.messages(List.of(userMsg1, assistantMsg1, userMsg2)) // 完整上下文
.call()
.content();
// 模型能理解“刚才的文案”指第一轮的回复,因为上下文包含 assistantMsg1
3.4 工具响应消息
模型需要调用工具时会返回 AssistantMessage.toolCalls(工具调用指令),业务端在执行完工具后,需要使用 ToolResponseMessage 将结果回传给模型。
提示:相关使用方法会在工具篇进行介绍。
更多推荐

所有评论(0)