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 都对应一个具体消息实现类,他们的继承关系如下:

更多信息参考 JavaDoc 文档:https://docs.langchain4j.dev/apidocs/dev/langchain4j/data/message/ChatMessage.html
目前,LangChain4j 提供了五种聊天消息类型,每种类型对应消息的一个“来源”,下面将娓娓道来。
这是一条来自用户的消息。用户可以是你的应用程序的终端用户(一个人),也可以是你的应用程序本身。它可以包含如下信息:
contents():消息的内容,根据大语言模型(LLM)支持的模态,它既可以只包含单一文本(String),也可以包含其他模态。
name():用户的姓名,并非所有模型提供商都支持这一功能。
attributes():附加属性,这些属性不会发送给模型,但会存储在ChatMemory中。
这是 AI 生成的一条消息,用于回应用户发送的消息。它可以包含:
text():文本内容
thinking():思考/推理内容
toolExecutionRequests():执行工具的请求,后续将单独介绍工具。
attributes():附加属性,通常是特定于提供商的。
ToolExecutionResultMessage 是 ChatMessage 中专门用于封装工具执行结果的消息类型,是实现 “模型调用外部工具(如计算器、API、数据库)” 能力的核心载体。即 ToolExecutionRequest 的结果。
这是一条来自系统的消息。通常,作为开发者的你,应该定义这条消息的内容。一般来说,你会在这里写下关于大语言模型(LLM)在此次对话中的角色、应该如何表现、以何种风格回答等方面的指令。
大语言模型在训练时就被设定为会比其他类型的消息更关注 SystemMessage 消息。所以要小心,最好不要让终端用户随意定义或向 SystemMessage 中注入某些输入。
注意:SystemMessage 消息通常位于对话的开头。
这是一条可以包含任意属性的自定义消息。这种消息类型只能被支持它的 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 作为输入,而不是只提供一个呢?这是因为大型语言模型(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) 指的是融合两种或两种以上信息载体(模态) 进行信息处理、理解或生成的技术与方法。这些信息载体可以是不同类型的输入 / 输出形式,核心是打破单一模态的局限,让系统能够像人类一样综合利用多种感官信息进行认知。
常见多模态大模型有 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;
//...
}PdfFileContent 与 ImageContent 类似,但它表示 PDF 文件的二进制内容。定义如下:
public class PdfFileContent implements Content {
private final PdfFile pdfFile;
//...
}
public class PdfFile {
private final URI url;
private final String base64Data;
//...
}👉更多 LangChain4j 知识请继续阅读后续章节……