资讯动态

LangChain与LangGraph实战对比:RAG到Agent状态机开发进阶指南

发布时间:2026/10/27 23:37:33 来源:尧图企业网站定制
先说个背景我从 LangChain 0.1 时代就开始拿它写 RAG 流水线后来 LangGraph 一出来就跟着折腾到 0.4、0.5中间踩过的坑比文档里的示例多得多。最近半年被问得最多的问题就是LangChain 和 LangGraph 到底啥关系是不是学新的就行Dify、CrewAI 又在里面扮演什么角色这篇文章就一次性把这几件事讲透包括两者的本质区别、选型思路以及我认为最靠谱的一条学习路线。这篇内容不是概念科普而是基于实际项目经验的总结。适合刚接触 Agent 开发的工程师、正准备做技术选型的团队负责人以及被各种框架名词绕晕但想真正跑通一个项目的人。1. LangChain 和 LangGraph 到底差在哪先说结论LangChain 和 LangGraph 不是竞争关系而是底层与上层的关系。LangChain 是一套工具集LangGraph 是建立在这些基础组件之上的 Agent 运行时。你可以只用 LangChain 不带 LangGraph但只要你用 LangGraph 构建复杂 Agent底层几乎一定会用到 LangChain 的模型封装、工具接口和向量存储等组件。1.1 链式思维和图式思维的本质不同LangChain 的核心抽象是 Chain也就是一条直线管道。你定义prompt - model - parser然后数据顺着这条管道流下去一个萝卜一个坑前一个步骤的输出就是后一个步骤的输入。这种设计对 90% 的 RAG 场景完全够用检索、拼 prompt、调模型、解析结果一气呵成逻辑清楚调试也容易。但 Agent 场景有个硬需求循环。Agent 需要调用工具 - 拿结果 - 再决定下一步 - 再调用工具这种循环能力甚至还需要条件分支根据模型输出决定走 A 路线还是 B 路线。传统 Chain 面对这种需求只能靠工程师手写 while 循环代码很快就变成一团乱麻。LangGraph 的核心抽象是 Graph它是一个有向图。每个节点是一个函数或可运行组件节点之间有普通边和条件边数据在节点之间流动但可以绕圈跑、可以跳着走、可以停留等待人工介入。你可以把 LangGraph 理解成一个状态机框架它的执行模式不再是一条道走到黑而是按状态图规则不断跳转。1.2 代码层面的直观对比举一个最简单的例子让模型决定是调用搜索工具还是直接回答。用 LangChain 传统写法大概是# LangChain 传统写法示意 def agent_loop(query): messages [(user, query)] for _ in range(5): # 手写循环上限 response llm.invoke(messages tools_schema) if response.tool_calls: for call in response.tool_calls: result tools[call[name]].invoke(call[args]) messages.append(Message(tool_call_idcall[id], contentresult)) else: return response.content return 循环次数超限循环逻辑、状态维护、退出条件全部手写一旦分支变多这套代码维护成本就上来了。再看看 LangGraph 的标准写法from langgraph.graph import StateGraph, START, END from typing import TypedDict, Annotated from langgraph.graph.message import add_messages class AgentState(TypedDict): messages: Annotated[list, add_messages] def tool_node(state: AgentState): 执行上一次模型返回的工具调用 calls state[messages][-1].tool_calls return {messages: [execute_tool(call) for call in calls]} graph StateGraph(AgentState) graph.add_node(agent, call_model) graph.add_node(tools, tool_node) graph.add_edge(START, agent) def route(state: AgentState): if state[messages][-1].tool_calls: return tools return END graph.add_conditional_edges(agent, route, {tools: tools, END: END}) graph.add_edge(tools, agent)节点函数只管拿到 state、处理、返回新的 state 增量图机制负责调度、分支和循环。你不用自己写 for 循环不用到处传递临时变量状态变更在 state 里一目了然。这是两者最核心的差异LangChain 靠代码组织流程LangGraph 靠图定义流程。2. LangGraph 凭什么能管住复杂 Agent如果你想写一个真正的 Agent而不是一条固定的流水线langgraph 的三板斧是显式状态、图执行、持久化检查点。理解这三件事你就理解了它的设计灵魂。2.1 状态声明与更新机制add_messages 之类的基础LangGraph 的 StateGraph 要求你先声明一个 StateSchema通常用 TypedDict 定义。这个 State 会贯穿整个图生命周期每个节点函数接收的是当前 State返回的是 State 的增量Partial State框架负责把增量合并进完整状态。合并不是无脑覆盖而是交由 Reducer 决定。最典型的是消息列表如果一个节点向 messages 里加了一条消息另一个节点也加了一条直接覆盖肯定丢数据。所以add_messages这个 reducer 会把新消息追加进去并自动处理消息 ID 去重。class AgentState(TypedDict): messages: Annotated[list, add_messages] # 追加模式 query: str documents: list[str] retry_count: int final_answer: str这里有个容易踩坑的点字段没有指定 reducer 时默认就是覆盖模式。多个节点同时对一个普通字段写入后执行的会覆盖先执行的。如果这个字段同时被多个并行节点操作你就得自己定义合并逻辑。我一开始就吃过这个亏用普通字段存中间结果结果两个并行节点互相覆盖数据莫名其妙丢了一半。2.2 条件边和循环的实际价值条件边就是图里的决策器。你在某个节点结束后传给add_conditional_edges一个路由函数路由函数返回下一个节点的名字。这样就能实现模型说要调用搜索工具图就跳去工具节点模型说直接给答案图就走向 END。循环能力也因此变得很自然agent - tools - agent这条循环边让 Agent 可以反复调用工具直到模型认为信息足够。更重要的是你可以给循环加防御机制比如在 State 里放一个计数器和上限判断避免模型陷入死循环。这一点在真实项目中特别重要我见过不加防护的 Agent 因为单个工具反复失败一次任务烧掉几百次模型调用。2.3 Checkpointer 与可恢复执行LangGraph 真正区别于其他框架的地方是它把图执行的每一步状态都记录下来。这个机制叫 Checkpointing由 Checkpointer 组件实现。执行过程中每个节点运行完成后都会把 State 快照保存到存储后端比如 SQLite、Postgres 或内存MemorySaver。有了检查点你获得三个能力时间旅行随时回退到历史某个节点状态重新从那个点继续执行。这在调试幻觉问题、工具调用参数错误时极其有用。人工介入在图中用interrupt暂停执行等待人类审核或输入审核通过后再继续。这在金融审批、内容审核、需用户确认的 Agent 工作流中几乎是标配。容错恢复进程崩溃、网络故障后可以从最近的检查点恢复而不是整个任务重新跑。实践中的建议是测试阶段可以用MemorySaver图省事生产环境务必换成可以在进程间共享持久化存储的方案否则重启就前功尽弃。3. 选型大白话LangChain、LangGraph、Dify、CrewAI 到底该学哪个很多人一上来就想知道哪个框架最好我的答案从来都是脱离场景谈框架就是耍流氓。这四个东西的定位差异十分明显。3.1 四类框架的定位速查框架定位编程模式适合人群LangChainLLM 应用工具集链式管道LCEL/Runnable需要快速搭建 RAG、工具调用应用的开发者LangGraphAgent 运行时/状态机图结构显式状态与分支需要构建复杂多步骤、可审计、可恢复 Agent 的团队Dify低代码应用平台可视化编排 平台托管想快速出产品、不想写太多代码的业务团队CrewAI多智能体角色协作框架角色Role 任务Task 流程Process需要模拟团队协作、分工明确的多 Agent 场景3.2 实际项目中的选型参考我的经验是如果你的核心诉求是文档问答、资料库检索、简单的意图分类LangChain 就够了。它组件丰富文档处理、向量化、检索、提示模板都有现成方案学习成本低跑通一个 Demo 基本一个下午。如果你的 Agent 需要多轮工具调用、依赖前序步骤的中间结果、或者需要人工审核后再执行下一步那直接选 LangGraph。它把状态管理这件事做到位了代码的可维护性比手写循环高一个数量级。如果团队里大部分成员不是专业后端工程师或者项目以业务验证为核心目标Dify 这类低代码平台更高效。我个人用它做内部知识库工具比从零搭一套 LangChain 应用快得多。如果你想实验多个 AI 代理角色协作解决一个问题CrewAI 的上手体验比 LangGraph 轻。但它更像一个面向特定范式的封装遇到不匹配的场景反而束手束脚。另外强调一点LangChain 和 LangGraph 不是互相替代的关系而是互补。用 LangGraph 写复杂状态机的时候里面大量的节点任务依然要借助 LangChain 的 LCEL 组件来完成。所以我的结论是LangChain 是必修课LangGraph 是核心技能Dify 是效率工具CrewAI 是特定场景的补充。4. 一条从零到能干活的学习路线相信你已经发现直接扎进 LangGraph 文档容易被绕晕因为涉及概念太多StateGraph、Reducer、Checkpointer、interrupt、conditional edges……如果没有前置铺垫很容易中途放弃。我根据自己带新人的经验整理了一条相对平滑的路线。4.1 阶段一打地基先吃透 LangChain 基础用法目标能独立构建一个带 RAG 能力的问答应用。耗时约 3 到 5 天具体看你的 Python 基础。要掌握的内容PromptTemplate 和 ChatPromptTemplate 的用法如何调用 OpenAI、Anthropic、Ollama 等模型文档加载器 文本切分器 Embedding 向量库的基本流程LCEL 管道如何用|把组件串联成 RunnableTool 的基本定义方式理解函数如何被包装成工具这个阶段不需要碰 LangGraph。重点是理解LangChain 是一套可以独立使用的组件库这件事。我见过太多人直接学 LangGraph结果连Runnable接口都不清楚最后看文档非常痛苦。4.2 阶段二理解 Agent 模式再动手写一个 ReAct Agent目标明白 Agent 的内在逻辑。耗时 3 天左右。在这个阶段自己用纯 Python 手写一个 ReAct 循环即可甚至可以只用几十行代码实现把 prompt 喂给模型模型输出 Action你执行工具把 Observation 返回给模型如此循环。这个练习的意义在于让你真正理解工具调用Function Calling背后发生了什么。你不理解循环的本质就理解不了 LangGraph 为什么需要有向环。然后再用 LangChain 的 legacy Agent 接口跑通一个 Agent感受一下它和手写 ReAct 的区别。注意LangChain 老版 Agent 执行器已经逐渐被 LangGraph 生态取代所以不要在这里花太多时间跑通即可。4.3 阶段三进入 LangGraph先跑通最简单的有向图目标把一个流程从线性管道改写成图结构。耗时 3 天。跟着官方文档把入门示例写一遍定义一个 State定义两个节点用add_edge连接用compile()编译然后执行。再试着加一个条件边模拟模型决定是否调用工具。这个阶段要刻意练习的是把旧代码的 for 循环、if else 分支改写成节点 条件边。一开始会很别扭但这是思维方式转变的关键一步。每改完一个分支就在本地跑测试用例验证 State 的变化。4.4 阶段四拿下高阶必杀技能状态编程 检查点 人工介入目标能设计一个生产可用的复杂 Agent。耗时 1 到 2 周。重点攻克这些能力自定义 Reducer处理并行节点的状态合并配置持久化 Checkpointer从 SQLite 起步再试 Postgres用interrupt()实现人工审批流程用Command控制图内的动态跳转和提前终止掌握 LangGraph 的回放与调试手段LangSmith 是重要的辅助工具设计健壮的异常处理与循环次数上限这个阶段的建议找一个小而完整的业务场景练手比如带人工审批的报销单审核 Agent或多步骤资料收集 Agent。完成一个完整的生产级项目比看十篇教程都有效。5. 真实踩坑记录与排查思路最后分享我在实际项目里踩过的坑和对应的排查方法每条都是血的教训。5.1 消息列表越跑越短多半是 Reducer 没配对症状Agent 在第二轮循环时历史消息突然丢失模型像失忆一样。排查先看 State 定义里messages字段有没有声明Annotated[list, add_messages]。如果没有新节点返回的 messages 会直接覆盖旧值。我犯过一次手滑把add_messages漏了线上 Agent 表现忽好忽坏排查了一下午才发现。解决方案所有多节点共享的消息字段都必须使用add_messages或自定义合并函数否则就乖乖以不同字段名隔离数据。5.2 条件边不跳转先检查路由函数返回值是否匹配症状节点执行完毕图却报类似Can not reach END或直接停在原地。排查add_conditional_edges里的路由函数返回的字符串必须与后面的路径映射表的 key 完全一致。包括大小写、空格、下划线。我见过不少人把return Tool但映射表里 key 写的是tool结果永远匹配不上。解决方案定义路由路径映射时用常量而不是散落的字符串。例如ROUTE_TOOLS tools ROUTE_END end在路由函数里直接复用这两个常量从根上杜绝拼写不一致。5.3 检查点配置了却没效果Checkpointer 要在编译时传入症状配了MemorySaver重启进程后状态依然丢失甚至interrupt之后无法恢复。排查检查graph workflow.compile()这一步是否传入了checkpointer。很多人只定义了checkpointer MemorySaver()却忘了在compile(checkpointercheckpointer)里传参。另外每次执行都要用同一个graph实例去 invoke并传入独立的thread_id配置否则恢复时找不到对应会话。checkpointer SqliteSaver.from_conn_string(state.db) app workflow.compile(checkpointercheckpointer) config {configurable: {thread_id: request-123}} result app.invoke({messages: [(user, 帮我查一下报销政策)]}, config)5.4 图跑得慢成本翻倍往往是工具调用太频繁症状同样一个问题别人的 Agent 调用 2 次模型就完成你的却要调 5 次。排查观察路由日志看模型是不是每次都选择调用工具即便它已经掌握了足够信息。这既和 prompt 设计有关也和图的拓扑结构有关。解决方案三招。第一在 prompt 里明确仅在信息不足时调用工具第二给工具加权重或评分引导模型优先试用成本更低的工具第三设计验证节点在工具调用前先要求模型给出调用理由减少无效调用。有一次我把第三个策略用在一个内部客服 Agent 上模型调用次数降了 40%token 成本直接减半。5.5 版本兼容性是最隐蔽的坑LangChain 和 LangGraph 的 API 更新非常频繁我见过旧代码里StateGraph的导入方式变了invoke参数签名变了甚至整个ToolExecutor类都被废弃了。解决方案项目一开始就锁定版本号比如langchain0.3.x、langgraph0.4.x升级前先跑完整的回归测试。另外多学一点源码LangGraph 的开源代码结构本身比文档清晰看不懂文档时直接看类的定义往往能省很多时间。6. 我的最终建议从 LangChain 到 LangGraph 不是简单学一个新框架而是一次思维方式的升级从编写流水线到设计状态机。刚开始会觉得别扭但一旦你习惯用图来表达 Agent 的每一步你会发现在处理复杂、多步骤、需要人工参与的任务时代码的清晰度和可靠性都有明显提升。我的建议是先花一周把 LangChain 的 RAG 基础打牢再花两周硬啃 LangGraph 的小型实战项目之后遇到任何框架层面的困惑不要直接百度结论先翻官方文档里的 API 说明和源码再对照LangSmith 的追踪日志去查实。这个习惯能帮你避开 80% 的坑。最后分享一个小技巧学 LangGraph 时先别急着写代码先拿出纸笔把状态图草图画出来节点、边、条件分支都列清楚。代码永远是从图推导出来的不是边写边想出来的。这个习惯我从 LangGraph 0.2 用到现在每个复杂 Agent 都是这么落地的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