LangGraph不是又一个模型调用库,它是把Agent应用流程画成状态图、再按图执行的编排框架。简单说,过去你用普通Python函数一步步把LLM、工具、文本处理串起来,现在你把每个处理步骤拆成“节点”,用“边”控制它们怎么流转,包括分支、循环、并行和子流程。这套思路适合已经写过几轮LLM调用、开始觉得代码难以维护的人,也适合想把RAG、工具调用、多轮记忆整合到一个可控流程里的开发者。最值得先了解的不是它有哪些API,而是它背后的状态机设计:每个节点只做一件事,状态集中管理,分支和循环都变成图上的显式路线。
下面按“先搞清关系→搭最小环境→吃透核心机制→跑一个能落地的Agent→排查问题→梳理学习路径”的顺序拆一遍。内容偏工程实践,不会只讲概念。
1. 先搞清楚LangGraph在LangChain生态里解决什么问题
1.1 LangChain和LangGraph的分工不同
LangChain本质上是一个组件集合,封装了模型调用、Prompt模板、输出解析、向量数据库、文档加载、工具调用这些高频动作。LangGraph则往上走了一层,它关心的是“这些组件按什么顺序执行、在什么条件下跳转、中间状态怎么保存”。可以这样理解:LangChain负责把单个零件做好,LangGraph负责把整条流水线装配起来。
很多人会问LangChain是不是过时了。这个问题其实不太准。LangChain作为组件库依然有用,很多LangGraph节点内部还是会用到LangChain的模型封装和消息结构。LangGraph并不是替代LangChain,而是在LangChain之上或旁边提供流程编排能力。如果你的项目只是单次问答,用LangChain链就够了;一旦出现多轮工具调用、条件分支、退出循环,直接写链式代码会越来越难维护,这时候才需要LangGraph。
1.2 LangGraph的能力边界:它不负责模型,只负责流程
LangGraph本身不会替你调用大模型,也不会决定用哪个模型。它提供的是状态管理和执行引擎。所谓状态,就是一个被图里所有节点共享的字典对象。每个节点读取当前状态,计算后返回需要更新的字段,LangGraph再把更新合并回状态里,然后根据边决定下一步执行哪个节点。
这意味着图里的节点可以是任意函数,里面可以做LLM调用、普通计算、数据库查询、HTTP请求、文件读写、工具调用。LangGraph只保证执行顺序和状态一致,不限制你在节点里做什么。这点很重要。很多人一开始以为LangGraph会自带Agent决策能力,实际上它只提供“框架”,真正让Agent聪明的还是模型、Prompt、工具和你的流程设计。
1.3 MCP在中间扮演什么角色
MCP是一个开放协议,目标是让LLM应用能够以统一方式发现和调用外部工具、数据源。它解决的不是流程控制,而是工具接入标准化问题。一个MCP Server把某个工具封装成标准协议,Agent通过协议去调用,不必给每个工具单独写一套集成代码。
所以LangGraph和MCP不是二选一的关系。LangGraph里可以写一个节点,这个节点去调用某个MCP Server暴露的工具;也可以让LangChain工具管理器去封装MCP工具。真正的流程控制仍然在LangGraph的图里。了解这个关系之后,学习路径会清楚很多:先学图结构,再学怎么把外部能力接进节点。
2. 环境准备与最小LangGraph应用
2.1 建议的Python环境和依赖
先准备Python环境,常见要求是Python 3.10及以上。用虚拟环境跑比较省心,避免和系统Python混在一起。依赖方面至少需要langgraph,如果计划接入大模型,还需要对应的模型SDK,比如langchain-openai,或者直接用openai库。安装命令很简单:
pip install langgraph langchain-openai如果你的机器网络下载慢,换成国内镜像源是常规操作。安装完成后,建议先跑一下版本确认:
python -c "import langgraph; print(langgraph.__version__)"能正常输出版本号,说明环境基本可用了。后面如果代码里某个API找不到,先确认你安装的版本是否比较新,不要一上来就怀疑代码写错了。
2.2 最小案例:节点、State和图
LangGraph的核心API围绕三类对象:State、节点函数、图。
State用TypedDict定义,表示整个流程共享的数据结构。节点函数接收当前state,返回一个字段更新字典。图中通过add_node注册节点,通过add_edge定义节点之间的连线。下面是一个最简单可运行的例子:
from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): input_text: str output_text: str def process_start(state: State) -> dict: return {"output_text": state["input_text"].upper()} # 创建图 graph = StateGraph(State) graph.add_node("process", process_start) # 连线 graph.add_edge(START, "process") graph.add_edge("process", END) # 编译 app = graph.compile() # 调用 result = app.invoke({"input_text": "hello langgraph"}) print(result)这个例子做了三件事:定义状态、注册一个处理节点、让流程从START进入节点再结束。执行结果是一个字典,包含input_text和output_text两个字段。先跑通这个最小案例,后面加分支、循环和外部调用时才不会被状态更新问题干扰。
2.3 为什么先跑最小案例而不是直接上复杂Demo
很多人学LangGraph时,第一件事就是找一个带Agent、工具调用、向量检索的完整Demo来跑。这个思路能让你快速看到效果,但一旦报错,你很难判断是环境问题、状态字段问题、还是模型调用问题。
我更建议把第一次测试拆成三步:先确认环境,再跑最小案例,最后才往里面加模型和工具。最小案例的价值在于验证三件事:依赖能正常导入、图能正常编译、invoke能正常返回。只要这三件事成立,后续绝大部分问题都能定位在业务逻辑和参数上。实测中很多“为什么模型没被调用”“为什么分支没走对”的问题,最后都回到最基础的图结构和state字段上。
3. 核心机制:节点、条件路由、循环、子图与并行分支
3.1 节点函数怎么写更清晰
节点函数是LangGraph的基本执行单元。它接收当前state,返回需要更新的字段。这里有一个很容易踩的坑:返回的内容代表“对状态的更新”,而不是“完整的新状态”。你可以只返回要改的字段,LangGraph会做合并。
def call_model(state: State) -> dict: return {"messages": state["messages"] + ["model reply"]} def tool_node(state: State) -> dict: return {"messages": state["messages"] + ["tool result"]}节点里不要写太多东西。一个节点只做一件事,比如“调用模型”“检索文档”“调用工具”“汇总结果”。因为LangGraph的优势就是让你用图的方式观察每个环节,节点里塞太多逻辑,图就退化成普通函数链了。
3.2 conditional_edge:条件路由怎么玩
条件路由是LangGraph最常见的进阶功能。它解决的问题是:下一步不一定固定,需要根据当前状态判断走哪个节点。实现方式是注册一个路由函数,这个函数读取state,返回某个节点名或节点key。
from typing import Literal def route_after_think(state: State) -> Literal["need_tool", "need_answer"]: if state.get("need_tool"): return "need_tool" return "need_answer" graph.add_conditional_edges( "think_node", route_after_think, { "need_tool": "tool_node", "need_answer": "final_node", }, )这里第二参数是路由函数,第三参数是路由结果到节点名的映射。注意映射的key必须和路由函数返回的字符串完全一致,否则会报找不到节点。
条件路由的意义在于:它把if-else从代码里提出来,变成图上可见的一条判断边。调试时只要看状态里哪些字段影响路由函数,就能还原决策路径。很多教程里专门拆开讲conditional_edge,确实值得重点练一遍,因为它是Agent能否灵活决策的关键。
3.3 循环:Agent为什么能反复调用工具
循环是LangGraph区别于普通链式调用的关键能力。典型场景是Agent循环:模型判断需要调用工具→执行工具节点→回到模型节点继续判断→直到模型给出最终答案。用图表示,就是从某个节点画一条边回到前面的节点,形成一个环。
# 简单示意:工具节点执行完回到模型节点 graph.add_edge("tool_node", "model_node")光有环还不够,必须考虑退出条件。否则模型一直判断“还需要工具”,流程就会卡死。LangGraph本身有递归深度限制。通过invoke参数可以传recursion_limit,也可以在自己维护的计数器中控制最大轮数:
result = app.invoke( {"input_text": "帮我查一下天气然后回答"}, config={"recursion_limit": 10} )更稳妥的做法是在循环中维护一个步骤计数,达到上限后强制进入最终输出节点。这里不要急着调大递归上限,因为卡死通常不是上限不够,而是退出条件没有设计好。
3.4 子图、并行分支与状态合并
当流程越来越长,可以把一部分逻辑抽成子图。子图本质是一个可以被整体当作节点来执行的小图。适合用来封装“检索+生成”“多轮对话”这类可复用流程。LangGraph官方文档和不少示例都支持这种组合方式。
并行分支则适合“同时处理多个相对独立的子任务”。从同一个节点分出多条边到不同节点,这些节点会并行执行,最后再合并到一个节点。并行分支的坑在于状态合并。多个节点同时往state里同一个字段写值,可能出现互相覆盖或行为不确定。建议让不同并行分支更新不同字段,最后用一个合并节点汇总。
并行很诱人,但不要一开始就上。先跑单条任务,确认每个分支单独执行都正确,再验证并发下的状态合并。很多看似“功能不支持”的问题,实际上是并行分支之间在互相覆盖返回值。
3.5 核心参数与判断标准
在学习和调参阶段,我会关注这几个点:
- recursion_limit:控制整条链路最多执行多少步,防止死循环。
- 并发参数:不同后端可能有不同并发配置,先小并发验证。
- checkpointer:是否保存会话状态,决定多轮对话是否记忆。
- debug开关:是否输出详细日志。
判断流程图跑得对不对,不是只看最终结果。更有效的指标是:每个节点的输入输出是否符合预期、路由函数走到了哪条分支、循环结束在第几步、状态里最终保留了哪些字段。把这些观察点记录下来,排查问题会快很多。
4. 把一个真正可用的Agent跑起来:记忆、RAG、MCP
4.1 多轮对话记忆和checkpointer
LangGraph的state默认只在一次invoke内有效。要让多轮对话记住历史,需要给图配置checkpointer。checkpointer是LangGraph里用于保存和恢复状态快照的组件。本地学习时可以用内存型实现,进程一重启就没了;生产环境需要换成文件存储或数据库存储。
配置方式一般是在compile时传入checkpointer:
from langgraph.checkpoint.memory import MemorySaver checkpointer = MemorySaver() app = graph.compile(checkpointer=checkpointer) # 多轮调用时传入同一个thread_id app.invoke( {"input_text": "你好"}, config={"configurable": {"thread_id": "session-1"}} )多轮对话的关键是thread_id。同一个thread_id下,后面调用能读到之前保存的状态。如果你没有传thread_id,每次调用会被当成新会话,记忆自然不生效。遇到“怎么没有记住刚才的话”时,第一时间检查的就是这个。
4.2 把RAG检索作为一个独立节点
RAG在LangGraph里可以很自然地拆成多个节点:用户输入→向量化→检索→组装上下文→生成回答。检索作为一个独立节点的好处是,你可以单独替换向量库、单独调试召回结果,甚至可以在检索后加一个“是否需要重新提问”的判断节点。
一个常见的误区是:把检索和生成写在一个节点里。短期看省事,但当你需要观察检索结果、调整Prompt、或者增加多轮检索时,就会后悔。LangGraph的价值就是让这些环节之间可以插入判断和循环,你只有把它们拆开,才能用上这种能力。
4.3 通过MCP接入外部工具
MCP的价值在工具数量多、工具类型杂的时候最明显。传统做法是给每个工具写一套专用调用代码,MCP把工具接入变成标准化握手:Agent通过MCP协议发现工具有哪些、看描述、用统一格式传参数、再接收结果。
在LangGraph图里,MCP工具通常不是图的顶层概念,而是被封装在一个工具调用节点里的执行能力。流程大概是:
- 在外部启动或配置一个MCP Server,它暴露工具的调用入口。
- 在LangGraph应用里配置MCP客户端,让Agent能发现工具。
- 在工具节点里根据模型返回的工具调用指令,执行具体工具。
- 把工具结果作为新消息写回state,回到模型节点继续判断。
这里的细节不少,但先把握住核心:MCP负责“工具怎么暴露、怎么调用”,LangGraph负责“什么时候调用、调用失败后怎么走、结果回到哪里”。两者结合,才能构成完整的Agent工具调用闭环。
4.4 MCP和Agent Skill到底有什么区别
这个问题热度很高。简单说,MCP解决的是工具接入的协议问题,是一个“接口层”;Skill(或者叫Agent Skill、技能包)解决的是任务执行模板问题,是一个“能力层”。
Skill通常包含:这个技能适用什么场景、需要哪些提示词、固定流程是什么、可能调用哪些工具、输出怎么整理。它关注的是“怎么把一个复杂任务拆成步骤并稳定复现”。MCP Server只是这个流程里可被调用的其中一个工具来源。两者可以配合:Skill定义任务怎么做,MCP Server提供Task里需要的工具。
如果一个人只装了MCP Server,但没有定义任何Skill,模型仍然可以按工具调用的方式去用它;如果一个人只写Skill,不通过MCP接入工具,他用普通函数调用也能实现。所以它们不是同一个层级的东西,不要混为一谈。
4.5 怎么验证Agent真的可用
一个Agent项目能跑起来,不代表可用。我建议用一组验收用例来判断:
- 普通问答:不调用工具,能否直接回答。
- 工具调用:问题需要查数据,能否正确选择工具并传参。
- 多轮对话:前一轮结果是否影响下一轮。
- 失败处理:工具返回错误或超时,流程能否降级。
- 长流程:超过10步时,是否稳定、是否出现状态漂移。
验收时不只看最终答案,还要看日志和中间状态。把这组用例跑完,基本就知道这个Agent离“能交付”还差多远。
5. 开发调试与排查链路
5.1 观察图的执行过程
LangGraph给了状态、节点、边这些概念,调试时就要用好它们。最简单的做法是在节点函数里打印当前state的关键字段,或者把中间结果写入日志。如果图比较复杂,可以输出图的Mermaid结构,直观确认边的走向。还有不少项目会接入可观测性工具,把每次调用的节点执行序列和token消耗记录下来,适合后期做性能分析。
学习阶段不用追求完整链路,至少保证两点:能看每个节点的输入输出,能看路由函数实际返回了哪个分支。有了这两点,绝大多数流程问题都能定位。
5.2 常见报错和排查顺序
我把LangGraph初学者最容易遇到的问题整理成一张表:
| 现象 | 优先排查点 | 补充建议 |
|---|---|---|
| 启动或导入报错 | Python版本、依赖版本、安装环境 | 用虚拟环境重装依赖,确认langgraph版本 |
| invoke后没有任何输出 | 图是否编译成功、输入state字段是否齐全 | 先跑最小案例,排除环境问题 |
| 报找不到节点 | 节点名拼写、conditional_edges映射key是否一致 | 用映射字典比对函数返回值 |
| 分支没走预期路线 | 路由函数读取的字段是否被更新 | 在路由函数里打印state |
| 模型调用失败 | API key、模型名、上下文长度、网络 | 先单独测试模型SDK调用 |
| 流程长时间不结束 | 退出条件、recursion_limit | 把递归上限调小,先暴露问题 |
| 工具没有被调用 | 工具描述是否清晰、参数Schema是否匹配 | 用最简工具先验证 |
| 并行结果丢失 | 多个分支写同一个state字段 | 给每个分支独立字段,最后合并 |
排查顺序我一般是这样:先看现象,再看输入,再看日志,再看代码逻辑,最后才怀疑框架本身。很多时候看起来像是框架的问题,实际是state字段没传对、路由key写错或者模型返回格式不符合预期。
5.3 从Demo到生产要注意的边界
本地Demo跑通之后,直接拿去生产会遇到另外一层问题:
- 持久化:内存型checkpointer不能用于生产,需要换数据库或对象存储。
- 超时:模型、HTTP工具、长流程都可能有超时,需要单独设置。
- 重试:工具调用失败后的重试策略要明确,否则会出现重复写入。
- 并发:批量和多用户并发时,资源占用会明显上升,先压测。
- 日志:要有能追踪单次完整链路的结构化日志。
这些不是LangGraph本身能帮你解决的,需要在使用它的应用层去设计。如果只是学习和实验,内存方式完全够用;如果是上线服务,这些问题必须在第一天就想清楚。
6. 学习路线:从入门到能自己画流程
6.1 分阶段突破
LangGraph学起来最忌讳一上来就啃全部概念。我建议按下面顺序推进:
- 最小案例:理解state、节点、边、invoke。
- 条件路由:用一个二选一分支练手。
- 循环:实现一个简化版Agent,让模型决定是否调用工具。
- 子图和并行:把检索、生成拆成子流程。
- 加记忆:用checkpointer实现多轮对话。
- 接真实工具:从普通HTTP接口开始,再到MCP Server。
每一步都保留一个可运行的最小项目,不要急着把所有功能堆在一起。我见过不少人一天之内把所有Demo都跑了一遍,但换一个新需求仍然不会设计图结构,就是因为缺少分阶段的独立思考。
6.2 常见误区
第一个误区是把LangGraph当成普通流水线,把所有节点都设计成“从上往下执行一次”。这样你用不到条件路由和循环,也无法体现LangGraph的价值。设计图之前,先想清楚你的业务有没有分支节点、有没有需要反复执行的环节、有没有需要并行的子任务。
第二个误区是混淆了流程和工具接入。MCP能解决工具接入标准化,但它不能替你设计Agent的决策链。LangGraph是编排引擎,MCP是工具来源之一,LangChain是组件库,三者的边界要先分清。
第三个误区是报错后乱改参数。例如循环卡住,第一反应是调大recursion_limit;内容不对,第一反应是换模型。实际上大部分问题要回到state、路由和工具返回格式上排查。先把日志和状态输出打开,再决定改哪里。
6.3 接下来研究什么
如果前面这些都已经掌握,可以继续往这几个方向深入:
- 多Agent协作:多个图或子图之间如何通信、如何分派任务。
- 人工确认环节:在关键节点插入人工审批,适合生产流程。
- 结构化输出:让模型返回规范JSON,并和state字段绑定。
- 流式输出:面向交互场景,逐步返回模型生成结果。
- 可观测性:把节点执行序列、耗时、token消耗统一记录下来。
这些方向在官方文档和社区示例里都有对应场景。学习时不要只盯着新的API,更要理解每个模式是为了解决什么业务问题。LangGraph本身就是一个流程设计的表达工具,能力上限取决于你对业务的分拆能力和对状态流转的理解。
我个人的建议是,不管你看的是哪种形式的教程,最后一定要自己动手画一张跟你业务相关的流程图出来。画不出来,就说明还没真正掌握。LangGraph最值得投入的时间,不在API记忆上,而在“把模糊需求拆成图结构”这件事上。先跑通单节点,再一步一步加条件、加循环、加记忆、加工具,这个过程本身就是最快速的入门路线。