ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LangChain和LangGraph怎么选?从状态与循环看Agent开发核心差异

LangChain和LangGraph怎么选?从状态与循环看Agent开发核心差异

上周在社群里看到一位同学贴出的代码,他写了一整串LCEL代码,用RunnableLambda把好几个函数硬生生串起来,试图做一个"多步推理"的流程,最后跑来问我:"为什么我这段代码跑一遍就结束了?我想让它循环到答案满意为止。"

这个问题特别典型。它恰好戳中了langchain和langgraph的本质差距:langchain的设计目标是"把 LLM 调用编排成一条链",而langgraph的目标是"把 LLM 调用放进一张可循环、有状态、能持久化的图里"。如果你准备走 agent 应用开发学习路线,不把这两者的区别搞明白,后面学什么都容易绕弯路。

这篇内容我尽量用实操视角去拆,不搬官方文档。适合刚接触大模型应用的初学者,也适合已经搭过几个 demo、但被循环流程、记忆管理卡住的人。我会先讲清两个框架各自的定位,再讲决定性的技术差异,最后给你一份我自己梳理的学习路线。

1. 两个框架的本质定位:一条链和一张图

很多新人第一次接触langchain的时候,会看到一个很舒服的写法:

from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser prompt = ChatPromptTemplate.from_template("请把这句话翻译成英语:{text}") chain = prompt | llm | StrOutputParser()

这个写法本身没什么问题,它就是langchain的核心抽象:链(Chain)。你用|符号把 prompt、model、parser 串起来,数据沿着这条链路单方向流动,输入进去,输出出来,完事。

1.1 LangChain 的初心:简化 LLM 调用的组装

langchain解决的核心问题是"编排"。它想把"提示词模板、模型调用、结果解析、外部工具调用"这些零散的组件,变成可以像乐高积木一样拼装的东西。这也就是为什么它的核心叫 LCEL(LangChain Expression Language),用一套统一的接口把一切包装成 Runnable。

在这个阶段,它适用的场景非常明确:

  • 把一段 prompt 发给模型,拿到结构化输出;
  • 在调用模型之前先查一下数据库或调用某个 API;
  • 把多个模型调用按固定顺序串起来,比如先写大纲再写正文;
  • 简单 RAG,检索出来拼上下文直接回答。

这类需求的特点是:流程固定、没有来回、不需要在多次会话之间保留复杂状态。就好比你走进一间快餐店,下单、取餐、走人,一条直线走完,这就是langchain擅长的地方。

1.2 LangGraph 的诞生背景:链解决不了"回到起点"

但实际业务里,很多问题不是一条直线能走完的。我被问得最多的一种场景是:

我需要一个智能客服,它能先判断用户诉求,再查订单库,如果退款条件不足,还要追问补充材料,补充完之后重新评估,最后给出结论。

这种流程里有判断、有循环、有"走到某一步发现不对要回退"的情况。你用chain = a | b | c | d这种模式根本表达不了"如果条件不满足,跳回去重新走一遍"。

langgraph就是为这个而生的。它是 LangChain 官方团队在链式抽象之外推出的图编排框架,核心模型叫StateGraph。你别把它理解为"LangChain 的全新版本",它更像是 LangChain 生态里负责"流程控制"的那块拼图。

在langgraph里,你的基本单位是节点(Node)和边(Edge)。节点负责干活,边负责定义流程怎么走。干活的时候,你携带一个全局状态(State),每个节点都可以读状态、改状态,改完交给下一个节点。最关键的是,图允许你定义条件边,让流程自己决定下一步去哪,甚至可以形成环。

from typing import TypedDict, Optional from langgraph.graph import StateGraph, START, END class State(TypedDict): draft: str review_count: int approved: bool def draft_node(state: State) -> dict: return {"draft": "这是初稿内容", "review_count": state.get("review_count", 0) + 1} def review_node(state: State) -> dict: if state["review_count"] >= 3: return {"approved": True} return {"approved": False} def should_continue(state: State) -> str: if state["approved"]: return "end" return "review" graph = StateGraph(State) graph.add_node("draft", draft_node) graph.add_node("review", review_node) graph.add_edge(START, "draft") graph.add_edge("draft", "review") graph.add_conditional_edges("review", should_continue, {"review": "review", "end": END}) app = graph.compile()

这段代码里,draft和review两个节点形成环,review节点返回的状态决定流程是再来一轮还是直接结束。这是langchain的链式写法做不到的,也是两者最本质的差别。

