主题
模型 invoke 如何识别消息与历史上下文
概述
调用 invoke 时,消息列表内包含多条消息(含历史对话)。大模型本身不会单独标记或者记忆历史消息,依靠「消息角色类型」+「消息列表的先后顺序」来理解完整对话上下文。每一次执行 invoke,都会将消息列表中全部消息一次性打包序列化,传给大模型接口。
LangChain 底层处理逻辑
messages 本质是有序 Python 列表,列表顺序等价于对话时序,列表从前到后就是对话发生的先后顺序。
python
messages = [
HumanMessage("今天北京天气如何?"), # 最早:第一轮用户提问
AIMessage(..., tool_calls=[...]), # 模型返回工具调用请求
ToolMessage("北京天气晴朗", ...), # 工具返回结果
]执行 model_with_tools.invoke(messages) 的内部流程:
- LangChain 将完整消息数组按顺序序列化,转换为 OpenAI 接口标准 JSON 结构;
- LangChain 自动将 LangChain 的消息类型映射为 API 的
role:HumanMessage→role: "user"用户消息AIMessage→role: "assistant"AI模型消息ToolMessage→role: "tool"工具返回消息
- 大模型接收有序 JSON 数组,从头到尾顺序读取:排在前面的条目为历史上下文,最后一条为最新输入。
重点:不存在专门字段标记"这是历史消息",排在列表前面的消息天然就是历史上下文。
序列化后传递给大模型接口的原始 JSON 示例:
json
[
{"role":"user","content":"今天北京天气如何?"},
{"role":"assistant","tool_calls": [{"id":"xxx","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"北京\"}"}}]},
{"role":"tool","tool_call_id":"xxx","content":"北京天气晴朗"}
]识别消息的两个核心维度
1. role 消息角色
标识这条消息的发送方:用户 / AI助手 / 工具。
ToolMessage 必须携带
tool_call_id,用来和上一轮 AIMessage 发起的工具调用配对绑定。角色类型一旦错乱,会直接导致模型理解异常。
2. 列表先后顺序
消息顺序不可随意打乱。列表从上至下代表对话时间线:
- 靠前:更早产生的历史消息
- 末尾:最新消息
模型会读取全部有序上下文,基于完整信息生成新一轮回复。
常见误区澄清
| 误区 | 真相 |
|---|---|
| 模型会在服务端保存历史对话 | 每次 invoke 都必须完整传入全部消息。调用结束后,模型侧不会留存对话,上下文完全由本地维护的 messages 列表承载。 |
| 存在内置字段标记哪部分属于历史消息 | 没有独立的历史标记字段。只要追加进 messages 列表,所有消息都会作为上下文送入模型。 |
| ToolMessage 不属于历史消息 | ToolMessage 追加到 messages 之后,就成为历史上下文的一部分。下一轮 invoke 时模型会读取工具返回内容继续处理。 |
结合工具调用流程示例
python
1. messages = [HumanMessage("今天北京天气如何")]
2. response = model_with_tools.invoke(messages) # 传入:仅用户消息
3. messages.append(AIMessage) # AIMessage加入列表,成为历史
4. messages.append(ToolMessage) # ToolMessage加入列表,成为历史
5. model_with_tools.invoke(messages) # 第二次调用:一次性传入【用户消息 + AIMessage + ToolMessage】全部历史第二次 invoke,模型一次性拿到全部3条有序消息,读懂用户提问、AI工具调用、工具返回结果,输出最终自然语言回答。
工程边界说明
- 上下文窗口限制:消息持续追加,token总量持续增长,超过模型最大上下文窗口会报错或者自动截断;生产环境需要实现消息裁剪策略。
- tool_call_id 的作用:仅用于工具调用配对,不是用来区分历史消息。