
这次我们来看一个和智能体开发高度相关的项目LangGraph。它不是某张显卡上的模型而是 AI 大模型应用层的编排框架。如果你已经接触过 LangChain、MCP、智能体 Agent 这些概念但一直没搞清楚它们之间怎么配合、怎么落地这篇文章可以帮你把整条链路串起来环境安装、核心组件、条件路由、子图、并行分支、API 服务调用以及常见坑位排查。LangGraph 来自 LangChain 团队核心思路是“把智能体定义成一张状态图”。传统 LLM 应用是一段顺序代码调用模型、拿到结果、结束而 LangGraph 把流程拆成节点Node和边Edge节点执行具体操作边决定下一步走到哪里。因此它可以处理循环、条件跳转、人工介入、子任务复用这类复杂流程这也是它能做生产级 Agent 的原因。这篇文章会从零开始带你在本地搭建一个 LangGraph 项目接着用代码演示 StateGraph、节点、边、条件路由、子图和并行分支然后接入大模型做智能体工具调用最后给出 API 服务调用示例和批量任务的工程建议。整个过程不依赖 ComfyUI、WebUI 这种图形界面而是直接跑 Python 代码方便你接到自己的系统和数据流水线里。1. LangGraph 核心能力速览能力项说明项目类型开源 LLM 应用编排框架聚焦智能体状态图来源LangChain 团队维护仓库在 langchain-ai/langgraph主要功能智能体流程编排、条件路由、循环控制、子图、并行分支、持久化、人工介入与 LangChain 的关系LangChain 提供组件模型封装、工具、记忆LangGraph 提供状态图执行引擎与 MCP 的关系MCP 是工具服务协议LangGraph 的 ToolNode 可封装 MCP 工具两者配合使用运行环境本地 Python 3.9 即可无需 GPU 也能学模型推理才需要 API Key 或本地模型显存占用框架本身几乎不占显存显存取决于你接入的大模型支持平台Windows / macOS / Linux 均可启动方式Python 脚本直接调用或 langgraph dev 启动服务或 Docker 启动接口 API支持 LangGraph Server提供 REST API 和 Python/JS SDK批量任务可用多线程、异步、队列方式调用图需要自己设计并发与重试适合场景客服 Agent、知识库问答、工具调度、工作流自动化、定时任务、代码审查助手从核心定位来看LangGraph 不是“开箱即用的一键 Agent”而是“让开发者自己定义 Agent 运行逻辑”的框架。它会给你完整的控制权同时也要求你动手写图结构。2. 适用场景与使用边界2.1 适合什么人已经会用 Python 调大模型 API想让多个模型调用、工具调用、知识库检索组成完整流程的开发者。用 LangChain 写过简单链但发现顺序链无法处理“判断后跳转”“循环反思”“并行工具”等场景的人。团队要落地客服、RAG 问答、数据分析助手需要把流程固化为可测试、可审计的代码。想了解 Agent 框架底层原理为后续学习 AutoGen、CrewAI、Dify 等框架打基础的算法/后端工程师。2.2 能解决什么问题先看一个典型场景用户问“帮我查一下本周订单量并总结趋势”。如果用普通 LangChain 链你需要手工写 if-else先判断是否需要查数据库、调用哪个工具、拿回结果如何总结。逻辑一多代码就散成一片。LangGraph 的处理方式是把“判断意图”“调用工具”“总结回答”建成节点用条件边把节点连接起来。模型返回工具调用指令后图自动进入工具节点工具结果回填后再回到模型节点循环直到模型认为可以结束。整个流程的状态、中间结果、执行路径都是显式的。这样可以带来的实际收益循环可控模型可以多次调用工具直到拿到足够信息。流程可视化StateGraph 可以打印结构也能对接 LangGraph Studio 可视化调试。可持久化节点之间共享状态崩溃恢复和人工介入都有基础。可测试每个节点是纯函数可以单独单测。2.3 不适合什么场景纯前端或非 Python 用户建议先确认团队是否有 Python 能力。只想拖拽配置、不写代码的运营人员Dify、Coze 这类低代码平台更合适。对单次 API 调用都嫌麻烦的场景直接写一个 function call 就够不需要引入图框架。需要 GPU 密集型推理调优的场景LangGraph 只是编排层训练和推理优化仍需要模型侧做功。2.4 安全与合规边界这是很重要的一点。LangGraph 本身不产生模型能力也不直接访问外部系统所有工具调用都来自你定义的 ToolNode。因此使用边界完全由你控制涉及用户隐私、业务数据时先确认数据脱敏和访问授权。API Key 不要硬编码进代码仓库使用环境变量或密钥管理服务。如果 Agent 能调用数据库、发邮件、删除文件一定要加人工确认步骤即 human-in-the-loop。开源代码和模型的使用要遵循各自 License商用前务必核对。内容生成类应用要保证最终输出经过复核避免模型幻觉被当成事实发布。3. LangGraph 本地部署环境准备LangGraph 是一个 Python 框架门槛不高核心依赖是langgraph和langchain-core。跑最简单的图甚至不需要配置大模型 API因为节点里可以写普通函数。3.1 基础环境检查建议本机按以下版本准备不是硬性要求但更稳环境项建议Python3.9 / 3.10 / 3.11 / 3.12 均可pip22.0 以上操作系统Windows 11、macOS、Linux 均可虚拟环境推荐 venv 或 conda大模型 APIOpenAI 兼容接口或 LangChain 支持的模型服务比如 OpenAI、DeepSeek、Qwen、智谱等如果本机没有 Python先装 Python 3.11安装时勾选 Add Python to PATH。磁盘占用不大框架加依赖大约几百 MB。真正占空间的是虚拟环境和后续可能需要的内存模型学习阶段只会用到 API不需要 GPU。3.2 创建虚拟环境Windows PowerShellpython -m venv .venv .venv\Scripts\activatemacOS / Linuxpython3 -m venv .venv source .venv/bin/activate激活后命令行前缀会出现.venv说明进入了虚拟环境。4. LangGraph 安装部署与启动方式4.1 pip 安装核心包pip install -U langgraph langchain-core如果想使用 LangGraph 的服务能力和 SDK继续装pip install -U langgraph-cli langgraph-sdklanggraph-cli用来管理本地 LangGraph 服务langgraph-sdk是 Python 客户端。验证安装python -c import langgraph; print(langgraph.__version__)能输出版本号就说明安装正常。langgraph.__version__在不同版本可能位置有差异如果没有这个属性就用import importlib.metadata; print(importlib.metadata.version(langgraph))。4.2 启动 LangGraph 服务LangGraph 提供了类似“本地服务”的启动方式适合开发调试和接口联调。项目根目录需要有graph.py或langgraph.json作为入口。最常见的开发模式langgraph dev这个命令会启动一个本地开发服务默认地址通常是http://127.0.0.1:2024。如果 2024 端口被占用命令会自动提示或报错这时可以用--port指定langgraph dev --port 2025如果本机安装 Docker 且希望用容器方式启动可以运行langgraph uplanggraph up会根据langgraph.json构建容器并启动服务。文件内容类似{ dependencies: [.], graphs: { my_agent: ./graph.py:graph } }其中./graph.py:graph表示在graph.py中导出一个名为graph的编译对象。这样启动后API 里就能通过my_agent这个名称调用你的图。更简单的启动方式其实是直接写 Python 脚本调用图对象不需要任何服务进程。服务模式主要用于 API 集成和可视化调试。4.3 环境变量配置调用大模型时需要配置 API Key。以 OpenAI 兼容接口为例Windows PowerShell$env:OPENAI_API_KEY 你的KeymacOS / Linuxexport OPENAI_API_KEY你的Key也可以写入项目根目录的.env文件然后用dotenv加载。无论哪种方式都不要把 Key 提交到 git 仓库。5. LangGraph 核心组件与智能体开发基础LangGraph 的核心抽象是 StateGraph。理解下面几个词基本就理解一半了组件作用State全局状态类型通常是 TypedDict节点间共享数据Node图中的一个处理单元接收 State返回 State 的部分更新Edge连接 Node 的路径决定从哪个节点走到哪个节点START图表入口所有执行流从 START 进入END图表结束标记Conditional Edges条件边根据状态或函数返回值决定走哪条边Subgraph子图把一个小图嵌套进大图的节点Compile把 StateGraph 编译成可调用的 Runable 对象5.1 第一个最简单的图先用三个普通函数演示 StateGraph 的基本生命周期。from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): message: str def node_1(state: State) - State: return {message: state[message] - node_1} def node_2(state: State) - State: return {message: state[message] - node_2} def node_3(state: State) - State: return {message: state[message] - node_3} builder StateGraph(State) builder.add_node(node_1, node_1) builder.add_node(node_2, node_2) builder.add_node(node_3, node_3) builder.add_edge(START, node_1) builder.add_edge(node_1, node_2) builder.add_edge(node_2, node_3) builder.add_edge(node_3, END) graph builder.compile() result graph.invoke({message: start}) print(result)运行后输出会依次经过 node_1、node_2、node_3打印出{message: start - node_1 - node_2 - node_3}这个例子没有调用任何大模型但它已经展示了图的基本逻辑State 在节点之间传递每个节点可以读取和更新状态。5.2 接入大模型做一个能调用工具的 Agent下面这个例子更接近真实智能体。这里使用一个“假工具”来演示模型如何触发工具调用。实际项目中可以把任意 Python 函数比如天气查询、数据库查询、计算器传给tool装饰器。from typing import TypedDict, Annotated from langchain_core.tools import tool from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode, tools_condition tool def add(a: int, b: int) - int: 计算两个整数的和。 return a b llm ChatOpenAI(modelgpt-4o-mini, temperature0) tools [add] llm_with_tools llm.bind_tools(tools) class AgentState(TypedDict): messages: Annotated[list, lambda x, y: x y] def bot(state: AgentState): result llm_with_tools.invoke(state[messages]) return {messages: [result]} builder StateGraph(AgentState) builder.add_node(bot, bot) builder.add_node(tools, ToolNode(tools)) builder.add_edge(START, bot) builder.add_conditional_edges( bot, tools_condition, {tools: tools, END: END} ) builder.add_edge(tools, bot) graph builder.compile()这里的核心是add_conditional_edges。tools_condition是 LangGraph prebuilt 模块提供的判断函数如果模型返回了工具调用指令就跳到 tools 节点如果没有就直接进入 END。messages字段使用Annotated[list, lambda x, y: x y]表示消息列表采用“追加”方式更新否则后面节点的消息会覆盖前一个节点的结果。调用这个图result graph.invoke({ messages: [{role: user, content: 3 5 等于多少}] }) for msg in result[messages]: if hasattr(msg, content): print(f{msg.type}: {msg.content})如果 API Key 配置正常你会看到模型先判断需要调用 add 工具然后工具节点执行加法再把结果送回模型节点模型最终输出答案。这个流程就是最典型的 ReAct Agent 模式模型推理 - 调用工具 - 观察结果 - 继续推理。6. LangGraph 条件路由与分支控制实战条件路由是 LangGraph 相对传统顺序链的核心优势。场景用户问题需要根据不同意图走不同处理分支。比如“计算题”走计算工具“天气”走天气工具“闲聊”直接回答。6.1 自定义条件路由函数from typing import TypedDict, Literal class RouterState(TypedDict): question: str def route_question(state: RouterState) - Literal[calculate_node, chat_node]: if any(k in state[question] for k in [加, 减, 乘, 除, 计算]): return calculate_node return chat_node def calculate_node(state: RouterState) - RouterState: return {question: f进入计算分支待求问题{state[question]}} def chat_node(state: RouterState) - RouterState: return {question: f进入闲聊分支待回复{state[question]}} builder StateGraph(RouterState) builder.add_node(calculate_node, calculate_node) builder.add_node(chat_node, chat_node) builder.add_edge(START, route_node) # 这里先加一个路由判断节点 # 实际写法直接对 START 加条件边更标准的做法是对 START 加条件边builder StateGraph(RouterState) builder.add_node(calculate_node, calculate_node) builder.add_node(chat_node, chat_node) builder.add_conditional_edges( START, route_question, { calculate_node: calculate_node, chat_node: chat_node } ) builder.add_edge(calculate_node, END) builder.add_edge(chat_node, END) graph builder.compile()测试print(graph.invoke({question: 帮我计算 123456})) print(graph.invoke({question: 你好呀}))第一个会进入计算分支第二个进入闲聊分支。这就是条件路由的核心用法。6.2 循环检测与最大步数控制循环是 Agent 中非常常见的需求模型反复调用工具直到信息完整。但如果模型陷入死循环图会无限执行下去。LangGraph 的递归限制会在达到默认步数时抛出错误。from langgraph.errors import GraphRecursionError try: graph.invoke({messages: [{role: user, content: 请不断调用工具直到你觉得可以停止}]}) except GraphRecursionError as e: print(达到递归上限已强制停止, e)工程上更稳妥的做法是在编译时直接限制递归步数graph builder.compile(interrupt_beforeNone, limit20)limit参数可以控制最大执行步数超过就停止避免资源浪费。也可以用graph.recursion_limit在运行时临时设置。真正常见的实现方式是给状态加一个step计数器在条件边里检查当前步数超过阈值就跳转到 END 或人工节点。6.3 子图Subgraph当一个流程复杂度上升把所有节点写在一张图里会非常难维护。LangGraph 支持把一张图作为另一个图的节点这就是子图。from typing import Any # 子图定义 def sub_process_1(state: RouterState) - RouterState: return {question: state[question] [sub1]} sub_builder StateGraph(RouterState) sub_builder.add_node(sub_process_1, sub_process_1) sub_builder.add_edge(START, sub_process_1) sub_builder.add_edge(sub_process_1, END) sub_graph sub_builder.compile() # 主图使用子图 def main_node(state: RouterState) - RouterState: return {question: state[question] [main]} main_builder StateGraph(RouterState) main_builder.add_node(main_node, main_node) main_builder.add_node(sub_graph, sub_graph) # 子图作为节点 main_builder.add_edge(START, main_node) main_builder.add_edge(main_node, sub_graph) main_builder.add_edge(sub_graph, END) main_app main_builder.compile() result main_app.invoke({question: test}) print(result)子图的好处是每个子图可以独立测试也可以在不同主图中复用。生产项目通常把“意图识别”“知识库检索”“答案生成”分别做成子图再组合成完整 Agent。6.4 并行分支LangGraph 支持一个节点分叉成多个并行分支最后再合并。比如同时调用多个工具、同时做多个独立检索然后汇总。class ParallelState(TypedDict): question: str result_a: str result_b: str def branch_a(state: ParallelState) - ParallelState: return {result_a: fA处理: {state[question]}} def branch_b(state: ParallelState) - ParallelState: return {result_b: fB处理: {state[question]}} def merge(state: ParallelState) - ParallelState: return {question: state[result_a] | state[result_b]} pb StateGraph(ParallelState) pb.add_node(branch_a, branch_a) pb.add_node(branch_b, branch_b) pb.add_node(merge, merge) pb.add_edge(START, branch_a) pb.add_edge(START, branch_b) pb.add_edge(branch_a, merge) pb.add_edge(branch_b, merge) pb.add_edge(merge, END) parallel_app pb.compile() print(parallel_app.invoke({question: hello, result_a: , result_b: }))这里两个分支并行执行都完成后汇入 merge 节点。需要注意并行分支之间不要互相依赖否则状态容易混乱。7. 智能体与 LangChain、MCP 生态集成7.1 LangGraph 和 LangChain 的关系很多初学者会把 LangGraph 和 LangChain 当成同一种东西其实它们是分工关系对比维度LangChainLangGraph定位LLM 应用工具链Agent 状态机编排引擎核心抽象Chain、Model Wrapper、Tool、MemoryStateGraph、Node、Edge复杂度适合顺序流程适合复杂条件分支、循环、并行最新趋势官方推荐复杂 Agent 用 LangGraph承接 LangChain 的部分 Agent 场景简单理解LangChain 提供模型封装、提示词模板、工具定义、文档加载等基础能力LangGraph 提供执行引擎决定这些能力以什么顺序、什么条件运行。7.2 LangGraph 与 MCP 的配合MCPModel Context Protocol是工具服务协议核心作用是让 Agent 通过统一协议调用外部工具比如数据库、文件系统、API 服务、蓝湖设计稿等。LangGraph 本身不强制使用 MCP它的 ToolNode 可以直接挂普通函数但在真实团队环境中多个 Agent 共用一套工具服务时MCP 可以避免“每个 Agent 各自封装一遍工具”。在 LangGraph 中使用 MCP 工具常见流程是使用 MCP SDK 或 MCP 客户端连接到远程工具服务器。将 MCP 工具列表转换成 LangChain Tool 对象。添加到 ToolNode编译成图。MCP 的引入更多是在工具侧做标准化LangGraph 仍然负责流程调度。二者是互补关系而不是替代关系。7.3 人工介入与持久化生产级 Agent 最重要的能力之一就是“需要的时候能停下来等人确认”。LangGraph 支持中断和恢复graph builder.compile(interrupt_before[tools])当执行到 tools 节点之前图会暂停等待外部确认。调用时可以获取当前状态和下一步执行计划确认后继续# 第一次调用会中断 result graph.invoke(initial_input) # 通过 get_state 获取中断信息 snapshot graph.get_state(thread_id) # 确认后继续 result graph.invoke(None, thread_idthread_id)这里需要配置thread_idLangGraph 用它来保存和恢复执行状态。生产环境中可以配合SqliteSaver或 Postgres 持久化确保服务重启后 Agent 还能从断点继续。from langgraph.checkpoint.sqlite import SqliteSaver checkpointer SqliteSaver.from_conn_string(checkpoints.sqlite) builder StateGraph(AgentState) # ... 添加节点和边 graph builder.compile(checkpointercheckpointer)这样每个线程的执行进度都会持久化Agent 多轮对话、中断恢复、审计追踪都有数据支撑。8. LangGraph 接口 API 与批量任务LangGraph 的图对象可以嵌入 FastAPI 等服务对外暴露也可以使用官方 LangGraph Server 直接提供 REST API。8.1 启动 API 服务在包含langgraph.json的项目目录下运行langgraph dev服务启动后访问http://127.0.0.1:2024可以看到 API 文档。LangGraph Server 提供线程创建、运行创建、状态查询等接口。8.2 Python SDK 调用示例from langgraph_sdk import get_client client get_client(urlhttp://127.0.0.1:2024) # 创建线程 thread client.threads.create() thread_id thread[thread_id] print(thread_id:, thread_id) # 运行图 result client.runs.create( thread_idthread_id, assistant_idmy_agent, input{messages: [{role: user, content: 35等于多少}]} ) print(result)assistant_id对应langgraph.json中配置的图名称不是随意填的。8.3 curl 调用示例curl -X POST http://127.0.0.1:2024/runs \ -H Content-Type: application/json \ -d { assistant_id: my_agent, input: { messages: [ {role: user, content: 今天天气怎么样} ] } }响应中会包含运行状态、最终输出和中间步骤信息。8.4 批量任务设计与建议LangGraph 的图对象在单进程内可以复用批量任务的关键是并发控制和错误处理。建议方案使用ThreadPoolExecutor或asyncio并发调用图对象。每条任务使用独立线程 ID避免状态串线。记录每条任务的开始时间、结束时间、token 消耗。对 API 限流错误做指数退避重试。批量任务队列使用 Redis 或数据库持久化避免进程重启后任务丢失。简化版示例from concurrent.futures import ThreadPoolExecutor, as_completed questions [ 35等于多少, 12*4等于多少, 100-25等于多少, ] def run_one(q: str): return graph.invoke({ messages: [{role: user, content: q}] }) with ThreadPoolExecutor(max_workers3) as executor: futures [executor.submit(run_one, q) for q in questions] for future in as_completed(futures): result future.result() print(result[messages][-1].content)工程上建议任务数量大时接入消息队列而不是只用线程池。9. 资源占用与性能观察9.1 如何观察资源占用LangGraph 本身是轻量框架主要消耗是 CPU/内存以及调用大模型 API 的网络请求和 token 费用。如果你接入的是本地大模型比如 Ollama、vLLM、llama.cpp显存消耗来自模型推理进程而不是 LangGraph。观察 API 延迟每次 invoke 的总耗时可以用 time 包包裹。观察 token 消耗从模型返回的 usage 字段读取。观察并发性能用 instrumentation 记录每个节点的执行耗时。9.2 影响性能的关键因素因素影响模型调用次数每多一次模型调用就多一轮延迟工具节点数量工具执行越快整体越快图深度节点多、循环多执行路径变长并发策略并行分支比顺序执行快但要注意工具本身是否支持并发上下文长度消息列表越长模型推理越慢费用越高基础模型大模型延迟远高于小模型优先用 gpt-4o-mini 这类小模型测试9.3 如何降低资源开销限制递归步数compile(limit20)或设置步数计数器。精简消息上下文只保留最近 N 轮消息用trim_messages清理历史。优先用低成本模型做路由判断用强模型做最终生成。压缩工具描述工具描述过长会占用上下文影响 token 消耗。批量任务控制并发数避免触发 API 限流。10. LangGraph 常见问题与排查方法问题现象可能原因排查方式解决方案langgraph命令找不到没有安装 langgraph-cli运行pip install langgraph-cli安装 CLI 后重试启动langgraph dev报错找不到 graphlanggraph.json中 graphs 字段配置错误检查文件路径和导出变量名确认./graph.py:graph对应真实文件和导出对象invoke时报GraphRecursionError循环次数超过默认上限查看报错堆栈确认是否有节点反复执行加步数条件边或提高 limit模型没有调用工具模型不支持 function calling或工具描述不清晰打印state[messages]查看模型输出换支持 function calling 的模型优化工具描述API 返回 404assistant_id 和实际配置不一致检查langgraph.json中的 key使用配置文件中的图名称多轮对话状态错乱没有传 thread_id 或没有使用 checkpointer检查线程 ID 是否一致使用 checkpointer 保存状态工具节点执行结果没返回模型没有把 tools 节点连回模型节点检查边是否配置完整添加add_edge(tools, bot)中文输出乱码终端编码问题检查终端 UTF-8 设置Windows 下运行chcp 65001并行分支结果合并异常多个节点写同一个状态字段检查 State 字段类型是否支持合并使用不同字段或用 reducer 合并内存占用持续上涨消息列表无限追加检查 Prompt 中 messages 是否裁剪定期 trim 消息上下文10.1 一个实际的调试技巧当图执行结果不符合预期第一步不是改代码而是打印每步状态。可以在节点内加临时打印def bot(state: AgentState): print(bot state messages:, state[messages][-1]) result llm_with_tools.invoke(state[messages]) return {messages: [result]}把状态打印出来就能清楚知道模型返回了什么、工具节点拿到的输入是什么、为什么走到了错误的分支。11. 最佳实践与使用建议11.1 先从最小可运行配置开始第一次不要试图搭建完整的客服 Agent先用三个节点的无模型图跑通框架再逐步加入模型、工具和条件路由。保留一份starter.py内容是最简单的 StateGraph后续任何图有问题都可以回退到这份代码验证环境是否正常。11.2 目录结构建议langgraph-agent/ ├── .env # API Key 等环境变量不提交 git ├── .gitignore ├── langgraph.json # LangGraph 服务配置 ├── graph.py # 图定义入口 ├── nodes/ # 各节点核心逻辑 │ ├── __init__.py │ ├── router.py │ ├── tools.py │ └── generator.py ├── tools/ # 工具函数 │ └── calculator.py ├── test/ # 单元测试 │ └── test_graph.py └── requirements.txt工具函数、节点函数、图组装代码要分层不要全部堆在graph.py里。11.3 工具函数的工程化工具函数应该是独立的纯函数不直接依赖图状态。参数尽量使用基础类型方便模型理解。每个工具函数要有清晰的 docstring因为 LLM 会依赖工具描述来决定是否调用。错误处理不能只在图层面兜底工具函数自身也要 try-except。工具崩溃时返回错误信息字符串让模型有机会重新选择工具或向用户说明失败原因。11.4 合规与安全实践涉及真实用户数据必须做脱敏处理。Agent 对外的动作发邮件、改数据库、删文件必须加人工确认节点。任何自动审批逻辑都要记录日志审计可追溯。使用开源模型时确认模型的商用许可。对外提供服务时限制接口访问范围和并发量防止被滥用。12. 总结与下一步LangGraph 最值得尝试的点是它把智能体开发从“写一堆 if-else”变成了“画一张状态图”。一旦你熟悉了 StateGraph、节点、条件边这三个核心概念你会发现复杂的 Agent 流程开始变得可控、可测试、可扩展。建议你按这篇文章的顺序先在本地跑通“状态图 条件路由 工具调用”的最小例子再逐步加入子图、并行分支、持久化和 API 服务。最容易踩的坑是条件边配置错误和消息状态被覆盖把状态打印出来基本就能定位。接下来可以继续看 LangGraph 官方文档重点研究checkpointer的持久化方案、human-in-the-loop的人工打断流程、以及和 MCP 工具服务的对接方式。这篇教程可以作为一套起步路线图存好真正写代码时按章节去对照即可。