bind_tools 之后模型会返回 tool_calls,但 谁去执行函数、谁把结果写回 messages 仍需一层执行逻辑。手写 for 循环容易漏并行、漏错误包装。LangGraph 提供 ToolNode 作为标准执行节点;在纯 LangChain/LCEL 场景下,你需要同等语义的执行模式——本篇讲 LangChain 侧 如何可靠跑工具,并说明与 LangGraph ToolNode 的分工。
段末注释:ToolNode 在 LangGraph 中为预构建工具执行节点;LangChain 应用层可手写等价循环或委托
create_agent内置图。
1. 一句话定位
| 维度 | 内容 |
|---|---|
| 角色 | 能力层工具执行:AIMessage.tool_calls → ToolMessage |
| 输入 → 输出 | AIMessage(含 tool_calls)→ list[ToolMessage] |
| 典型调用入口 | 手写 execute_tools;或 LangGraph ToolNode(tools).invoke({"messages":[ai]}) |
| 与 LangGraph | LangGraph ToolNode 是官方实现;create_agent 内部也用它;本篇侧重执行语义与 LangChain 手写模式 |
2. 实现逻辑
手写 LangChain 执行循环(与 ToolNode 语义对齐):
1 | 1. ai = model_with_tools.invoke(messages) |
字段级变形:
1 | tool_calls=[{id:"1", name:"add", args:{a:1,b:2}}] |
3. 原理说明
3.1 LangGraph ToolNode 做什么
ToolNode(类,langgraph.prebuilt)
功能:从 state 的最后一条 AIMessage 取出 tool_calls,并行执行,把 ToolMessage 追加回 messages。
| 字段 / 参数 | 类型 | 默认值 | 最小维度 |
|---|---|---|---|
tools |
list | 构造必填 | len≥1,name 须覆盖模型可能调用的工具 |
handle_tool_errors |
bool | True |
True 时异常变 ToolMessage 文本 |
ToolNode.invoke(state, config=None)(方法)
输入最小结构:{"messages": [AIMessage(tool_calls=[至少 1 条])]}。
输出:{"messages": [..., ToolMessage, ...]},ToolMessage 条数 = 有效 tool_calls 条数。
1 | from langgraph.prebuilt import ToolNode |
3.2 LangChain 侧为何还要理解
线性脚本未上图时手写循环即可。自定义 Agent 前须知 tool_call_id 与错误回填。create_agent 内置 ToolNode,调试仍看 ToolMessage 序列。
3.3 错误回填策略
参数非法:把错误文本放进 ToolMessage.content,让模型自修正。执行期异常:handle_tool_errors=True 时不把图打崩。
最小回填:每条失败的 tool_call 仍要 1 条 ToolMessage(id 对齐),不能缺 id。
1 | ToolMessage(content="参数 a 必须是 int", tool_call_id=call["id"]) |
3.4 与 return_direct
return_direct: bool = False(Tool 字段)
功能:True 时部分 Agent 跳过再次 call model,直接把工具结果给用户。手写循环须显式分支。
最小维度:布尔;与 ToolNode 默认行为(始终产出 ToolMessage)独立。
4. 最小可运行示例
1 | pip install -U langchain-openai langgraph |
1 | from langchain_openai import ChatOpenAI |
纯 LangChain 手写(不依赖 ToolNode):
1 | from langchain_core.messages import AIMessage, ToolMessage |
重要配置参数
| 参数(API 名) | 类型 / 默认值 | 功能说明 | 作用与影响 | 参考起点 | 配置指导 |
|---|---|---|---|---|---|
handle_tool_errors |
bool/str/callable,ToolNode,默认 True | 工具抛错时是否改写成 ToolMessage 而不是打崩图 | True 模型可看到错误并自愈;False 直接中断 | True | 生产建议 True |
tools |
list[BaseTool],构造必填 | ToolNode 允许执行的白名单 | 与 bind_tools 名不一致则 KeyError | 与 bind 列表同一批 | 不要漏注册 |
max_iterations |
int,Agent 层 | 工具循环最多跑几轮 | 过小答不完;过大费钱、易死循环 | 10~25 | 防无限 loop |
tool_call_id |
str,回填必填 | 把执行结果对上哪一次模型调用 | 缺失或错配则下一轮非法 | 与 AIMessage 一致 | 从 tool_calls 原样拷 |
messages_key |
str,默认 "messages" |
state 里消息列表的字段名 | 自定义 state 不改此键会读空 | "messages" |
与图 state 定义一致 |
| 并行执行 | ToolNode 内置,默认开 | 一条 AIMessage 上多个 tool_call 同时跑 | IO 工具加速;注意下游 rate limit | 默认开 | 有副作用的工具慎并行 |
5. 易踩坑
- 只 bind 了工具却用未注册 name 执行:模型幻觉出
foo,tools_by_name无此键。 - 异常直接 raise 不回填:模型看不到失败原因,下一轮上下文断裂。
- 混淆 LangChain 与 LangGraph 职责:ToolNode 属 LangGraph;纯 LCEL 无环时手写循环即可,不必强行上图。
小结
- 工具链核心:tool_calls → invoke → ToolMessage(tool_call_id)。
- LangGraph ToolNode 是标准执行实现;LangChain 脚本可手写同等循环。
- 错误应 回填为 ToolMessage 文本,便于模型修正。
create_agent内部已封装该循环;自定义时本篇逻辑仍适用。