news 2026/10/2 12:15:18

LangGraph react agent 执行过程原理详解:从节点流转到工具调用的完整链路拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangGraph react agent 执行过程原理详解:从节点流转到工具调用的完整链路拆解

1. 从一次“卡住不返回”的 Agent 说起:LangGraph react agent 执行过程到底怎么跑

如果你正在用 LangGraph 搭 agent,大概率遇到过这种场景:用户问一句“帮我查下北京明天天气,再决定要不要提醒带伞”,日志里tool_calls打印出来了,工具也执行了,但图就是不走下一步,或者干脆在agent和tools两个节点之间来回横跳停不下来。这类问题八成不是模型笨,而是你没看清 LangGraph react agent 执行过程的节点流转机制——它本质是一张有向图,靠状态(State)在节点(Node)之间传递,靠边(Edge)决定下一步去哪。

LangGraph react agent 是什么?简单说,它是 LangChain 生态里用「状态图」实现的 ReAct 循环:把「推理(Reason)」和「行动(Act)」拆成图上的节点,用条件边控制循环与终止。它能做什么?让 agent 支持多轮工具调用、动态分支、可追溯的状态变化。适合谁?正在用 LangGraph 写 agent、被StateGraph的节点和边绕晕、想让执行链路可调试的开发者。

我试过把整个链路拆成五段来看:输入解析 → 推理节点 → 工具调用 → 结果回填 → 循环终止。每一段对应图上的一个节点或一条边,看懂这五段,日志里每一步跳转你都能对上号。下面按这个顺序逐段拆,每段都给可复制的StateGraph配置和验证动作,最后用一次完整执行链路的日志把节点与边的驱动关系串起来。

需要先说明一点:本文所有模型调用都通过统一的 API 入口完成,Base URL 指向https://taotoken.net/api,这样你在本地跑 LangGraph 时不用改一堆环境变量,Key 和模型 ID 在一处配好即可。后面 §2 会给出具体的前置配置。

2. 前置:把模型入口和依赖配好,让 StateGraph 能跑起来

在拆节点之前,先把运行环境搭好。LangGraph 本身不绑定模型供应商,它通过 LangChain 的 ChatModel 接口调用 LLM。你要做的是:装依赖、配好 Base URL 和 Key、确认模型 ID 可用。这一步不做,后面tool_calls根本出不来。

2.1 安装依赖

pip install langgraph langchain-openai langchain-core

langgraph提供StateGraph、add_node、add_conditional_edges;langchain-openai提供兼容 OpenAI 协议的ChatOpenAI客户端,用来指向统一入口。

2.2 配置模型入口(Base URL + Key + Model ID 三件套)

LangGraph 里模型客户端建议单独抽一个文件,比如llm_client.py:

# llm_client.py import os from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="claude-sonnet-4-5", # Model ID,按你控制台里可用的填 base_url="https://taotoken.net/api", # Base URL,注意不要带多余路径 api_key=os.environ["TAOTOKEN_API_KEY"], temperature=0, )

三件套缺一不可:Base URL 决定请求打到哪,Key 决定能不能过鉴权,Model ID 决定用哪个模型。任何一处写错,典型表现就是 401 或者model not found。

2.3 环境变量

export TAOTOKEN_API_KEY="sk-你的key"

Key 在控制台的 API Keys 页面生成,生成后只显示一次,记得存好。如果你还没建 Key,可以先到模型对话页面确认模型能正常回话,再去 API Keys 建 Key,顺序别反。

2.4 先验证模型能通

在写图之前,先跑一段最小调用,确认三件套没问题:

from llm_client import llm resp = llm.invoke("用一句话说明什么是 ReAct") print(resp.content)

能打印出内容,说明模型入口通了。这一步过了再动StateGraph,否则后面报错你分不清是图的问题还是模型入口的问题。

提示:如果你用的是 Claude 系列模型做 agent,注意工具调用格式走的是 Anthropic 风格,langchain-openai的兼容层会帮你转换,但模型 ID 要填对,否则tool_calls字段可能为空。

前置做完,下面进入正题:状态怎么定义、节点怎么加、边怎么连。

3. 可复制配置:StateGraph 的节点、边与状态定义

这一段是全文核心。LangGraph react agent 执行过程原理,落到代码上就是三件事:定义 State、加节点、连边。我把它拆成可直接复制的片段,你照着改就能跑。

3.1 定义 State:messages 是主线索

ReAct 循环的状态核心是消息列表。工具调用请求和工具返回结果都以消息形式追加进去,节点每次读取最新状态、返回增量更新。

# state.py from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class AgentState(TypedDict): messages: Annotated[list, add_messages]

add_messages是 reducer,作用是「追加而非覆盖」。节点返回{"messages": [new_msg]}时,LangGraph 会把它合并进已有列表。这是理解结果回填的关键:工具结果不是替换状态,而是追加。