1.3 版本号带来的认知混乱

很多人在学习时会被版本号搞得晕头转向。现在的langchain和langgraph已经分化为两个独立的 PyPI 包,版本迭代速度很快,API 也在持续调整。比如早期的langchain.agents还在用AgentExecutor那一套,现在官方主推的方式是用create_react_agent直接构建基于图的 agent。网上大量教程还在教旧的调用方式,你一搜langchain教程,搜出来的代码可能连langchain的版本都和你本地装的不一致。

我建议你记住一个判断方法:凡是看到教程里在讲"chain"、讲"pipeline"、大量使用RunnableSequence的,属于 LangChain 系;凡是看到"state"、"node"、"edge"、"graph"、"checkpointer"这些词的,属于 LangGraph 系。新手没必要把每个历史版本都吃透,抓住概念本质比背 API 重要得多。

2. 决定性的技术差异:状态、循环与可控性

如果只用一句话概括,我会说:langchain默认是无状态的线性管道,langgraph是有状态的图执行引擎。这个"有状态"三个字,是整个差异的核心。

2.1 显式状态:从"参数传递"到"全局状态"

用langchain写链的时候,数据怎么流动?从一个 Runnable 的输出直接流到下一个 Runnable 的输入。如果你想在中间某个环节保留一点额外信息,要么改造 prompt 模板,要么写一个自定义的RunnableLambda去手动传参。流程一复杂,你会发现参数的传递越来越乱。

langgraph里的State是一个你可以自定义的类,通常用TypedDict定义。整个图执行过程中,这个 State 对象会被持续传递。每个节点函数接收当前 State,返回一个字典作为增量更新,这些更新会合并进全局状态。

打个比方:langchain的链像流水线,每个工人只负责接住上一站传来的一个零件;langgraph的图像一个项目作战室,墙上挂着一张项目状态表,任何工种都可以看表、改表,改完留给下一环。

这个差异直接决定了你写多轮 agent 时的体验。比如让 agent 记录"用户已经提供了地址,还缺收货时间",在langchain里你大概率得把这些信息塞进一个自定义的 memory 对象里,还得小心处理链的输入输出。在langgraph里你直接在 State 里加两个字段就行,哪个节点需要哪个节点读,清清楚楚。

2.2 可循环的控制流:图的威力

LLM 应用和普通程序最不一样的地方是什么?是"不确定"。一次调用模型,你无法保证它一定给对结果。所以真实的业务逻辑里,经常需要"不满意就再来一遍"的循环。

langchain不是不能写循环,你可以用 Python 的for循环在外面包一层,每轮调用同一个 chain。但问题是,这个循环的"状态"你得自己维护,一旦某一步出错,想恢复现场、重新从某个中间节点开始,管理成本就很高。

langgraph把循环变成了图结构的一部分。它可以做到:

  • 条件边:根据当前 State 的内容决定下一步走向哪个节点;
  • 循环边:允许从 A 节点走回 A 节点,形成"自愈"流程;
  • 中断与恢复:配合 checkpointer,可以随时暂停图的执行,下次从暂停点接着跑。

我用过的一个真实案例是写稿 agent。第一版我用langchain写:写稿节点 -> 评审节点 -> 再写稿节点,我用for循环包了三轮,勉强能跑。但后来需求变成"评审不过就改,最多改五轮,用户中途还能插话改要求",这套逻辑就崩了。改用langgraph之后,我只需要设计一个review条件边,让流程根据评审结果自己决定是回到写稿节点还是走到结束节点,代码结构反而更简单了。

2.3 并发分支与图编译

langgraph还支持并行分支。比如说你让 agent 同时搜索三个不同来源的资料,各自成为一个分支,最后在汇总节点合并结果。这在langchain里要靠asyncio.gather手写并发,在langgraph里你可以用add_parallel_edge之类的 API 把多个节点挂到同一个前驱节点下,框架层面管理并发。

这里补充一个概念:langgraph执行前需要先compile(),这会让框架把整个图的拓扑结构预先检查一遍,确定哪些节点可以并行,哪些边有冲突。用一句话概括,相比起langchain的"解释执行"风格,langgraph更像一个"编译执行"的流程引擎。

