Messages与对话结构

多轮对话 + 工具调用时,厂商 API 要求消息顺序与字段严格:assistant 声明 tool_calls 后必须跟对应 tool 角色结果,且 id 一一匹配。漏一条或顺序错乱,下一轮直接 400。Messages 类型把各 role 收成 Pydantic 对象,便于在 invoke 前校验与调试。

段末注释BaseMessage = LangChain 对话单元的基类;Tool Calling = 模型通过结构化字段请求执行外部工具。


1. 一句话定位

维度 内容
角色 能力层对话数据结构:统一 role、content、tool 元数据
输入 → 输出 应用层组装 list[BaseMessage] → 传入 ChatModel
典型调用入口 HumanMessage(...)AIMessage(tool_calls=...)ToolMessage(...)
与 LangGraph 图 state 中 messages 常为 Annotated[list, add_messages];类型仍用本篇

2. 实现逻辑

带工具的一轮完整结构:

1
2
3
4
1. [SystemMessage, HumanMessage("查北京天气")]
2. model.invoke → AIMessage(content="", tool_calls=[{id,name,args}])
3. 执行工具 → ToolMessage(content="晴", tool_call_id=与上 id 相同)
4. 再次 model.invoke([..., AIMessage(...), ToolMessage(...)]) → 最终 AIMessage(content="北京今天晴")

字段级变形

1
2
tool_calls[0].id = "call_abc"
→ ToolMessage(content="...", tool_call_id="call_abc") # 必须相等

3. 原理说明

3.1 常见消息类型

均继承 langchain_core.messages.BaseMessagecontent 可为 str 或多模态 list[block]

BaseMessage(抽象数据类)
功能:对话轮次的统一载体。
数据结构公共字段:content(必填);additional_kwargs: dict = {}response_metadata: dict = {}id: str | None = None
最小维度:至少 content(工具轮次 AI 可为 "")。

SystemMessage(数据类)
功能:系统指令,通常放列表首位。type="system"
最小维度:content ≥1 字符才有语义。

HumanMessage(数据类)
功能:用户输入。type="human"。最小 1 条即可构成一次提问。

AIMessage(数据类)
功能:模型回复。type="ai"

字段 类型 默认值 最小维度
content str / list 必填 工具轮次可为 ""
tool_calls list [] 0=纯文本;并行工具 ≥1
invalid_tool_calls list [] 解析失败时非空

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

字段 类型 默认值 最小维度
content str 必填 建议非空;异常时可写错误文本
tool_call_id str 必填 非空,必须等于对应 tool_calls[].id
name str / None None 可选
1
ToolMessage(content="晴,18°C", tool_call_id=ai.tool_calls[0]["id"])

3.2 tool_calls 结构

AIMessage.tool_calls(字段,list[dict])
功能:模型发起的工具调用。与 OpenAI tool_calls 对齐。

类型 默认值 最小维度
name str 必填 非空,须为已 bind 的工具名
args dict 必填 无参工具 {};键名对齐 schema
id str 必填 非空;回填 ToolMessage 必须相同
type str "tool_call"

最小一次调用 = {name, args, id} 三键。执行后必须用 相同 id 回填。

1
2
call = ai.tool_calls[0]
assert set(call) >= {"name", "args", "id"}

3.3 多 tool 并行

一条 AIMessage 可含 len(tool_calls) ≥ 2。对应 等量 ToolMessage(id 各自匹配),再一次性 invoke(history + [ai, *tool_msgs])
最小并行:2 条 tool_calls + 2 条 ToolMessage。

3.4 与 dict 互转

message_to_dict(message)(函数)
功能:Message → 可 JSON 序列化的 dict。无默认参数。最小输入:1 条 BaseMessage。

messages_from_dict(dicts)(函数)
功能:list[dict] → list[BaseMessage]。
最小维度:dicts len≥1(空列表返回 []);每项须含 typedata

1
2
3
from langchain_core.messages import message_to_dict, messages_from_dict
blob = [message_to_dict(m) for m in messages]
restored = messages_from_dict(blob)

生产仍推荐 typed Message,减少字段拼写错误。


4. 最小可运行示例

1
pip install -U 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
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, ToolMessage, SystemMessage
from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
"""查询城市天气。"""
return {"北京": "晴,18°C"}.get(city, "未知")

model = ChatOpenAI(
model="qwen3.5:9b",
api_key="ollama",
base_url="http://localhost:11434/v1",
temperature=0,
).bind_tools([get_weather])

messages = [
SystemMessage(content="需要天气时必须调用 get_weather。"),
HumanMessage(content="北京天气?"),
]
ai = model.invoke(messages)
print(ai.tool_calls)
# 预期形态:[{name: get_weather, args: {city: 北京}, id: ...}]

tool = ToolMessage(
content=get_weather.invoke(ai.tool_calls[0]["args"]),
tool_call_id=ai.tool_calls[0]["id"],
)
final = model.invoke(messages + [ai, tool])
print(tool.tool_call_id == ai.tool_calls[0]["id"], final.content)
# 预期:True,以及含晴或 18 的自然语言

重要配置参数

参数(API 名) 类型 / 默认值 功能说明 作用与影响 参考起点 配置指导
content str / list,必填 本轮对模型可见的正文或多模态块 工具轮次 ai.content 可为空;过长费 token 纯文本 引用资料时只塞需要的片段
tool_calls list,AIMessage,默认 [] 模型发起的待执行工具调用列表 非空则必须回填等量 ToolMessage 含 id/name/args args 须可 JSON 序列化
tool_call_id str,ToolMessage 必填 把工具结果绑回哪一次 tool_call 不一致则厂商 API 拒绝整轮 tool_calls[].id 相同 ai.tool_calls[i]["id"] 原样拷
name str,ToolMessage 可选 标明这条结果来自哪个工具 便于日志;部分厂商可选 tool_calls[].name 一致 调试时建议填
additional_kwargs dict,默认 {} 厂商扩展字段容器 业务代码少写;集成包内部用 少用 不要塞业务 payload
id str,消息级可选 单条消息的唯一标识 持久化/去重用;缺省由框架生成 可选 存库时建议设

5. 易踩坑

  1. ToolMessage 缺 tool_call_id 或 id 不匹配:OpenAI/Anthropic 均报错,且错误信息有时只提 “tool”。
  2. 在 tool 结果未齐时就 invoke:多个 tool_calls 只回填一个 ToolMessage,上下文非法。
  3. 把 ToolMessage 放在 AIMessage 之前:顺序须保持 user → assistant(tool_calls) → tool(s) → assistant。

小结

  • 对话在 LangChain 里是 list[BaseMessage],工具场景扩展 AIMessage.tool_calls + ToolMessage
  • tool_call_id 是工具链最容易错的一字段。
  • System 通常首位;多 tool 须 全部回填 再继续 model。
  • 持久化可用 dict 互转,运行时优先 typed Message。

参考链接

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