1. 前言

在之前的案例中,我们都是使用 ChatModelChatClient 和大模型进行交互:

String response = "你好";
// 使用 ChatClient 的 Fluent API 完成模型交互
String response = chatClient.prompt()
        .user(userInput)
        .call()
        .content();

// 直接调用底层 ChatModel 的 call 方法
String text = deepSeekChatModel.call(userInput);

这里直接传入了字符串,只适用于简单的文本生成任务,在稍加复杂的场景中,可以通过 PromptMessage 构建多样的模型输入,实现各种功能。

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:通过传入新的用户消息,生成一个经过更新 / 增强的 Prompt
  • copy:生成深拷贝副本。
  • 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:构造器直接创建。
  • builderBuilder 模式创建。

示例:

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:消息元数据,行为因模型提供商而异,有些用于请求、用户唯一标识符,有些则忽略它。一般都是业务侧的附加标记,不影响模型,仅用于系统追踪 / 处理。

传递 textContentmetadata 示例:

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 对象创建后,它的 textContentmetadatamedia 等属性无法直接修改,是一个不可变对象。

没有提供 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 将结果回传给模型。

提示:相关使用方法会在工具篇进行介绍。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