对比维度langchainlanggraph
核心抽象链(Chain)/ LCEL图(StateGraph)
数据流单向线性有向图,可循环
状态管理依赖外部 Memory内置 State + Checkpointer
分支/并行需要手动编排图结构天然支持
循环控制需要外部循环条件边原生支持
适合场景固定流程、RAG、轻量工具多轮 agent、复杂流程、生产级应用
学习成本低中高

2.4 持久化记忆:LangGraph 的检查点机制

langgraph另一个杀手级特性是 checkpointer。它可以把每一次节点执行后的 State 快照保存到外部存储里,比如 SQLite、Postgres 或者 Redis。这意味着图在执行到一半的时候宕机了,你可以恢复到最后一次检查点,重新接管执行流程。

这个能力在做企业级 agent 的时候特别关键。试想一下:用户跟你的 agent 对话了四十多轮,突然网络断了。如果 agent 没有持久化状态,所有上下文都要靠前端重发;有了 checkpointer,你只需要存一个thread_id,下次用户回来就能无缝继续对话,像什么都没发生一样。

新手容易混淆的一点是:这里的"记忆"和你在langchain里用的ConversationBufferMemory不是一回事。checkpointer 是执行层的状态断点,不是单纯的对话历史记录。它保存的是整个图的 State 快照,包括流程走到哪一步、各节点中间产出是什么、当前变量值是多少。理解了这个区别,你才真正理解为什么生产级 agent 开发会优先选langgraph。

3. 实际选型:什么时候用 LangChain,什么时候用 LangGraph

我经常收到一个灵魂拷问:"现在大家都推 LangGraph,我还有必要学 LangChain 吗?"答案是:学,但重心要分清。它俩是配合关系,不是竞争关系。

3.1 优先选 LangChain 的场景

如果你的目标是把"一次模型调用"做得很顺滑,比如:

  • 做一个翻译工具,输入中文直接输出英文;
  • 做一个 RAG 问答机器人,查库、拼接、回答;
  • 做一个摘要提取服务,喂长文出短摘要;
  • 写一个调用外部 API 的简单工具 agent,流程固定,不需要多轮自我反思。

那langchain依然是最高效的选择。你甚至可以直接用prompt | model | parser三行代码搞定,没必要为了用图而用图。小工具杀鸡用牛刀,反而是给自己找麻烦。

我自己的习惯是:凡是能确定流程只有一条路径的,优先用 LCEL 写。它轻量、直观、调试起来极其方便,出问题打一行日志就能定位。

3.2 优先选 LangGraph 的场景

换几种情况,你就应该直接上langgraph:

  • 流程中存在"判断后再走"的节点(比如内容审核通过/不通过);
  • 需要多次循环调用模型,自己反思、修改、再验证;
  • 一个 agent 需要调用多类工具,并且工具调用结果会改变后续步骤;
  • 用户可能在多轮对话中随时打断、修改目标,流程需要动态响应;
  • 你希望具备断点续跑能力,图执行到一半能保存现场。

最常见的例子是create_react_agent。官方把 ReAct 模式的推理-行动-观察循环封装成了一个函数,你只需要提供模型和工具列表,剩下的循环控制交给图来跑。这种 agent 一旦落地,你会发现"让模型下次调用更好"这件事,本质上就是在一个循环里不断积累观察结果,再回到推理节点重新决策。这正是langgraph的长项。

3.3 混合使用:把 LangChain 组件塞进 LangGraph 的节点里

这里要重点澄清一个误解:用langgraph不是要你抛弃langchain。实际开发中,langgraph的每个节点内部完全可以调用langchain的模型、prompt、parser、retriever。图负责流程,链负责单步工作。

以我最近写的客服 agent 为例,结构大概是:

  1. 入口节点:接收用户消息,存入 State;
  2. 意图识别节点:内部用ChatPromptTemplate+llm+StrOutputParser这条小链去判断用户意图,返回分类结果;
  3. 分支节点:根据分类结果,走向"订单查询"子图或"人工介入"节点;
  4. 订单查询节点:调用工具去查订单 API,把结果写入 State;
  5. 人工介入节点:触发interrupt,等人工回复后再继续。

你看到了吗?第 2 步那个"小链",完全就是langchain的普通写法。langgraph负责的是外面这层大骨架,langchain负责的是骨架里每一块肌肉。只学langgraph不学langchain,你会发现自己连节点里最基本的 prompt 封装都写得别扭;只学langchain不学langgraph,你会发现自己永远被困在线性思维里,做不出真正有智能体感的应用。

