Message类型

同一段「用户问天气」可以是 str{"role":"user"}HumanMessage。混用到工具轮次时,漏 tool_call_id 或把流式 AIMessageChunk 当完整 AIMessageinvoke,厂商 API 直接 400。LangChain 用 langchain_core.messages 把角色收成带 type 判别器的对象;下游(ChatModel、Parser、工具循环、图状态)按 而不是按字符串猜测。

段末注释BaseMessage = 对话单元基类;MessageChunk = 流式增量,可用 + 合并成完整消息。

社区方案:只用官方 langchain_core.messages 类型(Messages 概念)。不要自造 role dict 长期跑生产。风险:FunctionMessage / ChatMessage 仍可 import,但新工具协议与多数 partner 包已不保证。

图 1 四类对话信封进 ChatModel;Chunk 需拼合;Remove 走 reducer 不进模型(对应 §2~§3.4)


1. 一句话定位

维度 内容
角色 能力层 对话类型系统:角色、字段、序列化判别器
输入 → 输出 构造 BaseMessage 子类 → ChatModel.invokelist[BaseMessage],吐 AIMessage
典型调用入口 HumanMessage / AIMessage / ToolMessagestreamAIMessageChunk
与 LangGraph 图 state 的 messages 仍是这些类;RemoveMessage 只给 reducer,不给模型

2. 实现逻辑

一次调用里类型怎么分流:

1
2
3
4
5
1. 应用组装 list[BaseMessage](或 str → 内部包成 HumanMessage)
2. ChatModel 按 type 映射厂商 role:system / user / assistant / tool
3. HTTP 响应 → 解析为 AIMessage(完整)或 AIMessageChunk(stream)
4. 若 tool_calls 非空:执行工具 → ToolMessage → 再次 invoke
5. Parser 读 AIMessage.content;LangGraph add_messages 才认 RemoveMessage

字段级变形

1
2
3
4
5
str "北京天气?"     → HumanMessage(type="human")
model.invoke(...) → AIMessage(type="ai", tool_calls=[...])
工具返回 "晴" → ToolMessage(type="tool", tool_call_id=call.id)
model.stream(...) → AIMessageChunk + AIMessageChunk → 完整 AIMessage
RemoveMessage(id=x) → reducer 删 id=x 的那条;不会变成 role=remove 发给模型

3. 原理说明

3.1 类型总表

AnyMessage 是带 type 判别器的联合类型,反序列化靠它。运行时你真正 new 的是下表各类。

type 进 ChatModel.invoke 谁产出 新代码是否用
SystemMessage system 是,通常首位 应用
HumanMessage human 是;invoke(str) 的落点 用户/应用
AIMessage ai 是(作为历史) 模型,或手写历史
ToolMessage tool 是,须紧跟对应 AI(tool_calls) 应用执行工具后
FunctionMessage function 旧 function calling 旧工具循环 ,改 ToolMessage
ChatMessage chat 视集成;需自带 role 应用 仅非标准角色
AIMessageChunk AIMessageChunk 应先 + 合并 stream / astream 流式 UI
RemoveMessage remove 应用 仅图状态删除

BaseMessage(抽象数据类)
功能:所有消息的公共载体。
默认值:additional_kwargs={}response_metadata={}name=Noneid=None
最小维度:子类必须带 contentRemoveMessage 强制 "")。

字段 类型 默认值 最小维度
content str / list 必填 文本 ≥0 字符;多模态为 list len≥1
type str 子类 Literal 序列化判别器,勿手改
name str / None None 可选;Human 标识用户、Tool 标识工具
id str / None None Remove 必填;其余建议有
additional_kwargs dict {} 厂商扩展,业务少写
response_metadata dict {} 模型名、logprobs 等
1
2
from langchain_core.messages import HumanMessage
HumanMessage(content="你好") # type 自动为 "human"

3.2 对话角色类(数据差)

SystemMessage(数据类)
功能:系统指令,给模型定角色与约束。
数据结构:公共字段 + type="system"。无 tool_calls
最小维度:content ≥1 字符才有语义。多数厂商允许 0~1 条;多条时有的合并、有的只认第一条。

1
2
from langchain_core.messages import SystemMessage
SystemMessage(content="只答事实,一句话。")

HumanMessage(数据类)
功能:用户输入,可含多模态 content 块。
最小维度:一次提问至少 1 条;invoke("hi") 等价 invoke([HumanMessage("hi")])
name 用于区分多用户,厂商可能忽略。

1
2
3
from langchain_core.messages import HumanMessage
HumanMessage(content="北京天气?", name="alice", id="u1")
# 多模态:content=[{"type":"text","text":"..."}, {"type":"image_url","image_url":{"url":"..."}}]

AIMessage(数据类)
功能:模型输出(也可手写进历史当「模型说过」)。唯一tool_calls / usage_metadata 的常用类型。

