在 LangChain 中,消息(Messages)是构建聊天模型输入和输出的核心单元。LangChain 将不同角色的对话内容标准化为特定的类,以便于在不同的 LLM 供应商(如 OpenAI、Anthropic、Claude)之间切换。
1. Message消息类型与继承关系
所有的消息类都继承自 BaseMessage 基类。这个基类定义了所有消息共有的属性:content(内容)、name(可选,发送者名称)和 additional_kwargs(特定供应商的额外参数)。
继承关系图解:
| 类名 | 继承自 | 代表角色 | 说明 |
|---|---|---|---|
SystemMessage | BaseMessage | System | 设置 AI 的行为、性格或约束条件,通常放在对话首位。 |
HumanMessage | BaseMessage | User | 代表用户的输入,可以是文本、图片或其他多模态数据。 |
AIMessage | BaseMessage | Assistant | AI 生成的响应。包含文本内容、工具调用(tool_calls)等。 |
ToolMessage | BaseMessage | Tool | 传递工具执行结果回模型的消息。必须包含 tool_call_id。 |
FunctionMessage | BaseMessage | Function | 旧版函数调用结果(已逐渐被 ToolMessage取代)。 |
ChatMessage | BaseMessage | 自定义 | 允许手动指定任意 role字符串的通用消息类。 |
2. 各消息类型详细介绍与用法
SystemMessage (系统消息)
用于“洗脑”或初始化。它告诉模型:“你是一个专业的翻译官”或“你说话必须带海盗口音”。
- 用法:
SystemMessage(content="你是一个金融分析师,只回答财经问题。")
HumanMessage (人类消息)
用户的指令或提问。
- 用法:
HumanMessage(content="今天的黄金价格是多少?") - 多模态支持:它也可以接收列表形式的 content,用于传递图片:
HumanMessage(content=[{"type": "text", "text": "..."}, {"type": "image_url", ...}])
AIMessage (AI 消息)
模型返回的对象。它除了 content,最重要的属性是 tool_calls。当模型决定调用工具时,content 可能为空,而 tool_calls 会包含具体的函数名和参数。
- 用法:通常由模型
model.invoke()产生,但也可以手动构建:AIMessage(content="好的,我为您查询一下。", tool_calls=[...])
ToolMessage (工具消息)
这是闭环的关键。模型发出 tool_calls 后,你运行本地代码得到结果,必须用 ToolMessage 把结果传回。
- 用法:
ToolMessage(content="250元/克", tool_call_id="call_abc123") - 核心要求:必须匹配 AI 消息中对应的
id。
3. 代码示例:一个完整的对话流
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage, AnyMessage
messages = [
SystemMessage(content="你是一个智能助手。"),
HumanMessage(content="查询北京天气"),
# 模拟模型返回结果,决定调用工具
AIMessage(content="", tool_calls=[{"name": "get_weather", "args": {"city": "北京"}, "id": "123"}]),
# 模拟工具返回结果
ToolMessage(content="晴,25度", tool_call_id="123"),
# 模型根据工具结果给出最终回答
AIMessage(content="北京今天天气晴朗,气温 25 度。")
]
# AnyMessage 涵盖了这两种具体类型
def process_chat(msg: AnyMessage):
print(msg.content) # BaseMessage 共有属性
if isinstance(msg, AIMessage):
# 只有标注为 AnyMessage 或 AIMessage,IDE 才会在这里提示 .tool_calls
print(msg.tool_calls)
# 它可以接收任何子类
process_chat(HumanMessage(content="你好"))
process_chat(AIMessage(content="我好"))
4. AnyMessage
AnyMessage 并不是一个普通的 类(Class) ,而是一个 类型别名(Type Alias) 。
简单来说,AnyMessage 就是所有具体消息类型的 合集(Union) 。
4.1. 继承与包含关系
在 Python 的 typing 体系中,它们的逻辑关系如下:
BaseMessage是所有消息类的 祖先基类。HumanMessage,AIMessage,SystemMessage,ToolMessage等都 继承(Inherit) 自BaseMessage。
AnyMessage 是一个 Union 类型,python中的定义为:
AnyMessage = Annotated[ Annotated[AIMessage, Tag(tag="ai")]
| Annotated[HumanMessage, Tag(tag="human")]
| Annotated[ChatMessage, Tag(tag="chat")]
| Annotated[SystemMessage, Tag(tag="system")]
| Annotated[FunctionMessage, Tag(tag="function")]
| Annotated[ToolMessage, Tag(tag="tool")]
| Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")]
| Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")]
| Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")]
| Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")]
| Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")]
| Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
Field(discriminator=Discriminator(_get_type)),
]
"""A type representing any defined `Message` or `MessageChunk` type."""
这里边还有MessageChunk类型,就不详细介绍了,有兴趣可以问下大模型
4.2. 为什么要用 AnyMessage?
在 LangGraph 或复杂的 Chain 中,我们经常需要处理“一个包含各种消息的列表”。
- 如果标注为
list[BaseMessage]:类型检查器只知道里面是基础消息,当你访问AIMessage特有的tool_calls属性时,IDE 可能会报警告,因为它不确定BaseMessage是否有这个属性。 - 如果标注为
list[AnyMessage]:这是 类型安全 的做法。它告诉 IDE 和编译器:“这个列表里的对象一定是这几种具体消息类型之一”。这样当你进行类型判断(如if isinstance(msg, AIMessage))时,IDE 能提供精准的代码补全。
4.3. 在代码中的表现
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
# AnyMessage 涵盖了这两种具体类型
def process_chat(msg: AnyMessage):
print(msg.content) # BaseMessage 共有属性
if isinstance(msg, AIMessage):
# 只有标注为 AnyMessage 或 AIMessage,IDE 才会在这里提示 .tool_calls
print(msg.tool_calls)
# 它可以接收任何子类
process_chat(HumanMessage(content="你好"))
process_chat(AIMessage(content="我好"))