4. 一份务实的 LangChain 到 LangGraph 学习路线

扯了这么多,我知道你真正想要的是路径。下面这条路线是我自己带了不少人跑过的,周期大概六到八周,前提是你每天能保证一两个小时的实战时间。不强求一口气看完,但每一步我都建议配上代码练习。

4.1 第一周:LLM 应用的基本功,也别急着啃 LangChain

很多人一上来就安装库开始抄代码,结果连"结构化输出""tool calling"是什么都没搞清。我的建议是第一周先不做框架,直接裸调 API,重点搞懂四件事:

  • prompt 基础,尤其是 system message、temperature 这些基础参数的作用;
  • 结构化输出:让模型返回 JSON,而不是纯文本;
  • tool calling:让模型自己决定"要不要调用某个函数",传入函数定义,拿到调用参数;
  • 流式输出:处理 token 流,理解stream和invoke的差别。

为什么先做这一步?因为langchain和langgraph只是工具,底子还是你对模型能力的理解。如果你连 tool calling 都不清楚,学langgraph的 ReAct 循环会非常痛苦。

顺便说一句,langchain入门教程确实多,但很多教程只是把 API 文档抄了一遍。我建议你选官方文档里的 "LangChain 0.x 新教程"来看,不要看那种整天教你"链模板写诗"的旧教程。那些东西学完除了能发朋友圈,对你的 agent 开发没有实际帮助。

4.2 第二三周:用 LangChain 把"小工具"做顺手

这时候可以开始装langchain了。目标不是什么高级功能,就是熟练使用这几个组件之间的搭配:

  • ChatPromptTemplate:管理人设提示和用户提示;
  • 各类模型封装:ChatOpenAI这类,记住一个抽象就是"model 也是 Runnable";
  • 输出解析器:StrOutputParser、JsonOutputParser、PydanticOutputParser;
  • 检索器:接一个向量数据库,做一个最简单的 RAG;
  • LCEL 的管道写法:prompt | model | parser,理解.invoke()、.stream()、.batch()。

这个阶段不要深挖"记忆"和"工具",更不要花大量时间去背旧版的AgentExecutor用法。把你写出来的代码当成一颗颗小小的积木,学会怎么捏积木就行了。等进了langgraph,这些积木会被直接用到图节点里。

我通常会给学员一个作业:用 LCEL 写一个"读后感生成器",输入一本书,输出包括核心观点、摘抄点评、推荐人群三部分的 JSON。代码量不大,但能逼你把 prompt 设计、结构化解析、流式输出全走一遍。

4.3 第四五周:切入 LangGraph,学核心三件套

进入langgraph那天,把之前的链式思维暂时放一放。单纯从学习角度,建议死磕三个概念,其他都先不管:

  1. State:你的数据模型。试着定义一个TypedDict,花点时间想清楚你的节点需要共享哪些字段。
  2. 节点函数:入参是当前 State,返回值是"增量更新"。养成一个习惯:节点函数里只干一件事。
  3. 边和条件边:add_edge管固定路线,add_conditional_edges管动态分流。搞清楚START和END的作用。

练手项目我推荐一个最简单但又最有代表性的:"循环写手"。设计三个节点:draft(写稿)、critic(点评)、revise(修改),然后让critic返回一个分数,分数低于 80 分就回到revise,高于 80 分才走到END。这个项目练完,你基本就掌握了图的感觉。

之后再上手一个更接近实战的项目:给一个create_react_agent配上 2 到 3 个自定义工具,比如"获取当前时间""查天气""计算器"。观察模型是怎么自主决定调用工具、拿到结果继续推理的。

4.4 第六周左右:状态持久化与人工介入

走到这一步,你已经能写"会自动循环的图"了。但这离生产还差关键一环,就是可恢复性。

学习 checkpointer,我推荐先从 SQLite 起步,因为零配置。你只需要在compile(checkpointer=...)时传一个持久化实例,然后在invoke时传一个config={"configurable": {"thread_id": "唯一ID"}},就能实现"按会话隔离状态"。试试:跑一张图,跑到一半停掉,再用同一个thread_id继续执行。你能直观看到"断点续跑"是怎么生效的。

接下来学interrupt和Command。这俩是人工介入的基础。举个生活化例子:一个机票预订 agent,在扣款之前必须停下来等用户确认。用interrupt可以暂停图,把人机交互插入流程中间,用户说"确认"之后图再从断点继续。没有这个能力,你的 agent 永远只敢在沙盒里玩,不敢接真实业务。

