LangChain4j 的 ChatMessage 接口

ChatMessage 是 langchain4j 中所有聊天消息的最顶层接口,该接口定义仅包含一个 type() 抽象方法,定义如下:

package dev.langchain4j.data.message;

public interface ChatMessage {
    ChatMessageType type();
}

注意,type() 方法返回的 MessageType 是 langchain4j 定义的枚举,定义如下:

public enum ChatMessageType {
    // 系统消息
    SYSTEM(SystemMessage.class),
    // 用户消息
    USER(UserMessage.class),
    // AI回复消息
    AI(AiMessage.class),
    // 工具执行结果
    TOOL_EXECUTION_RESULT(ToolExecutionResultMessage.class),
    // 自定义消息
    CUSTOM(CustomMessage.class);
    //...
}

注意到了吗,每一种 ChatMessageType 都对应一个具体消息实现类,他们的继承关系如下:

image.png

更多信息参考 JavaDoc 文档:https://docs.langchain4j.dev/apidocs/dev/langchain4j/data/message/ChatMessage.html

  

ChatMessage 的类型

目前,LangChain4j 提供了五种聊天消息类型,每种类型对应消息的一个“来源”,下面将娓娓道来。

UserMessage

这是一条来自用户的消息。用户可以是你的应用程序的终端用户(一个人),也可以是你的应用程序本身。它可以包含如下信息:

  • contents():消息的内容,根据大语言模型(LLM)支持的模态,它既可以只包含单一文本(String),也可以包含其他模态。

  • name():用户的姓名,并非所有模型提供商都支持这一功能。

  • attributes():附加属性,这些属性不会发送给模型,但会存储在ChatMemory中。

AiMessage

这是 AI 生成的一条消息,用于回应用户发送的消息。它可以包含:

  • text():文本内容

  • thinking():思考/推理内容

  • toolExecutionRequests():执行工具的请求,后续将单独介绍工具。

  • attributes():附加属性,通常是特定于提供商的。

ToolExecutionResultMessage

ToolExecutionResultMessage 是 ChatMessage 中专门用于封装工具执行结果的消息类型,是实现 “模型调用外部工具(如计算器、API、数据库)” 能力的核心载体。即 ToolExecutionRequest 的结果。

SystemMessage

这是一条来自系统的消息。通常,作为开发者的你,应该定义这条消息的内容。一般来说,你会在这里写下关于大语言模型(LLM)在此次对话中的角色、应该如何表现、以何种风格回答等方面的指令。

大语言模型在训练时就被设定为会比其他类型的消息更关注 SystemMessage 消息。所以要小心,最好不要让终端用户随意定义或向 SystemMessage 中注入某些输入。

注意:SystemMessage 消息通常位于对话的开头。

CustomMessage

这是一条可以包含任意属性的自定义消息。这种消息类型只能被支持它的 ChatModel 实现使用(目前只有 Ollama 支持)。

上面我们已经了解了所有类型的 ChatMessage,下面就让我们看看如何在对话中把它们组合起来。

在最简单的场景中,我们可以向聊天方法提供一个用户消息(UserMessage)实例,例如:

// 创建 ChatModel 实现类(OpenAI 为例)
ChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://api.xty.app/v1")
        .apiKey(API_KEY)
        .modelName("gpt-3.5-turbo")
        .temperature(0.7)
        .logRequests(true)
        .logResponses(true)
        .build();
// 构建多轮对话消息
ChatMessage userMsg1 = UserMessage.from("什么是ChatModel?");
// 调用chat方法(可变参数)
ChatResponse response = chatModel.chat(userMsg1);
System.out.println("回复内容:" + response.aiMessage().text());

或者传递一个字符串(String)作为输入。例如:

// 创建 ChatModel 实现类(OpenAI 为例)
ChatModel chatModel = OpenAiChatModel.builder()
        .baseUrl("https://api.xty.app/v1")
        .apiKey(API_KEY)
        .modelName("gpt-3.5-turbo")
        .temperature(0.7)
        .logRequests(true)
        .logResponses(true)
        .build();
// 调用chat方法
String response = chatModel.chat("什么是ChatModel?");
System.out.println("回复内容:" + response);

注意,传递 UserMessage 和字符串的主要区别是,chat() 返回的不是字符串(String),而是聊天响应(ChatResponse)。

除了 AI 消息(AiMessage)之外,聊天响应(ChatResponse)还包含聊天响应元数据(ChatResponseMetadata)。

创建 UserMessage 有多种方法,具体取决于内容。最简单的是

 new UserMessage("Hi")

或者

UserMessage.from("Hi")

  

多个 ChatMessage

为什么你需要提供多个 ChatMessage 作为输入,而不是只提供一个呢?这是因为大型语言模型(LLMs)本质上是无状态的,这意味着它们不会保留对话状态。所以,如果你想支持多轮对话,就需要负责管理对话状态。