字段 类型 默认值 最小维度
content str / list 必填 纯工具轮次可为 "" / []
tool_calls list [] 0=纯文本;≥1 必须回填 ToolMessage
invalid_tool_calls list [] schema 解析失败时非空
usage_metadata dict / None None 有则含 input/output/total_tokens
1
2
from langchain_core.messages import AIMessage
AIMessage(content="4", usage_metadata={"input_tokens": 8, "output_tokens": 1, "total_tokens": 9})

ToolMessage(数据类)
功能:把一次工具执行结果回填给模型。type="tool"

字段 类型 默认值 最小维度
content str 必填 建议非空;失败写错误文本
tool_call_id str 必填 非空,等于对应 tool_calls[].id
name str / None None 建议填工具名
artifact Any / None None 不发给模型,给应用下游(如 doc_id)
status str,视版本 成功态 "error" 时仍要带 id
1
2
from langchain_core.messages import ToolMessage
ToolMessage(content="晴,18°C", tool_call_id="call_123", name="get_weather", artifact={"src": "mock"})

FunctionMessage(数据类,legacy)
功能:旧 OpenAI function callingrole=function)。有 name没有 tool_call_id
最小维度:content + name 均必填。新代码一律 ToolMessage

1
2
from langchain_core.messages import FunctionMessage
FunctionMessage(content="晴", name="get_weather") # 无 tool_call_id,新 API 会拒

ChatMessage(数据类)
功能:任意 role: str 的通用消息。type="chat"
最小维度:role 非空 + content。多数 ChatOpenAI / Anthropic 集成只认标准四角色,自定义 role 可能被丢或 400。

1
2
from langchain_core.messages import ChatMessage
ChatMessage(role="user", content="你好") # 能跑,但应改用 HumanMessage

3.3 Chunk 与 RemoveMessage

AIMessageChunk / HumanMessageChunk / SystemMessageChunk / ToolMessageChunk / ChatMessageChunk / FunctionMessageChunk(数据类)
功能:对应完整类型的流式切片。字段与父类同形,content 常为短 str。
默认值:空 content="" 的心跳 chunk 可能出现。
最小维度:一次 stream 至少 1 个 chunk。合并:full = c1 if full is None else full + c

1
2
3
4
full = None
for c in model.stream([HumanMessage("只答一个字:北京")]):
full = c if full is None else full + c
# type(full).__name__ 预期含 AIMessage(合并后)

RemoveMessage(数据类)
功能:声明「删掉 id 等于某值的消息」。type="remove"
数据结构:id: str 必填content 强制 "",传入非空会 ValueError
最小维度:仅 id。下游是 LangGraph add_messages reducer,不是 Chat Completions。

1
2
from langchain_core.messages import RemoveMessage
RemoveMessage(id="msg_1")

3.4 下游调用区别

同一 list 里类型不同,下游走不同分支。

下游 认哪些类型 数据怎么用 用错会怎样
ChatModel.invoke System / Human / AI / Tool(+ 少数 Chat) 映射为厂商 role;Anthropic 常把 System 抽到 顶层 system=,messages 里只留 user/assistant/tool Tool 缺 id → 400;Remove/未合并 Chunk → 非法 payload
ChatModel.invoke(str) 无显式类型 内部 包成 1 条 HumanMessage,没有 System 需要系统指令时必须走 list
bind_tools 后再 invoke 同上 + 返回 AIMessage.tool_calls 有 tool_calls 时下游应执行工具,不要当最终答案 把 AI 当终态会丢掉工具
model.stream / astream 产出 AIMessageChunk 逐块 content;应用层 + 合并 把单个 Chunk 当完整 AI 再回填会丢 tool_calls 后半段
StrOutputParser AIMessage / Chunk content;list block 则拼文本 喂 ToolMessage 得到工具原文,不是模型总结
JsonOutputParser AIMessage(完整 content) json.loads(content) Chunk 半包 JSON 必炸
工具循环 / ToolNode 读最后一条 AIMessage.tool_calls,写出 ToolMessage 一条 tool_call 对应一条 ToolMessage FunctionMessage 顶替 ToolMessage:新 OpenAI 兼容端拒收
MessageHistory.add_message 任意 BaseMessage 原样追加 不存 Tool 则下轮工具上下文断裂
LangGraph add_messages 含 RemoveMessage Remove 按 id 删除,其余 append/去重 把 Remove 塞进 ChatModel.invoke 无对应 role

厂商 role 对照(对话类):

LangChain 类 OpenAI role Anthropic 备注
SystemMessage system 常升格为请求级 system 不要插在对话中间当「旁白」
HumanMessage user user 多模态主要挂这里
AIMessage assistant assistant 唯一合法的模型回复容器
ToolMessage tool tool 必须带 tool_call_id
FunctionMessage function 基本不支持 旧协议
ChatMessage 你写的 role 不保证 避免