这部分内容对新手是有门槛的,但这是 agent 应用开发学习路线上绕不开的一环。以后你去面大模型应用岗位,十有八九会被问到"你的 agent 做到一半想让人干预怎么办"。

4.5 冲刺阶段:从 Demo 到生产

剩下时间建议你做一个完整的项目,把前面零散知识点连起来。我推荐的项目是"多工具协同的行业顾问 bot":

  • 用户提出一个问题;
  • agent 决定是否调用搜索工具、文档检索工具、计算工具;
  • 回答之前自动检查是否漏了关键信息;
  • 如果模型自己觉得信息不够,就继续追问或搜第二轮;
  • 全程把每次交互保存到数据库;
  • 支持用户中断后再次进入继续对话。

这个项目做完,你会碰到很多细节问题:怎么避免模型陷入死循环、怎么设定最大步数、怎么把长历史塞进 limited context、怎么给图加超时。这些问题都不是看文档能解决的,必须踩坑。到时候你可以体会到我前面说的那句话:langgraph是"编译执行的流程引擎",它的调试方式甚至有点像老派的后端服务,日志、状态快照、断点一个都不能少。

5. 我踩过的坑和一些反直觉经验

最后这部分,把你可能踩的坑提前说几个。字字都是实操换来的。

5.1 别再用链的思想写图

我见过最多的问题:有人把langgraph的节点函数写成了一个超级函数,里面又串了十几步LCEL,然后问我为什么图跑起来很慢。原因很简单,他把"一个节点"当成了"一条链"的容器,一个节点里干了几十件事,图的分步状态优势完全没发挥出来。

正确的做法是:一个节点只做一件完整的事。如果这个节点内部确实需要多个步骤,尽量在节点内部又用图或子图,而不是塞一个巨大的调用。这样你才能利用langgraph的检查点、断点、并行能力。

5.2 记忆不是你想的那种记忆

很多人学到"记忆"就以为是把所有的对话都塞到 prompt 里。这确实是老版本的思路,但它有两个硬伤:一是 token 太长,二是无关信息会干扰模型判断。

在langgraph的世界里,"记忆"要拆开看:对话历史用消息列表管理,该清空就清空,该摘要就摘要;流程状态用 State 管理;长期记忆用向量库或数据库管理。三种东西混在一起谈,越学越乱。学习的时候,用"数据库表"的思维方式去看状态管理,而不是用"聊天记录"的思维。

5.3 看教程要带"版权意识"

关于学习资源,我说句可能不中听的:现在搜出来的很多langchain中文教程、学习路线文章,发布时间差的可能有大半年。大模型圈半年意味着一个时代。你照着老教程去安装老版本 API,完全可能跑出一堆废弃警告。

我自己的习惯是:任何新框架都以官方文档和官方 GitHub 仓库的 examples 为基准,第三方博客只看思路不抄代码。遇到 API 调用不对,第一时间去翻官方 API Reference。尤其注意当前langchain和langgraph的版本号,很多接口名变了,比如langchain老版本和 0.3 版本相差很大。

5.4 从 LangChain 升级到 LangGraph,不是从"旧"升到"新"

最后说一个心态问题。langgraph不是langchain的"高配版",不是学会langgraph就不碰langchain了。纯 LLM 直接调用、简单 RAG 固定流程,老老实实写LCEL就够了;复杂 agent 编排、多轮循环、人工介入、生产级状态恢复,再上langgraph。两者是一套生态里的不同工具,没有绝对的高下。

我个人在实际操作中的体会是:现在学习的大方向确实已经全面倒向langgraph,几乎所有新出的 agent 框架都以图编排为核心,你不学好状态和循环,连别人的代码都看不懂。但真正写好、写熟练langgraph的人,没有一个会轻视langchain的基础组件积累。所以我的建议很直接:第一周练好裸模型基本功,第二三周快速过一遍 LangChain 组件,然后重心果断切到 LangGraph。别在链式思维里待太久,也别把 LangChain 跳过去。两条腿走路,才能走稳这条 agent 开发学习路线。

如果你正在个人项目里纠结这两个框架,记住那句最朴素的选择标准:流程是直线还是绕圈?直线用链,绕圈用图。等你想清楚这个问题,你的学习路线也自然清晰了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进