课程目标
掌握 LangChain.js 的消息数据模型:BaseMessage 继承体系、多模态内容、流式消息分片。
9.1 为什么需要统一消息模型?
不同 LLM Provider 的消息格式差异很大:
- OpenAI 用
{ role: "user", content: "..." } - Anthropic 用
{ role: "user", content: [{ type: "text", text: "..." }] } - Google 用完全不同的结构
LangChain.js 用 BaseMessage 体系统一它们,Provider 层负责格式转换。
9.2 消息类型继承体系
Serializable
└── BaseMessage
├── HumanMessage // 用户消息 (type: "human")
├── AIMessage // AI 回复 (type: "ai")
├── SystemMessage // 系统指令 (type: "system")
├── ToolMessage // 工具返回值 (type: "tool")
├── FunctionMessage // (已弃用,兼容旧版)
├── ChatMessage // 自定义 role
└── RemoveMessage // 标记删除消息
9.2.1 BaseMessage 核心字段
源码位置: libs/langchain-core/src/messages/base.ts
export type MessageContent = string | Array<ContentBlock>;
export interface BaseMessageFields {
content?: MessageContent; // 消息内容(文本或多模态块数组)
contentBlocks?: Array<ContentBlock>; // 标准化内容块
id?: string; // 消息 ID
name?: string; // 发送者名称
additional_kwargs?: Record<string, any>; // 额外参数
response_metadata?: Record<string, any>; // 响应元数据
}
content 的两种形态:
- 纯文本:
string— 最常见的情况 - 多模态块数组:
ContentBlock[]— 包含文本、图片、文件等混合内容
9.2.2 各消息类型详解
| 类型 | type 字段 | 用途 | 典型内容 |
|---|---|---|---|
HumanMessage | "human" | 用户输入 | 文本、图片 URL |
AIMessage | "ai" | 模型回复 | 文本、tool_calls |
SystemMessage | "system" | 系统指令 | 角色设定、规则 |
ToolMessage | "tool" | 工具执行结果 | 返回值文本 |
ChatMessage | 自定义 | 自定义角色 | 任意 |
9.3 AIMessage — 最复杂的消息类型
源码位置: libs/langchain-core/src/messages/ai.ts
export class AIMessage extends BaseMessage {
readonly type = "ai";
tool_calls?: ToolCall[] = []; // 结构化工具调用
invalid_tool_calls?: InvalidToolCall[]; // 解析失败的工具调用
usage_metadata?: UsageMetadata; // token 使用量
}
9.3.1 tool_calls
当 LLM 决定调用工具时,AIMessage.tool_calls 包含结构化的调用信息:
interface ToolCall {
name: string; // 工具名称
args: Record<string, any>; // 参数(已解析的 JSON)
id?: string; // 调用 ID(用于匹配 ToolMessage)
type: "tool_call";
}
示例:
const aiMsg = new AIMessage({
content: "",
tool_calls: [
{
name: "get_weather",
args: { city: "Beijing", unit: "celsius" },
id: "call_123",
type: "tool_call",
},
],
});
9.3.2 contentBlocks
contentBlocks 提供标准化的内容块表示,懒解析 Provider 特定格式:
// Provider 原始格式可能不同(Anthropic 风格、OpenAI 风格等)
// contentBlocks 将它们统一为标准 ContentBlock 格式
const blocks = aiMessage.contentBlocks;
// [
// { type: "text", text: "Based on..." },
// { type: "tool_use", name: "get_weather", input: {...} },
// ]
9.3.3 usage_metadata
interface UsageMetadata {
input_tokens: number; // 输入 token 数
output_tokens: number; // 输出 token 数
total_tokens: number; // 总 token 数
}
9.4 ToolMessage — 工具结果
import { ToolMessage } from "@langchain/core/messages";
const toolResult = new ToolMessage({
content: "Beijing: 25°C, Sunny",
tool_call_id: "call_123", // 匹配 AIMessage.tool_calls[].id
name: "get_weather",
});
tool_call_id 是关键——它将 ToolMessage 和触发它的 AIMessage.tool_call 关联起来。
9.5 多模态内容
9.5.1 ContentBlock 类型
type ContentBlock =
| { type: "text"; text: string } // 文本
| { type: "image_url"; image_url: { url: string } } // 图片 URL
| { type: "input_audio"; input_audio: { data: string } } // 音频
| { type: "file"; file: { url: string } } // 文件
// ... 更多类型
9.5.2 发送图片消息
const imageMsg = new HumanMessage({
content: [
{ type: "text", text: "What's in this image?" },
{
type: "image_url",
image_url: { url: "https://example.com/cat.jpg" },
},
],
});
9.5.3 Base64 图片
const base64Msg = new HumanMessage({
content: [
{ type: "text", text: "Describe this" },
{
type: "image_url",
image_url: { url: `data:image/png;base64,${base64Data}` },
},
],
});
9.6 AIMessageChunk — 流式消息
流式输出时,模型不是一次返回完整的 AIMessage,而是逐步返回 AIMessageChunk:
class AIMessageChunk extends BaseMessageChunk {
type = "AIMessageChunk";
tool_call_chunks?: ToolCallChunk[]; // 工具调用的流式分片
}
chunk 合并:多个 chunk 可以用 concat() 合并:
import { concat } from "@langchain/core/utils/stream";
let fullMessage: AIMessageChunk | undefined;
for await (const chunk of model.stream("Hello")) {
fullMessage = fullMessage ? concat(fullMessage, chunk) : chunk;
console.log(chunk.content); // 逐 token 输出
}
// fullMessage 现在包含完整的消息
tool_call_chunks 的合并:
// 流式中工具调用也是分片到达:
// chunk1: { tool_call_chunks: [{ name: "get", args: '{"ci' }] }
// chunk2: { tool_call_chunks: [{ args: 'ty":"Be' }] }
// chunk3: { tool_call_chunks: [{ args: 'ijing"}' }] }
// 合并后: tool_calls: [{ name: "get_weather", args: {city: "Beijing"} }]
9.7 消息创建的便捷方式
// 方式 1: 构造函数
const msg = new HumanMessage("Hello");
// 方式 2: 字符串(纯文本)
const msg = new HumanMessage("Hello");
// 方式 3: 元组形式(在 ChatPromptTemplate 中使用)
["human", "Hello {name}"] // → HumanMessage
["ai", "I am an assistant"] // → AIMessage
["system", "You are helpful"] // → SystemMessage
// 方式 4: BaseMessageLike 类型
type BaseMessageLike =
| BaseMessage
| [type: string, content: MessageContent] // 元组
| string; // 纯文本 → HumanMessage
9.8 消息序列化与反序列化
消息可以序列化为 JSON 存储,也可以从 JSON 恢复:
// 序列化
const stored: StoredMessage = {
type: "human",
data: {
content: "Hello",
role: "user",
name: undefined,
tool_call_id: undefined,
},
};
// 反序列化
import { mapStoredMessageToChatMessage } from "@langchain/core/messages";
const restored = mapStoredMessageToChatMessage(stored);
9.9 实战练习
练习 1:构造完整对话
import {
HumanMessage, AIMessage, SystemMessage, ToolMessage
} from "@langchain/core/messages";
const conversation = [
new SystemMessage("You are a helpful assistant with access to weather tools."),
new HumanMessage("What's the weather in Beijing?"),
new AIMessage({
content: "",
tool_calls: [{
name: "get_weather",
args: { city: "Beijing" },
id: "call_001",
type: "tool_call",
}],
}),
new ToolMessage({
content: "Beijing: 25°C, Sunny",
tool_call_id: "call_001",
name: "get_weather",
}),
new AIMessage("The weather in Beijing is 25°C and sunny!"),
];
练习 2:多模态消息
const multimodalMsg = new HumanMessage({
content: [
{ type: "text", text: "Compare these two images:" },
{ type: "image_url", image_url: { url: "https://example.com/img1.jpg" } },
{ type: "image_url", image_url: { url: "https://example.com/img2.jpg" } },
],
});
9.10 源码精读路线
| 优先级 | 文件 | 关注点 |
|---|---|---|
| P0 | messages/base.ts | BaseMessage, MessageContent, BaseMessageChunk |
| P0 | messages/ai.ts | AIMessage, tool_calls, AIMessageChunk |
| P1 | messages/human.ts | HumanMessage |
| P1 | messages/system.ts | SystemMessage |
| P1 | messages/tool.ts | ToolMessage, ToolCall, ToolCallChunk |
| P2 | messages/content/ | ContentBlock 类型定义 |
| P2 | messages/block_translators/ | Provider 格式转换器 |
本课收获总结
| 级别 | 你应该掌握的 |
|---|---|
| 🟢 基础 | 掌握五种消息类型及其用途;能构造包含工具调用的完整对话 |
| 🔵 中阶 | 理解 AIMessage.tool_calls 的结构和 ToolMessage 的 tool_call_id 匹配机制 |
| 🟡 高阶 | 掌握多模态内容的 ContentBlock 表示;理解 contentBlocks 的懒解析 |
| 🟠 资深 | 分析 AIMessageChunk 的流式合并策略,特别是 tool_call_chunks 的合并 |
| 🔴 架构 | 评估消息模型的扩展性:如何支持新的内容类型和 Provider 格式 |
下一课预告
第 10 课讲对话历史与消息管理——BaseChatMessageHistory 和 RunnableWithMessageHistory,解决多轮对话的上下文管理问题。