LangChain与LangGraph:从链到图,大模型应用开发框架选型指南

发布时间:2026/10/20 15:20:18
LangChain与LangGraph:从链到图,大模型应用开发框架选型指南
1. 从「链」到「图」到底在说什么如果你最近在折腾大模型应用开发大概率会在各种技术社区里反复刷到两个词LangChain 和 LangGraph。前者是老牌选手几乎成了「LLM 应用框架」的代名词后者是这两年被讨论得越来越多的新面孔官方定位是「构建有状态、多角色应用的框架」。很多人第一次看到这俩名字的反应是这俩是不是一个东西的新旧版本是不是 LangGraph 要取代 LangChain我到底该学哪个我先把结论摆在前面省得你带着疑问往下读LangChain 和 LangGraph 不是替代关系而是互补关系它们解决的是两个不同层次的问题。LangChain 更像是一套「组件库 编排范式」它把模型调用、提示词模板、输出解析、检索、工具调用这些零件标准化让你能快速拼出一条处理流水线而 LangGraph 关注的是「流程本身怎么组织」尤其是当你的应用需要循环、分支、多角色协作、人工介入、状态持久化的时候用一条直线式的链去表达就会非常别扭这时候图结构就派上用场了。这个标题里「从链到图」四个字其实非常精准。链Chain的本质是有向无环的线性流程输入进来经过 A、B、C 几步输出出去一步接一步干净利落。但真实的大模型应用往往不是这样的。举个最典型的例子——一个能自己调用工具、根据结果决定下一步、失败了还要重试的智能体Agent它的执行路径是一个带环的图思考 → 决定调用工具 → 执行工具 → 观察结果 → 再思考 → 可能再调用工具 → 直到满足条件才输出。你用链去硬套要么写一堆嵌套判断要么把循环塞进某个节点内部代码很快就变成一团乱麻。LangGraph 就是为这种场景生的。所以这篇文章我想干的事很明确把这两个东西各自是什么、设计哲学差在哪、什么时候该用哪个、怎么配合着用一次讲透。我会尽量少堆概念多用「你实际写代码时会遇到什么」的视角来讲。不管你是刚接触大模型应用开发的新手还是已经用 LangChain 写过几个 Demo、现在被复杂流程折磨得头大的老手应该都能从里面找到对自己有用的部分。下面我会从整体设计思路开始拆然后深入到核心概念、实操代码、常见坑最后聊聊我自己的选型经验。2. 先搞清楚两者各自的定位与设计哲学2.1 LangChain 到底解决了什么问题要理解 LangChain得先回到它诞生的那个时间点。早期大家调大模型基本就是openai.ChatCompletion.create(...)一把梭提示词硬编码在字符串里返回结果自己解析要接个向量库就自己写胶水代码。项目一旦稍微复杂点就会发现大量重复劳动提示词模板要复用、模型要能切换、输出要结构化、检索逻辑要抽象……LangChain 干的就是把这些共性需求抽象成标准组件。它的核心抽象大概有这么几类我用大白话解释一下Model模型把不同厂商的模型调用统一成一个接口今天用 A 家的明天换 B 家的上层代码基本不用动。Prompt Template提示词模板把提示词变成带变量的模板支持复用和组合不用再手拼字符串。Output Parser输出解析器把模型返回的自然语言解析成结构化数据比如 JSON、列表、Pydantic 对象。Retriever检索器把「从知识库里找相关内容」这件事抽象出来屏蔽底层是向量库还是关键词检索。Tool工具把外部能力搜索、计算、查数据库包装成模型能调用的形式。Chain链把上面这些组件按顺序串起来形成一条处理流水线。你看LangChain 的强项是**「零件齐全、上手快」**。你想做个「文档问答」几行代码就能把「加载文档 → 切分 → 向量化 → 检索 → 拼提示词 → 调模型 → 解析输出」串起来。这种线性流程用 Chain 表达非常自然这也是它早期最受欢迎的用法。但问题也随之而来。当流程开始出现条件分支、循环、多轮状态累积的时候Chain 的表达能力就捉襟见肘了。LangChain 里虽然有各种「Agent」实现来支持工具调用循环但那些 Agent 的内部逻辑是框架写死的你想自定义一个「先查资料如果资料不够就换个关键词再查查够了再总结总结完让人类确认一下」的流程会发现非常难改因为它的控制流被封装在框架内部你只能通过配置去影响不能直接编排。2.2 LangGraph 的定位把控制流还给你LangGraph 的出现本质上是对上面那个痛点的回应。它的核心思想是把应用的执行流程显式地建模成一张图节点Node是具体的处理步骤边Edge是步骤之间的流转关系整个图的执行状态由一个共享的 State 对象承载。这个设计带来的最大好处是控制流完全透明、完全可编排。你想加个循环加一条从节点 C 回到节点 A 的边就行。你想根据模型输出走不同分支写一个条件边Conditional Edge根据 State 里的某个字段决定下一步去哪。你想在中间插一个人工审核环节加一个节点等外部输入再继续。这些在 Chain 里要费很大劲才能实现的东西在图结构里是「一等公民」。我特别喜欢用一个类比来解释这两者的区别LangChain 像是给你一套乐高积木还附了几张拼装图纸ChainLangGraph 则是给你一块底板和连接件让你自己决定积木怎么搭、哪里要循环、哪里要分叉。前者上手快照着图纸拼就行后者自由度高但需要你自己想清楚结构。还有一点很关键LangGraph 并不排斥 LangChain。它俩是同一个生态里的东西LangGraph 的节点内部完全可以调用 LangChain 的组件——用 LangChain 的模型封装、用 LangChain 的检索器、用 LangChain 的输出解析器。你可以理解为 LangGraph 负责「骨架和神经」LangChain 负责「器官和肌肉」。这也是为什么我说它们是互补而非替代。2.3 一张表看清核心差异光说可能还是有点抽象我整理了一张对比表把两者最关键的区别列出来你可以对照自己的场景看维度LangChain链式范式LangGraph图式范式核心结构有向无环的线性流水线可带环的有向图控制流框架封装配置为主显式编排完全可控状态管理步骤间传递较弱共享 State强状态循环支持弱需借助 Agent 内部逻辑原生支持分支支持有限条件边灵活人工介入较难原生支持中断与恢复持久化需自行处理内置检查点机制适用场景线性处理、快速原型复杂流程、多角色、Agent学习曲线平缓稍陡需理解图模型这张表不是要分出高下而是帮你建立「什么场景用什么工具」的判断力。接下来我会把每个关键点展开讲尤其是那些实际写代码时才会暴露出来的细节。3. 核心概念拆解链的组件与图的要素3.1 LangChain 的核心组件怎么用先说说 LangChain 里几个你一定会用到的组件我用一个「文档问答」的例子串起来讲这样比干讲概念好懂。假设你有一堆 PDF 文档想让用户提问模型基于文档内容回答。这个流程拆开就是加载把 PDF 读成文本。LangChain 有各种 Document Loader对应不同来源本地文件、网页、数据库。切分长文本要切成小块因为模型有上下文长度限制而且检索时小块更精准。这里涉及一个关键参数chunk_size和chunk_overlap后面会细讲。向量化把每个文本块转成向量存进向量库。这一步用的是 Embedding 模型。检索用户提问时把问题也转成向量在向量库里找最相似的几个块。生成把检索到的块和用户问题拼成提示词丢给模型生成答案。用 LangChain 写大概长这样伪代码重点是结构from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import FAISS from langchain.chat_models import ChatOpenAI from langchain.chains import RetrievalQA # 1. 加载 loader PyPDFLoader(doc.pdf) docs loader.load() # 2. 切分 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50 ) chunks splitter.split_documents(docs) # 3. 向量化 存库 embeddings OpenAIEmbeddings() vectorstore FAISS.from_documents(chunks, embeddings) # 4. 5. 检索 生成一条链搞定 qa_chain RetrievalQA.from_chain_type( llmChatOpenAI(modelgpt-4), retrievervectorstore.as_retriever() ) answer qa_chain.run(文档里讲了什么)你看RetrievalQA这一条链就把检索和生成串起来了非常省事。这就是 LangChain 的魅力常见流程有现成的链拿来即用。但注意这条链是单向的检索一次生成一次结束。如果检索结果不好它不会自动换个关键词再检索如果生成的内容需要人工确认它也没地方插。这些「如果」就是 LangGraph 的用武之地。3.2 LangGraph 的三大要素State、Node、EdgeLangGraph 的概念其实不多核心就三个State状态、Node节点、Edge边。把这三个搞明白基本就会用了。State是整个图的共享内存。它通常定义成一个字典或者带类型标注的结构所有节点都能读它、改它。比如一个问答 Agent 的 State 可能是这样的from typing import TypedDict, List class AgentState(TypedDict): question: str # 用户问题 messages: List # 对话历史 documents: List # 检索到的文档 answer: str # 最终答案Node是一个函数接收当前 State返回对 State 的更新。比如「检索节点」就是拿question去查库把结果写进documentsdef retrieve_node(state: AgentState): docs retriever.invoke(state[question]) return {documents: docs}Edge决定节点之间的流转。普通边是「A 执行完就去 B」条件边是「根据 State 里的某个值决定去 B 还是 C」def should_continue(state: AgentState): if len(state[documents]) 3: return retry # 文档不够回去重试 return generate # 够了去生成把这三个要素拼起来就成了一张图。你会发现「循环」和「分支」在图里是天然存在的——一条边指回前面的节点就是循环条件边就是分支。这正是 Chain 做不到的。3.3 为什么图结构更适合 Agent这里我想专门聊聊 Agent因为它是「从链到图」这个演进最典型的驱动力。一个能自主使用工具的 Agent它的执行逻辑本质是一个循环思考 → 行动 → 观察 → 再思考。用 ReAct 范式来说就是模型先输出一段「Thought」决定要不要调用工具如果要就输出「Action」指定工具和参数系统执行工具得到「Observation」把 Observation 塞回上下文让模型继续思考。这个循环什么时候停直到模型输出「Final Answer」。你想想这个结构用链怎么表达链是单向的你没法让它「回到上一步」。LangChain 早期的 Agent 实现是把整个循环写死在框架内部的你只能通过 prompt 和工具列表去影响它想改循环逻辑就得改源码。而 LangGraph 里这个循环就是明明白白的一条边think → (需要工具?) → act → observe → think ↓ (不需要) finish每个环节你都能控制想加个「最多循环 5 次就强制结束」的护栏加个计数器就行想在每次工具调用前让人确认一下加个中断节点就行。这种把控制权交还给开发者的设计是 LangGraph 最核心的价值。4. 实操用 LangGraph 搭一个带循环的检索 Agent光讲概念没意思我带你从零搭一个「会自我纠错的检索 Agent」把 LangGraph 的完整用法走一遍。这个 Agent 的逻辑是先检索文档如果检索到的文档太少说明关键词不好就换个方式重试最多重试两次够了就生成答案。这个「重试」就是循环是 Chain 很难优雅表达的。4.1 环境准备与依赖安装先把环境搭起来。我假设你已经有了 Python 环境用 pip 装依赖pip install langgraph langchain langchain-openai faiss-cpu这里解释一下几个包langgraph是主角langchain和langchain-openai提供模型封装和组件faiss-cpu是本地向量库方便演示生产环境可以换成别的。装完之后你需要配置模型访问的凭证这个按你所用服务的官方文档来配就行我这里不展开。提示依赖版本更新很快如果遇到 API 对不上的情况先确认版本。LangGraph 早期版本和现在版本的 API 有差异建议用较新的稳定版。4.2 定义 State 与节点函数第一步是定义 State。我把它设计得稍微完整一点方便你看清数据怎么流动from typing import TypedDict, List, Annotated from operator import add class RAGState(TypedDict): question: str documents: List[str] retry_count: int answer: str注意这里我用了retry_count来记录重试次数这是实现「最多重试两次」的关键。State 的设计原则是凡是节点之间需要共享、需要累积的信息都放进来。不要试图用全局变量那样会破坏图的可测试性和可恢复性。接着写节点函数。第一个是检索节点def retrieve_node(state: RAGState): query state[question] # 如果是重试换个检索策略比如放宽条件 if state[retry_count] 0: query query 相关背景 # 简单演示实际可以换关键词 docs retriever.invoke(query) return { documents: [d.page_content for d in docs], retry_count: state[retry_count] 1 }第二个是生成节点def generate_node(state: RAGState): context \n.join(state[documents]) prompt f基于以下资料回答问题\n{context}\n\n问题{state[question]} answer llm.invoke(prompt) return {answer: answer.content}第三个是判断节点它不修改 State只决定往哪走def decide_next(state: RAGState): if len(state[documents]) 3: return generate if state[retry_count] 2: return generate # 重试够了硬着头皮生成 return retrieve # 继续重试4.3 组装图并运行有了节点和判断逻辑就可以组装图了from langgraph.graph import StateGraph, END workflow StateGraph(RAGState) # 添加节点 workflow.add_node(retrieve, retrieve_node) workflow.add_node(generate, generate_node) # 设置入口 workflow.set_entry_point(retrieve) # 添加条件边从 retrieve 出来后根据 decide_next 决定去哪 workflow.add_conditional_edges( retrieve, decide_next, { retrieve: retrieve, # 回到自己形成循环 generate: generate } ) # generate 之后结束 workflow.add_edge(generate, END) # 编译 app workflow.compile() # 运行 result app.invoke({ question: 这个项目的核心技术是什么, documents: [], retry_count: 0, answer: }) print(result[answer])这段代码里最关键的是add_conditional_edges那一块。它告诉框架从retrieve节点出来后调用decide_next函数如果返回retrieve就回到检索节点这就是循环返回generate就去生成节点。一个带自我纠错能力的 Agent 就这么搭起来了而且整个控制流一目了然你想改重试策略、加节点、插人工审核都是加几行代码的事。4.4 加上持久化与人工介入LangGraph 还有两个很实用的能力值得单独说检查点Checkpoint和中断Interrupt。检查点机制让图的状态可以持久化。你只要在编译时传一个 checkpointer每次节点执行后的 State 都会被存下来。这带来两个好处一是断点续跑程序崩了能从上次的状态恢复二是支持多轮对话同一个会话用同一个 thread_id历史状态自动带上。from langgraph.checkpoint.memory import MemorySaver memory MemorySaver() app workflow.compile(checkpointermemory) config {configurable: {thread_id: user-123}} result app.invoke(initial_state, config)中断机制则允许你在某个节点前暂停等外部输入再继续。比如你想在生成答案前让人工确认检索到的资料对不对app workflow.compile( checkpointermemory, interrupt_before[generate] )运行到generate前会停下来你可以检查当前 State确认无误后再调用app.invoke(None, config)继续。这个能力在需要人工审核的场景比如客服、医疗、金融里非常关键而用纯 Chain 实现起来要费不少功夫。5. 常见问题与选型避坑指南5.1 到底该用哪个一份决策清单这是被问得最多的问题。我的建议是别纠结「哪个更好」而是问自己「我的流程长什么样」。下面这份清单你可以直接对照如果你的流程是一条直线输入 → 处理 → 输出中间没有回头路用 LangChain 的 Chain 就够了别上 LangGraph杀鸡用牛刀。如果你的流程有条件分支根据中间结果走不同路径两者都能做但 LangGraph 的条件边更清晰。如果你的流程有循环需要重试、迭代、多轮工具调用直接上 LangGraph别在 Chain 里硬凑。如果你需要多角色协作比如一个「研究员」查资料、一个「写手」写稿、一个「审核员」把关LangGraph 的多节点图天然适合。如果你需要人工介入或状态持久化LangGraph 的检查点和中断是现成的Chain 要自己造轮子。如果你只是快速验证想法先用 LangChain 把 Demo 跑通等流程复杂了再迁移到 LangGraph迁移成本没你想的高因为组件是通用的。我自己的习惯是原型阶段用 LangChain 快速试一旦发现流程里出现第二个「如果」或者任何「再来一次」就切到 LangGraph。这个切换点很好判断。5.2 实操中容易踩的坑坑一State 设计得太随意。新手常把 State 当成一个万能字典什么都往里塞结果节点之间职责不清调试时根本不知道哪个字段被谁改了。我的经验是State 的字段应该对应「流程中真正需要传递的信息」每个字段都要能说清楚「谁写、谁读」。另外如果多个节点会往同一个列表里追加内容比如对话历史记得用Annotated[list, add]这样的 reducer 声明合并方式否则后写的会覆盖先写的。坑二循环没有退出条件。图支持循环是好事但如果你忘了加「最多循环 N 次」的护栏一旦模型一直判断「需要重试」程序就会无限跑下去烧钱又烧时间。任何带环的图都必须有一个明确的终止条件这是铁律。我一般会在 State 里放一个计数器判断节点里先检查计数器。坑三把 LangGraph 当成 LangChain 的替代品。有些人一上来就用 LangGraph 重写所有东西结果发现简单的线性流程用图表达反而更啰嗦。记住图是为了解决「链解决不了的问题」而存在的不是所有场景都需要图。简单场景用简单工具这是工程判断力。坑四忽略检查点的存储成本。检查点会把每次状态都存下来如果 State 里塞了大对象比如整个文档内容存储会迅速膨胀。生产环境里State 里尽量只放必要信息大内容用引用比如文档 ID代替。坑五节点函数有副作用。节点函数最好是「纯」的——给定输入 State产出确定的更新。如果你在节点里做了不可逆的操作比如发邮件、扣款一旦图重跑或恢复可能会重复执行。这类操作要么放到图外要么做好幂等设计。5.3 一个常见报错速查表现象可能原因排查方向图跑起来直接结束入口节点没设或边没连对检查 set_entry_point 和 add_edge循环停不下来缺少退出条件检查判断节点的终止逻辑State 字段被覆盖没用 reducer 合并列表类字段加 Annotated恢复后状态不对thread_id 不一致确认 config 里的 thread_id中断后无法继续没传 checkpointer编译时加 checkpointer节点报 KeyErrorState 字段名拼错对照 TypedDict 定义检查这张表是我自己踩坑踩出来的遇到问题先对照着看能省不少时间。6. 我个人的一些使用体会聊了这么多技术和代码最后说点掏心窝的经验。我最早用 LangChain 的时候觉得它简直是神器什么都能几行搞定。但项目一复杂尤其是做那种「需要模型自己决定下一步」的 Agent就发现 Chain 的抽象开始漏水——你总得绕过框架去改一些它不让你改的东西。那种感觉就像你买了个组装好的家具想加个抽屉结果发现螺丝是焊死的。LangGraph 给我的感觉完全不同。第一次用它搭出一个带循环的 Agent看着那张图在脑子里清清楚楚地跑起来是有种「终于把控制权拿回来了」的爽感的。它不替你决定流程它只提供表达流程的工具。这种设计哲学上的差异比任何功能对比都重要。但我也要泼盆冷水LangGraph 不是银弹。它的学习曲线确实比 LangChain 陡图模型、状态合并、检查点这些概念需要花时间消化。如果你的应用就是简单的「检索 生成」硬上 LangGraph 只会增加复杂度。工具的价值在于匹配场景不在于先进。还有一个我反复强调的点别被框架绑架。LangChain 和 LangGraph 都是工具它们封装了很多东西但底层无非是「调模型、拼提示词、管状态、控流程」。理解了这些本质你用什么框架都能上手不理解本质换个框架照样抓瞎。我见过太多人把框架 API 背得滚瓜烂熟却说不清楚一个 Agent 为什么这么设计这是本末倒置。如果你现在正站在「学 LangChain 还是 LangGraph」的岔路口我的建议是先花两天把 LangChain 的核心组件用熟理解链式思维再花三天啃 LangGraph 的图模型动手搭一个带循环的小 Agent。两个都摸过之后你自然就有判断力了。纸上谈兵永远比不上亲手跑通一个例子。至于未来这两个东西大概率会继续共存、互相借鉴。LangChain 在往更强的编排能力走LangGraph 在补更丰富的组件生态。作为使用者我们要做的不是押注哪个会赢而是理解它们各自擅长什么在合适的场景用合适的工具。这才是工程师该有的态度。