标题党式的教程越来越多。随便打开一个技术平台都能看到“26年最新LangGraph智能体实战教程”“AI大模型/MCP/LangChain全流程”这类合集关键词堆得很满收藏数也很高。但真正开始学LangGraph的开发者往往会遇到一个尴尬环境装了一天示例代码跑通了下一步却不知道怎么用。因为LangGraph解决的从来不是“让模型能回答问题”而是“让一个复杂智能体流程变得可控、可分支、可循环、可维护”。这句话理解了后面所有组件和代码就好办了。我见过不少团队最初用LangChain做客服智能体链式调用在需求简单的时候非常清晰。可一旦加上“是否转人工”“是否需要调用订单接口”“是否还要追问用户”这些判断代码里全是if else状态散落在各个函数之间bug开始变多。后来转向LangGraph不是因为它在参数效果上有什么魔法而是因为它把流程本身变成了可以被定义、被调试、被测试的东西。1. 先搞清楚LangGraph解决的不是“调模型”而是“控制流程”1.1 从LangChain到LangGraph为什么多了一个“图”概念LangChain解决的是模型交互、提示词模板、文档加载、工具调用这些基础组件。可以把LangChain比作生产线上的一台台设备搅拌机、传送带、质量检测仪。LangGraph不是又来了一台新设备而是把设备连接成完整生产线的控制层。你可以在控制层里规定什么条件下走哪条支线产品不合格就送回返工合格就进入包装。很多教程会直接讲“LangGraph是LangChain的升级版”这个说法有误导性。它不是LangChain的替代品而是LangChain生态里的流程编排层。LangChain负责组件LangGraph负责流程。二者完全可以配合使用。LangChain里的Chain也可以连接多步但链是顺序的、固定的。你写了一条A到B到C的链它就会一直顺着跑下去。如果B的结果有两种可能一种走C一种走D链本身很难优雅表达。你可以写很多分支条件但维护起来像在一根水管上到处开阀门。LangGraph是图结构有循环、分支、并行可以保存中间状态每一步都像是被显式画在图纸上。1.2 一个最简单的例子看出链和图的区别假如你要做一个“先调用模型判断用户意图再决定是否转人工”的流程。用LangChain的链式写法大概是这样from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI prompt PromptTemplate.from_template(分析用户问题{question}输出意图) chain prompt | ChatOpenAI() | parser这条链只有一个固定方向输入问题输出意图。如果接下来要“根据意图决定走到哪个环节”链就开始别扭了。LangGraph的最小图则可以这样描述from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): question: str intent: str need_manual: bool def analyze(state: State) - State: # 实际这里是模型调用 state[intent] 退换货 state[need_manual] 退换货 in state[question] return state def manual_review(state: State) - State: state[intent] state[intent] 已转人工 return state def route(state: State): return manual if state[need_manual] else END graph StateGraph(State) graph.add_node(analyze, analyze) graph.add_node(manual, manual_review) graph.add_edge(START, analyze) graph.add_conditional_edges(analyze, route, {manual: manual, END: END}) graph.add_edge(manual, END) app graph.compile() result app.invoke({question: 我要退货}) print(result)重点不是代码本身而是它展示了一种变化流程不是被几个if写在函数里而是被拆成了“节点”和“边”。哪里需要判断哪里可以回头看哪里是出口都一目了然。1.3 智能体真正需要的三种能力LangGraph能够成为智能体开发的主流选择核心是它把下面三种能力做成了框架的基础设施而不是让开发者自己写循环。循环。智能体最常见的动作是“思考-行动-观察-再思考”。模型可能第一次调用工具拿不到足够信息需要再查一次数据库再计算一次。这样的循环如果写在普通函数里通常用while和一个计数器一旦轮次变多容易失控。LangGraph里可以让工具节点回到模型节点形成循环。同时通过递归限制控制轮数。条件路由。不是每条流程都该走到底。根据模型输出、工具结果、用户状态等条件决定走哪条分支这才是智能体灵活性的来源。LangGraph的add_conditional_edges就是为这个场景设计的。状态管理。整个流程中产生的用户问题、模型回答、工具调用结果、中间标记都需要有一个共享容器保存。LangGraph用State来承担这个角色它本质上是一个可以跨节点传递、可以被节点更新的数据结构。如果你自己写过一套Agent框架你会发现最难维护的往往不是模型调用而是这三件事。LangGraph把它们形式化了状态是明确定义的类型节点是纯函数边是显式声明。这也是它真正值得学的原因。2. 环境安装与最小实战从零跑通第一个“会决策”的图2.1 环境准备先隔离再动手Python建议使用3.10或更高版本。不要直接在全局环境里装我一般会建一个独立虚拟环境避免LangChain生态里常见的依赖版本冲突。python -m venv .venv source .venv/bin/activate # Windows 使用 .venv\Scripts\activate pip install --upgrade pip pip install langgraph langchain langchain-openai这里没有写具体版本号因为LangGraph和LangChain的迭代速度很快。你在某个时间点安装后的版本和半年后都会不一样。如果项目里遇到API差异优先去官方文档或pip show langgraph看当前版本。如果你只是本地验证不必着急装一大堆库。先安装上面三个再按需增加。装多了反而容易出现transformer、tokenizer版本冲突。2.2 配置模型访问LangGraph本身不绑定具体模型。你既可以用OpenAI也可以用本地部署的模型只要它提供OpenAI兼容接口。在本地设置环境变量export OPENAI_API_KEY你的密钥如果使用本地模型服务可能要额外设置Base URLexport OPENAI_API_BASEhttp://127.0.0.1:8000/v1密钥不要写死在代码里尤其是提交到Git仓库。可以使用.env文件配合python-dotenv加载。这不是LangGraph独有的事但做智能体项目时模型密钥会出现在不同节点和回调里一旦泄露损失比普通脚本大。2.3 最小可运行示例一个“会决定是否重复”的图很多人以为最小实例一定要接真实大模型。其实为了理解机制可以先用一个模拟函数替代模型调用把注意力放在图结构上。from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): question: str answer: str need_more: bool def fake_llm(state: State) - State: # 真实项目中替换为大模型调用 state[answer] f针对「{state[question]}」的模拟回答 state[need_more] len(state[question]) 5 return state def done(state: State) - State: state[answer] state[answer] 处理完成 return state def should_continue(state: State): return llm if state[need_more] else done graph StateGraph(State) graph.add_node(llm, fake_llm) graph.add_node(done, done) graph.add_edge(START, llm) graph.add_conditional_edges(llm, should_continue, {llm: llm, done: done}) graph.add_edge(done, END) app graph.compile() result app.invoke({question: 你好}) print(result)这个例子看起来简单实际上它把最重要的几个概念都带进来了State定义数据结构节点修改数据条件边决定下一步去哪个节点图最后可以编译执行。invoke传入的初始字典要和State结构兼容。返回的结果是整个状态字典你可以看到每一步的累积效果。注意不要一上来就把批量数和并发数拉满。先用一条样例确认输入、输出和日志都正常再考虑扩展。3. 核心组件拆解状态、节点、边、条件路由和子图3.1 State整个流程的“共享白板”State是LangGraph里最容易忽略但最关键的部分。它定义了流程运行过程中到底有哪些数据会在节点之间传递。实际开发中State通常是一个TypedDict。节点函数接收State返回一个更新后的字典。LangGraph会把返回值合并进原状态。设计State时我建议至少分几类字段用户输入问题、用户ID、会话ID。中间结果模型输出、工具返回、查询结果。控制标记是否需要工具、是否结束、当前状态。元数据时间戳、重试次数、成本占比。一个常见的坑是把所有内容都塞进一个messages字段。多轮对话时如果节点直接写state[messages] [new_message]历史消息就会被替换。正确做法是先读取原列表追加后再赋值state[messages] [*state.get(messages, []), new_message]State更新是浅合并不是深合并。如果你有一个嵌套结构比如state[meta] {count: 1}节点返回{meta: {count: 2}}它不会保留原meta里的其他字段而是直接替换整个meta字典。这一点非常容易踩坑。3.2 Node节点的职责边界节点是图里的最小工作单元。一个节点可以是大模型调用、工具执行、本地代码逻辑、外部API请求。它接收当前State返回部分更新。最好让一个节点只做一件事。比如“读取用户问题并调用模型”是两件事应该拆成“读取问题”和“调用模型”两个节点吗不一定要看是否需要中间状态被复用。但如果一个节点内部开始写各种if else图的优势就消失了。节点越纯粹越容易被单独测试和复用。我习惯这样设计节点命名用动词短语例如call_model、query_order、decide_route。节点返回值只包含真的要修改的字段。节点内部不要直接调用另一个节点函数。一个节点就是一个“操作员”它只负责自己的那一步操作。3.3 Edge顺序边、条件边和循环检测LangGraph里边的种类不多但理解它比理解节点更重要。顺序边最简单graph.add_edge(node_a, node_b)。表示node_a完成后走到node_b。条件边是智能体的核心graph.add_conditional_edges(node_a, route_function, mapping)。route_function会拿到当前State返回一个字符串keymapping里定义了这个key对应哪个节点。循环本质上也是边。让node_b回到node_a就形成了循环。但循环必须有限制否则模型会一直调用工具成本失控。LangGraph有recursion_limit通常默认是25。你可以通过app.invoke(input, config{recursion_limit: 50})调大但更应该设置业务上的最大轮数。比如在State里维护turn_count每循环一次加1超过阈值就让路由返回END。3.4 子图与并行分支子图就是把一张图作为一个节点放进另一张图。适合复用复杂流程。比如先做一个“用户意图识别”子图再把它接入“订单处理”主图。子图需要注意状态映射父图传给子图的字段子图处理完要返回哪些字段。不同的LangGraph版本处理方式有差异上手时可以先不用子图等流程确实重复了再抽取。并行分支适合“一次调用多个互不依赖的工具”。比如用户问“帮我查订单并算一下运费”你可以同时发起订单查询和运费计算等两个结果都回来后再走下一步。并行分支要特别注意状态写入冲突。多个并行节点同时修改同一个字段最终结果可能取决于执行顺序。我一般会约定并行节点只写入各自独立命名的字段最后在合并节点统一处理。3.5 记忆与断点从“单轮问答”到“多轮会话”很多人误以为LangGraph自带记忆。其实StateGraph本身只负责一次运行内的状态传递。跨会话的记忆要靠checkpointer实现。checkpointer可以把状态持久化到内存、SQLite或Postgres。有了它图可以在指定节点中断再从断点恢复。典型场景是模型调用工具后人工审核确认结果再继续下一步。还有一个重要概念断点不只是用来暂停还可以用来插入人工干预。比如自动化流程跑了一半需要运营人员确认LangGraph可以停在某个节点之前等外部信号再继续。记忆真正的价值是不需要把历史消息都塞进模型上下文而是只保留关键状态在需要时重建上下文。很多Demo把历史消息全拼在prompt里数据量一大成本和延迟都会上升。使用checkpointer后你可以控制什么留、什么去。4. 与MCP、LangChain生态组合让智能体真正“动手”4.1 MCP是什么为什么智能体需要它MCP的全称是Model Context Protocol一个开放协议。它要做的事是让智能体应用和外部工具、数据源之间形成标准连接方式。你可以把它理解成给所有工具做统一接口只要工具实现了一个MCP Server客户端就能通过同一套协议发现和调用它而不需要为每个平台单独写连接器。这里需要区分一个层次MCP不是模型能力也不是Agent框架它是连接协议。LangGraph负责流程LangChain负责组件MCP负责让工具“可接入”。在LangGraph里如果你的工具数量不多直接用LangChain的tool装饰器定义一个工具再交给ToolNode调用其实是最直接的方式。MCP的优势在工具数量多、需要跨平台复用时才更明显。4.2 agent skill 和 MCP 的区别社区里经常看到“Agent Skill”和“MCP”这两个词被放在一起比较。它们在两个维度上。MCP是底层协议解决的是“工具怎么暴露、怎么调用”。Agent Skill更偏上层能力封装比如“会搜索、会写代码、会做数据分析”这类技能组合它关心的是“任务怎么组织”。比较直接的理解是MCP告诉你怎么把一台设备插到通用电源上Skill告诉你把这台设备拿出来之后应该按什么流程完成一个工作。所以不需要纠结“哪个取代哪个”。在LangGraph里你可以用MCP连接外部工具也可以用LangChain技能库组织任务二者可以共同存在。4.3 在LangGraph里接入一个自定义工具以订单查询为例先定义一个工具函数from langchain_core.tools import tool from langgraph.prebuilt import ToolNode tool def get_order_status(order_id: str) - str: 根据订单ID查询物流状态。 # 这里调用真实的订单系统API return f订单 {order_id} 正在配送中 tools [get_order_status] tool_node ToolNode(tools)再把工具节点加进图里graph.add_node(tools, tool_node) graph.add_edge(model, tools) graph.add_edge(tools, model)这样就形成了一个“模型决定调用工具→执行工具→结果回到模型”的循环。实际项目中你需要确保工具服务可用、返回格式稳定。更多的坑发生在字段映射上工具返回的结果写进了state[messages]但模型节点只读了state里的其他字段结果就接不上。我建议在接入工具前先写一个最小测试单独调用工具函数确认输出结构。5. 实战中最容易踩的坑一套排查链路5.1 现象图不停循环、不结束这是新手最常见的报错也是LangGraph里最让人头疼的问题。先不要急着调整recursion_limit。先检查条件路由函数。它在每个循环里返回了哪个key是不是永远返回同一个分支比如一个路由函数本应判断“是否继续”但判断条件依赖的字段从未被更新那它就会一直回到模型节点。一个有效手段是在路由函数入口加日志打印关键字段。不要只在报错时看正常跑的时候也要看。看到每个循环的实际状态问题往往一眼就能发现。5.2 现象工具调用了但结果没有传回模型这种问题多数不是LangGraph错了而是节点之间的字段约定不一致。例如工具节点把结果放在state[tool_result]但模型节点的prompt模板只读取state[user_input]模型当然拿不到工具结果。修改方式不是调并发而是统一状态字段名或者在模型节点里把工具结果拼进当前消息。也要检查工具节点是否真的执行了。如果add_edge(model, tools)没有接好图会直接跳过工具节点。5.3 现象状态字段被覆盖或消息丢失在State更新时如果直接对列表字段赋值很可能把原来的数据覆盖掉。正确做法是先读取再更新current state.get(messages, []) state[messages] current [new_message]还有一个容易忽略的点如果返回值里只写了部分字段LangGraph默认是合并。但如果你返回一个完整字典里面把某个嵌套字段完全替换了那原始数据就会丢。所以修改嵌套结构时要先把原字段取出来做合并再放回去。5.4 排查顺序先分层再动手我建议按照下面的顺序排查LangGraph相关问题。不要一上来就怀疑框架。看现象是报错、超时、循环、空输出还是结果不符合预期看输入传给invoke的初始State是否完整字段名和类型是否符合定义看环境LangGraph、LangChain、模型SDK版本是否匹配API密钥和Base URL是否正确看参数recursion_limit、并发数、超时时间、工具连接参数是否合理看边界是否用了当前版本不支持的API流程是否太复杂本来就不适合一张图搞定大多数问题在第二步和第四步就能定位。如果是版本差异优先查官方文档的迁移说明不要靠猜。5.5 一个简化版检查表现象优先检查常见原因图不结束条件路由、recursion_limit路由永远返回某个分支工具结果无效状态字段、节点连接工具返回键和模型节点输入不匹配记忆不生效checkpointer配置没有持久化或会话ID不一致节点不执行边定义条件映射缺少目标节点速度越来越慢循环轮数和消息累积消息只增不减上下文过大6. 从Demo到工程化价值不在“跑通”而在“可维护”6.1 先跑通再谈优化很多刚接触LangGraph的开发者会恨不得把整个业务全画进一张图里十几个节点五六种分支加上子图嵌套。结果一运行就崩根本不知道哪里出了问题。更好的路径是先做一个最小流程只处理一条主路径。跑通后再加一个条件分支。验证稳定后再加工具调用。最后才上checkpointer、并行分支、MCP Server接入。每一步都保证当前版本可运行、可回滚。这样即使出了问题也知道是新加的那部分引起的。6.2 工程化需要的四块拼图单次Demo跑通只能说明流程没有断。真要放进生产环境还需要补四块能力。日志与追踪。每个节点开始和结束时记录输入输出摘要。给一次运行分配唯一请求ID。这样用户反馈问题后能通过请求ID找到那次运行的完整路径。状态持久化。把State保存到数据库。意外宕机后可以恢复也方便人工审计。不要以为智能体不需要审计只要有工具调用就可能产生资金、权限、隐私相关操作。权限与资源限制。工具不能无限制被模型调用。比如内部订单系统要限制查询范围、调用频率避免模型在循环里不断拉取敏感数据。还要限制单次运行的最大轮数和最大成本。测试与回归。智能体不是“写一次就完了”。模型版本更新、prompt调整、工具返回变化都会影响行为。准备一组固定输入和期望行为每次改版后都跑一遍防止旧功能悄悄坏掉。6.3 适用边界LangGraph不适合什么LangGraph的重点是复杂流程控制但它不是所有场景的最优解。如果只是固定上下文的问答用LangChain一个prompt链就够了不需要引入图。如果团队成员不熟悉状态机学习成本可能会被低估。如果业务的异常分支很少顺序执行更清晰。如果只是快速原型可视化智能体平台比如Dify这类平台可能上手更快至少团队里不一定人人都要写代码。LangGraph的优势在流程复杂度上来之后才明显分支多、需要循环、需要断点人工审核、需要持久化、需要和工具系统深度集成。这时代码化流程的版本可控、测试可写、复用性强的价值才会体现。选择工具不要看“哪个更火”要看自己的流程复杂度在哪里。一个工具解决不了所有问题能帮团队把复杂流程变得可控才是它的真正价值。6.4 最后的建议从一个最小条件路由开始如果你刚接触LangGraph我建议你不要照着大项目整段复制。先自己写一个只有两个节点、一条条件边的最小图一个节点生成一个值一个节点根据这个值决定继续还是结束。跑通后再把真实模型加进去接着加工具节点最后加记忆。重点不是记住API而是理解三件事状态是流程中的共享白板节点是白板上的操作员边是操作员之间的交接规则。当你能用自己的话把这句讲清楚你就已经入了门。LangGraph这个生态还在快速演进版本号会变API名会微调模型能力也在升级。但复杂智能体的核心问题——流程可控、状态可管理、工具可插拔——不会变。那些标题很满的教程可能收藏起来很爽但真正让你有底的永远是亲手跑通一个最小图再一点点把它养大。