3.2 定义工具

# tools.py from langchain_core.tools import tool @tool def get_weather(city: str) -> str: """查询指定城市的天气,返回温度和是否降雨。""" fake_db = {"北京": "晴,18℃,无雨", "上海": "小雨,22℃,有雨"} return fake_db.get(city, "暂无数据") tools = [get_weather]

工具描述(docstring)会被塞进模型上下文,模型据此判断要不要调、调哪个。描述写清楚,tool_calls才准。

3.3 绑定工具到模型

# graph_build.py from llm_client import llm from tools import tools llm_with_tools = llm.bind_tools(tools)

bind_tools把工具 schema 注入请求,模型返回的消息里就会带tool_calls字段。

3.4 加节点:agent 节点与 tools 节点

from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode from state import AgentState def agent_node(state: AgentState): # 推理节点:把当前消息喂给模型,返回模型消息 response = llm_with_tools.invoke(state["messages"]) return {"messages": [response]} tool_node = ToolNode(tools) # 工具调用节点,自动执行 tool_calls builder = StateGraph(AgentState) builder.add_node("agent", agent_node) builder.add_node("tools", tool_node)

agent节点负责推理,tools节点负责执行。ToolNode是预置节点,它会读取上一条消息里的tool_calls,逐个执行并把结果包成ToolMessage追加回状态。

3.5 连边:条件边驱动循环

from langgraph.graph import END def should_continue(state: AgentState): last = state["messages"][-1] if getattr(last, "tool_calls", None): return "tools" # 有工具调用 → 去 tools 节点 return END # 没有 → 结束 builder.add_edge(START, "agent") builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END}) builder.add_edge("tools", "agent") # 工具执行完回到 agent,形成循环 graph = builder.compile()

这段配置就是 ReAct 循环的全部骨架:START → agent → (条件) → tools → agent → ... → END。should_continue是决策点,它看最后一条消息有没有tool_calls:有就去执行工具,没有就终止。tools → agent这条边是回边,保证结果回填后能再次推理。

3.6 参数对照表

配置项作用写错的表现
add_messages追加消息而非覆盖状态被覆盖,历史丢失
bind_tools注入工具 schematool_calls为空,永不调工具
should_continue决定循环还是终止死循环或提前结束
tools → agent回边结果回填后重新推理工具执行完图就停

把上面几个文件拼起来,graph.invoke({"messages": [("user", "北京明天天气如何?")]})就能跑。下一段用日志验证每一步。

4. 验证请求:一次完整执行链路的日志拆解

配置写完,最怕的是「看起来对但不知道内部怎么走」。这一段用日志把五段链路逐段对上:输入解析、推理节点、工具调用、结果回填、循环终止。

4.1 加日志钩子

# run_with_log.py from graph_build import graph def run(question: str): print("=== 输入解析 ===") print("user:", question) result = graph.invoke( {"messages": [("user", question)]}, config={"recursion_limit": 10}, # 防止死循环 ) print("=== 最终消息链 ===") for i, m in enumerate(result["messages"]): print(f"[{i}] {type(m).__name__}: {getattr(m, 'content', '')[:80]}") if getattr(m, "tool_calls", None): print(" tool_calls:", m.tool_calls) return result run("北京明天天气如何?需要带伞吗?")

recursion_limit是保险丝,循环超过 10 步直接抛错,避免你调试时无限跑。

4.2 逐段对照日志

跑完后消息链大致长这样:

=== 输入解析 === user: 北京明天天气如何?需要带伞吗? === 最终消息链 === [0] HumanMessage: 北京明天天气如何?需要带伞吗? [1] AIMessage: tool_calls: [{'name': 'get_weather', 'args': {'city': '北京'}, 'id': 'call_abc'}] [2] ToolMessage: 晴,18℃,无雨 [3] AIMessage: 北京明天晴,18℃,无雨,不需要带伞。

逐段拆:

输入解析对应[0],用户消息进状态。推理节点对应[1],agent_node调用模型,模型判断需要调工具,返回带tool_calls的AIMessage。工具调用对应[2],ToolNode执行get_weather,把结果包成ToolMessage。结果回填就是[2]被追加进messages,状态更新。循环终止对应[3],tools → agent回边触发第二次推理,这次模型看到工具结果,不再产生tool_calls,should_continue返回END,图结束。

4.3 多轮工具调用长什么样

如果问题需要多次调用,比如「查北京和上海天气再对比」,消息链会变成:

[0] HumanMessage [1] AIMessage (tool_calls: get_weather 北京) [2] ToolMessage (北京结果) [3] AIMessage (tool_calls: get_weather 上海) [4] ToolMessage (上海结果) [5] AIMessage (最终对比回答)