合法顺序(工具轮):[System?, Human, AI(tool_calls), Tool×N, (Human\|AI)...]。Tool 不得出现在对应 AI 之前。


4. 最小可运行示例

验证于 langchain-core + 本地 Ollama。需 ollama serve 且已 ollama pull qwen3.5:9b。异步:await model.ainvoke(msgs),参数与同步一致。

1
pip install -U langchain-core langchain-openai
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
AIMessage,
AIMessageChunk,
ChatMessage,
FunctionMessage,
HumanMessage,
RemoveMessage,
SystemMessage,
ToolMessage,
)

def show(m):
"""打印类型名、type 判别器、独有字段。输入任意 BaseMessage,无返回。"""
extra = {}
if isinstance(m, AIMessage):
extra["tool_calls"] = m.tool_calls
if isinstance(m, ToolMessage):
extra["tool_call_id"] = m.tool_call_id
extra["artifact"] = m.artifact
if isinstance(m, FunctionMessage):
extra["name"] = m.name
if isinstance(m, ChatMessage):
extra["role"] = m.role
if isinstance(m, RemoveMessage):
extra["id"] = m.id
print(type(m).__name__, m.type, extra or m.content[:20])

catalog = [
SystemMessage(content="只答一个词。"),
HumanMessage(content="中国的首都?"),
AIMessage(content="北京"),
ToolMessage(content="晴", tool_call_id="call_1", name="weather", artifact={"k": 1}),
FunctionMessage(content="晴", name="weather"),
ChatMessage(role="user", content="hi"),
RemoveMessage(id="msg_old"),
]
for m in catalog:
show(m)
# 预期:七行,type 分别为 system/human/ai/tool/function/chat/remove

model = ChatOpenAI(
model="qwen3.5:9b",
api_key="ollama",
base_url="http://localhost:11434/v1",
temperature=0,
)
msgs = [SystemMessage("只答一个词,不要标点。"), HumanMessage("浙江的省会?")]
ai = model.invoke(msgs)
print(type(ai).__name__, ai.content)
# 预期形态:AIMessage 杭州

chunk_n = 0
full = None
for c in model.stream(msgs):
chunk_n += 1
assert isinstance(c, AIMessageChunk)
full = c if full is None else full + c
print("chunks", chunk_n, "merged", type(full).__name__, getattr(full, "content", ""))
# 预期:chunks≥1;合并后 content 与 invoke 同义(措辞随模型)

RemoveMessage / FunctionMessage 不要塞进上面的 model.invoke。生产异步用 astream,chunk 类型相同。

重要配置参数

参数(API 名) 类型 / 默认值 功能说明 作用与影响 参考起点 配置指导
content str / list,必填 发给模型或展示给用户的载荷 过长费 token;工具轮 AI 可空 纯 str 多模态用 list block
type str,子类固定 序列化判别器,决定反序列化成哪一类 手改会导致 messages_from_dict 失败 勿赋值 靠选对类
tool_calls list,仅 AIMessage,默认 [] 模型发起的工具调用 非空则下游必须回填 ToolMessage name/args/id 不要写到 Human/System 上
tool_call_id str,仅 ToolMessage,必填 把结果绑回哪一次 tool_call 错/缺 → 厂商 400 tool_calls[].id 相同 原样拷贝
artifact Any,ToolMessage,默认 None 给应用的旁路数据,不进模型上下文 可放 doc_id 而不涨 prompt 检索工具 模型可见的只放 content
name str / None Human 标用户;Tool/Function 标工具名 Function 上必填;Human 上视厂商 可选 新工具链优先 ToolMessage.name
role str,仅 ChatMessage 自定义厂商角色名 非标准值多数集成不映射 避免 能用专用类就不用 ChatMessage
id str / None 消息主键;RemoveMessage 用它指定删除目标 无 id 则图上无法 Remove 持久化时设 模型返回的 id 请保留

5. 易踩坑

  1. FunctionMessage 冒充 ToolMessage:新 OpenAI 兼容端(含 Ollama /v1)要 role=tool + tool_call_id
  2. 未合并的 AIMessageChunkinvoke:后半段 tool_calls 可能还在后续 chunk 里。
  3. RemoveMessage 丢进 ChatModel:没有 role=remove;只给 add_messages 这类 reducer。
  4. invoke("问句") 以为能带 System:字符串捷径只有 Human,系统指令必须显式 SystemMessage

小结

  • 对话类四件套:System / Human / AI / Tool;新代码不要 FunctionMessage
  • 数据差在独有字段:tool_callstool_call_idartifactrole、Remove 的强制空 content
  • 下游差:ChatModel 映射 role;stream 出 Chunk;Parser 吃 AI content;图 reducer 才吃 Remove。
  • 最易踩:id 对不上Chunk 当完整消息Remove 发给模型

参考链接

-------------本文结束感谢您的阅读-------------