假设你想构建一个聊天机器人。想象一下用户和聊天机器人(AI)之间简单的多轮对话,如下:

  • 用户:你好,我叫克劳斯

  • AI:嗨,克劳斯,我能帮你做什么?

  • 用户:我叫什么名字?

  • AI:克劳斯

这就是与 ChatModel 的交互方式:

// 第一条用户消息
UserMessage firstUserMessage = UserMessage.from("你好,我叫克劳斯");
// AI回答
AiMessage firstAiMessage = model.chat(firstUserMessage).aiMessage(); // 嗨,克劳斯,我能帮你做什么?
// 第二条用户消息
UserMessage secondUserMessage = UserMessage.from("我叫什么名字?");
// 将前面所有消息全都输入给AI
AiMessage secondAiMessage = model.chat(firstUserMessage, firstAiMessage, secondUserMessage).aiMessage(); // 克劳斯

如你所见,在 chat() 方法的第二次调用中,我们提供的不仅仅是一条单独的 secondUserMessage,还有对话中的先前消息。手动维护和管理这些消息很繁琐。因此,ChatMemory 这一概念应运而生,我们将在后续章节中对其进行探讨。

  

多模态(Multimodality)

多模态(Multimodality) 指的是融合两种或两种以上信息载体(模态) 进行信息处理、理解或生成的技术与方法。这些信息载体可以是不同类型的输入 / 输出形式,核心是打破单一模态的局限,让系统能够像人类一样综合利用多种感官信息进行认知。

常见多模态大模型有 GPT-4V、Claude 3 等,支持同时输入文本、图片、音频,实现跨模态的问答和生成。

例如:融合语音识别(听觉→语言)、图像识别(视觉)、自然语言理解,实现 “语音指令 + 图像输入” 的交互。

在 LangChain4j 中,UserMessage 不仅可以包含文本,还可以包含其他类型的内容。UserMessage 消息包含一个 List<Content> contents,代码定义如下:

public class UserMessage implements ChatMessage {
    private final String name;
    private final List<Content> contents; // 看这个
    private final Map<String, Object> attributes;
    //
}

其中,Content 是一个接口,具有以下实现:

  • TextContent  文本内容

  • ImageContent  图片内容

  • AudioContent  音频内容

  • VideoContent  视频内容

  • PdfFileContent  PDF 文件内容

注意:你可以访问 https://docs.langchain4j.dev/integrations/language-models 地址,查看对比表中哪些大语言模型(LLM)提供商支持哪些模态。

以下是向大语言模型同时发送文本和图像的示例:

UserMessage userMessage = UserMessage.from(
    TextContent.from("Describe the following image"), // 文本消息
    ImageContent.from("https://example.com/cat.jpg")  // 图片消息
);
ChatResponse response = model.chat(userMessage);

文本内容

TextContent 是 Content 的最简单形式,它表示纯文本并封装了一个 String。UserMessage.from(TextContent.from("Hello!")) 等价于UserMessage.from("Hello!")。

可以在 UserMessage 中提供一个或多个 TextContent,例如:

UserMessage userMessage = UserMessage.from(
    TextContent.from("Hello!"),
    TextContent.from("How are you?")
);

图像内容

根据大型语言模型(LLM)提供商的不同,ImageContent 既可以通过远程图像的 URL 创建(参见上面的示例),也可以通过 Base64 编码的二进制数据创建,例如:

// 获取图片二进制数据
byte[] imageBytes = readBytes("/home/me/cat.jpg");
// 将图像数据转换为Base64
String base64Data = Base64.getEncoder().encodeToString(imageBytes);
// 使用 Base64 构建 ImageContent
ImageContent imageContent = ImageContent.from(base64Data, "image/jpg");
UserMessage userMessage = UserMessage.from(imageContent);

注意:你还可以指定 DetailLevel 枚举(包含 LOW / HIGH / AUTO 选项)来控制模型处理图像的方式。更多详细信息请查看 https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding

音频内容

AudioContent 与 ImageContent 类似,但表示音频内容。定义如下:

public class AudioContent implements Content {
    private final Audio audio;
    //...
}

public class Audio {
    private final URI url;
    private final String base64Data;
    private final String mimeType;
    //...
}

视频内容

VideoContent 与 ImageContent 类似,但表示视频内容。定义如下:

public class VideoContent implements Content {
    private final Video video;
    //...
}

public class Video {
    private final URI url;
    private final String base64Data;
    private final String mimeType;
    //...
}

PDF 文件内容

PdfFileContent 与 ImageContent 类似,但它表示 PDF 文件的二进制内容。定义如下:

public class PdfFileContent implements Content {
    private final PdfFile pdfFile;
    //...
}

public class PdfFile {
    private final URI url;
    private final String base64Data;
    //...
}

👉更多 LangChain4j 知识请继续阅读后续章节……

 

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号