这里[3]是回边后第二次推理产生的,说明循环真的在跑。看到这种交替出现的AIMessage / ToolMessage,就说明节点流转正常。

4.4 用 stream 看实时跳转

想更细地看每一步,用stream:

for chunk in graph.stream({"messages": [("user", "北京天气如何?")]}, stream_mode="updates"): print(chunk)

updates模式每次节点执行完吐一次,你能直接看到{"agent": ...}和{"tools": ...}交替出现,节点名就是跳转证据。

验证通过后,下面把常见报错对号入座。

5. 常见错排查:401、tool_calls 为空、死循环、OAuth 报错

调试 LangGraph agent 时,报错基本集中在四类。逐个对照。

5.1 401 Unauthorized

openai.AuthenticationError: Error code: 401 - {'error': {'message': 'Invalid API key'}}

原因:Key 没配、配错、或者环境变量没生效。检查TAOTOKEN_API_KEY是否导出成功,base_url是否写成https://taotoken.net/api(不要多加/v1或尾部斜杠)。三件套里 Key 和 Base URL 必须成对正确。

5.2 tool_calls 为空,工具永不触发

AIMessage(content='北京明天晴...', tool_calls=[])

原因通常是没调bind_tools,或者工具 docstring 太模糊模型判断不出该调。确认llm_with_tools = llm.bind_tools(tools),并把工具描述写具体。另一个坑是模型 ID 不支持工具调用,换一个支持 function calling 的模型。

5.3 死循环:agent 和 tools 来回跳

GraphRecursionError: Recursion limit of 10 reached

原因:should_continue判断逻辑写错,或者工具一直返回让模型觉得「还得再调」的结果。检查should_continue是否读的最后一条消息,确认tools → agent回边存在但终止条件可达。临时用recursion_limit兜底,根治要修判断逻辑。

5.4 local proxy failed / 连接类报错

APIConnectionError: Connection error.

原因:Base URL 写错、网络不通、或本地环境变量指向了不存在的地址。确认base_url拼写,确认没有多余的路径段。这类报错和鉴权无关,纯粹是请求没发出去。

5.5 OAuth / 鉴权相关报错

如果你在 Claude Code 或 Codex 这类工具里接入,可能遇到 OAuth 相关提示。这类工具通常要求填 Base URL、Key、Model ID 三件套,缺一个就报鉴权失败。以 Codex 的auth.json为例,配置结构大致是:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的key", "model": "claude-sonnet-4-5" }

Cline 的 MCP 配置、CC Switch 的切换配置同理,都是三件套齐全才能通。任何一处缺失,表现都是鉴权失败或模型找不到。

5.6 排错顺序建议

先确认模型能单独调通(§2.4),再看tool_calls是否产生,再看should_continue返回值,最后看回边。按这个顺序,90% 的问题能定位。

6. 把链路跑通之后:接入入口与下一步

节点流转看懂了,接下来就是把它接到真实项目里。LangGraph react agent 执行过程原理的核心就一句话:状态在节点间传递,条件边决定循环还是终止,回边保证结果回填后能再次推理。你把 §3 的配置复制下来,把 §4 的日志跑一遍,整条链路就活了。

实际接入时,模型入口统一走https://taotoken.net/api,Key 在控制台的 API Keys 页面建,模型 ID 按控制台可用列表填。三件套配好,LangGraph 这边不用改任何节点逻辑。如果你还在选模型阶段,可以先去模型对话页面把几个候选模型都试一遍工具调用,确认哪个tool_calls稳定再写进llm_client.py。

长期跑编码类或 Agent 类任务的话,Coding Plan 更适合持续调用场景,不用每次单独管额度。接入文档里有各语言客户端的完整示例,排障时对着看比猜快。链路跑通只是开始,真正省时间的是把日志和recursion_limit当成标配,每次调图先看消息链,问题基本藏不住。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 12:15:10

零代码上线生产:用 TaoToken 统一 Key 搭一个 AI 应用监控平台

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 12:14:51

偶发bug排查实战:串口换机排除、蓝牙录屏取证、烧录批次对照

串口时不时丢数据、蓝牙用着用着就断开、烧录偶发失败——这类"偶尔出现、查不到规律"的问题,恐怕是嵌入式开发里最磨人的场景。你刚把逻辑分析仪接上,它就好了;你盯着串口助手看十分钟,它屁事没有;你一拍桌…

作者头像 李华
网站建设 2026/10/2 12:14:43

电赛四天三夜备赛实战指南:从组队到现场排雷

每年8月那几天,全国高校实验室都会同步亮起“作战”状态的灯。我见过太多队伍在电子设计竞赛(简称电赛)这四天三夜里,被自己的准备不足打得措手不及——有人连题目都没读完就开始焊板子,有人凌晨三点才意识到单片机引脚…

作者头像 李华