LangGraph vs LangChain:为什么图编排才是 Agent 的未来?


关键词

LangChain, LangGraph, 大语言模型(LLM) Agent, 图编排(Graph Orchestration), 状态管理(State Management), 循环推理(Cyclic Reasoning), 可控性(Controllability)


摘要

在大语言模型(LLM)从“问答工具”向“自主决策者”进化的过程中,Agent 编排框架已经成为决定系统上限的核心技术之一。LangChain 作为早期的 LLM 应用编排霸主,凭借其链式(Chain)结构快速搭建了从简单提示到复杂工具调用的桥梁,但随着 Agent 场景从静态文本处理转向动态决策、多轮对话、循环纠错等高复杂度任务,链式的线性、单向、无记忆(或半记忆)缺陷日益凸显。

为解决这些问题,LangChain 团队在 2024 年初发布了完全重构的图编排框架 LangGraph:它抛弃了 Chain 那种“一条路走到黑”的思维,改用有向图(Directed Graph)的节点(Node)、边(Edge)、状态(State)三大核心组件,允许 Agent 拥有全局可访问的结构化状态灵活的循环/分支路径控制明确的终止条件,甚至可以构建子图(Subgraph)实现模块化复用——这恰好契合了人类解决复杂问题时的“发散-收敛-再发散-再收敛”循环推理模式。

本文将通过 “一步步思考”(STEP BY STEP REASONING) 的方式,对比 LangChain 与 LangGraph 的核心架构、原理、实现难度、适用场景,并通过多个真实代码案例(从简单的旅行规划 Agent 到复杂的多步代码生成与测试 Agent),直观展示图编排的优势;最后,我们将分析 Agent 技术的发展趋势,论证为什么图编排是未来 3-5 年 LLM 应用开发的标准范式。全文约 12000 字,适合有基础 LangChain 使用经验的开发者、LLM 产品经理以及对 Agent 技术感兴趣的技术爱好者阅读。


1. 背景介绍

1.1 主题背景和重要性

1.1.1 从 LLM 到 Agent 的第三次范式革命

大语言模型的发展可以清晰地分为三个里程碑式的阶段:

  1. 文本生成阶段(2022.11-2023.06):以 ChatGPT 发布为标志,LLM 主要用于“一次性文本生成”——问答、翻译、写文案,核心逻辑是“输入→LLM 单次推理→输出”,几乎不需要复杂的编排。
  2. 工具调用与链阶段(2023.06-2023.12):随着 GPT-4、Claude 2 等具备工具调用能力的模型发布,LLM 开始“走出文本世界”——调用搜索引擎查实时信息、调用数学库计算、调用 API 操作数据,LangChain 的 Chain 结构 成为了串联这些“离散步骤”的事实标准:从 LLMChainRetrievalQAChain 再到 ConversationalRetrievalChain,开发者可以像拼乐高一样快速搭建应用。
  3. 自主决策与图阶段(2024.01-至今):但当我们需要 Agent 完成更复杂的任务时——比如“用 Python 爬取 10 条最近的 AI 新闻,分析每条新闻的情感和核心观点,生成一份 500 字以内的日报并发邮件给我”——Chain 结构就暴露了致命问题:
    • 线性单向:不能循环爬取错误的新闻源、不能循环修改有问题的代码、不能循环分析情感直到置信度达标;
    • 状态混乱:半记忆的 ConversationBufferMemory 只能存对话历史,无法管理结构化的中间结果(比如爬取的新闻列表、情感分析的置信度阈值、待发邮件的收件人);
    • 终止条件模糊:Chain 结构要么“走到最后一个节点自动结束”,要么“必须手动设计复杂的条件判断链”,但复杂任务的终止条件往往不是线性的(比如“只要生成的日报符合字数要求且情感覆盖了所有正面/负面/中性新闻,就结束;否则循环修改”)。

在这个阶段,图编排 凭借其天生的灵活性和可控性,开始取代 Chain 成为 Agent 开发的核心框架。根据 LangChain 官方 2024 年 6 月的统计数据,LangGraph 的周下载量已经从年初的 1000+ 飙升至 100,000+,超过了 70% 的 LangChain 活跃开发者已经开始尝试使用 LangGraph。

1.1.2 Agent 市场的爆发性增长与核心痛点

根据 Gartner 2024 年的预测,到 2027 年,全球 80% 的企业将部署至少 1 个 LLM Agent,市场规模将从 2023 年的 120 亿美元增长到 1.2 万亿美元——这意味着 Agent 技术将在未来 3 年内实现 100 倍的增长

但在这个爆发性增长的背后,开发者面临着三大核心痛点:

  1. 任务复杂度失控:随着任务步骤的增加,Chain 结构的代码会变得极其臃肿和难以维护——一个包含 5 个分支、3 个循环的任务,用 Chain 结构写需要至少 2000 行嵌套代码,而用 LangGraph 只需要 500 行左右;
  2. 可靠性不足:LLM 的推理结果具有一定的随机性(Temperature>0 时),Chain 结构无法对中间结果进行验证和纠错——比如如果 Agent 调用的数学库返回了错误的结果,Chain 结构只能继续往下走,最终导致整个任务失败;
  3. 可解释性差:当 Agent 执行失败时,Chain 结构很难快速定位问题出在哪里——只能通过打印中间变量来排查,但中间变量分散在各个 Chain 节点中,难以追踪;而 LangGraph 的图可视化功能可以直观地展示 Agent 的执行路径,帮助开发者快速定位问题。

1.2 目标读者

本文主要面向以下三类读者:

  1. 有基础 LangChain 使用经验的开发者:已经用 LangChain 搭建过简单的应用,但在开发复杂 Agent 时遇到了困难;
  2. LLM 产品经理:需要了解 Agent 技术的发展趋势,以便设计出更符合用户需求的产品;
  3. 对 Agent 技术感兴趣的技术爱好者:想要从零开始学习 Agent 开发,选择合适的编排框架。

为了让所有读者都能理解,本文会尽量使用通俗易懂的比喻,避免过多的专业术语,必要时会对术语进行解释。

1.3 核心问题或挑战

本文将围绕以下 四个核心问题 展开:

  1. 什么是链式编排?什么是图编排?它们的核心区别是什么?
  2. LangChain 的 Chain 结构有哪些优势和劣势?为什么它不再适合复杂 Agent 开发?
  3. LangGraph 的核心架构和原理是什么?它是如何解决链式编排的痛点的?
  4. 为什么图编排是 Agent 的未来?未来 3-5 年 Agent 技术会有哪些发展趋势?

2. 核心概念解析

2.1 链式编排:从“接力跑”到“单行道”

2.1.1 生活化比喻:接力跑运动员

我们可以把 链式编排(Chain Orchestration) 想象成一场 “固定路线的接力跑比赛”

  • 每个 Chain 节点(Node) 就是一个 接力跑运动员
  • 每个运动员只能做 一件固定的事情(比如第一个运动员负责“收集用户输入和对话历史”,第二个负责“生成检索词”,第三个负责“从知识库检索文档”,第四个负责“生成答案”);
  • 每个运动员只能把自己的输出 原封不动或简单包装后 传递给 下一个固定的运动员
  • 整个比赛的路线是 完全固定的——不能跳过某个运动员,不能让某个运动员多跑几次,不能改变运动员的顺序;
  • 比赛的终止条件是 最后一个运动员冲过终点线——不管中间有没有出问题,都必须继续跑。
2.1.2 概念结构与核心要素组成

链式编排的核心结构非常简单,只包含 两个要素

  1. 节点(Node):执行具体任务的单元——可以是 LLM 调用、工具调用、数据处理等;
  2. 边(Edge):连接节点的单向箭头——表示“前一个节点的输出是后一个节点的输入”。

我们可以用一个简单的文本示意图来表示链式编排:

用户输入 → [收集对话历史节点] → [生成检索词节点] → [检索文档节点] → [生成答案节点] → 输出答案
2.1.3 LangChain 链式编排的早期形态

LangChain 的链式编排最早是从 LLMChain 开始的——它是最简单的链式结构,只包含“用户输入处理”和“LLM 调用”两个节点。随着功能的增加,LangChain 又推出了一系列预定义的 Chain:

  • RetrievalQAChain:用于检索增强生成(RAG);
  • ConversationalRetrievalChain:用于多轮对话的 RAG;
  • SequentialChain:用于串联多个自定义 Chain;
  • RouterChain:用于简单的分支控制(但分支后仍然是线性的)。

2.2 图编排:从“单行道”到“迷宫探索者”

2.2.1 生活化比喻:带地图和背包的迷宫探索者

我们可以把 图编排(Graph Orchestration) 想象成一个 “带地图、带背包、可以自主决定路线的迷宫探索者”

  • 每个 图节点(Node) 就是迷宫里的 一个房间——每个房间都有特定的功能(比如“信息收集室”、“决策室”、“工具调用室”、“验证室”、“修改室”、“出口室”);
  • 每个 图边(Edge) 就是连接房间的 一扇门——门可以是单向的也可以是双向的(但在实际开发中通常用有向边,因为双向边可以用两个单向边代替),门旁边可能有 条件标志(比如“只有当验证通过时才能进入出口室,否则进入修改室”);
  • 探索者的 背包(State) 就是 全局可访问的结构化状态——里面可以装任何东西(比如用户输入、对话历史、检索到的文档、中间结果、验证结果、循环次数、置信度阈值等),探索者在任何房间都可以从背包里拿东西,也可以往背包里放东西;
  • 探索者的 地图(Graph Schema) 就是 图的结构定义——探索者可以根据地图自主决定走哪扇门,甚至可以循环进入同一个房间多次;
  • 探索者的 终止指令(Termination Condition) 就是 出口室的钥匙——只有当背包里的某个东西满足特定条件时(比如“验证通过”、“循环次数超过 5 次强制退出”),探索者才能拿到钥匙,打开出口室的门。
2.2.2 概念结构与核心要素组成

与链式编排不同,图编排的核心结构包含 五个要素(比链式多了三个关键要素):

  1. 节点(Node):执行具体任务的单元——和链式类似,但节点可以访问和修改全局状态;
  2. 边(Edge):连接节点的有向箭头——分为 无条件边条件边(Conditional Edge),条件边可以根据全局状态决定走哪条路径;
  3. 全局状态(State):贯穿整个图执行过程的 结构化数据容器——是图编排与链式编排最大的区别;
  4. 入口节点(Entry Point):图执行的起点——通常是一个专门处理用户输入并初始化状态的节点;
  5. 终止条件(Termination Condition):图执行的终点判断规则——可以是“到达某个特定节点”,也可以是“状态满足某个条件”。
2.2.3 全局状态的重要性

为什么全局状态如此重要?我们可以用一个简单的例子来说明:
假设我们要开发一个 “多步数学计算 Agent”——任务是“计算 1 + 2 * 3 - 4 / 2 的结果”。

如果用 链式编排,我们需要把任务分解成线性的步骤,但 1 + 2 * 3 - 4 / 2 的运算顺序是“先乘除后加减”,所以链式结构只能是:

用户输入 → [解析运算顺序节点] → [计算 2*3=6 节点] → [计算 4/2=2 节点] → [计算 1+6=7 节点] → [计算7-2=5 节点] → 输出结果

但如果用户输入的是更复杂的表达式,比如“(1 + 2) * (3 - 4) / 2”,我们需要重新设计整个链式结构——非常麻烦;而且如果中间某个计算节点出错了(比如把 2*3 算成 5),链式结构无法验证和纠错。

如果用 图编排,我们只需要定义一个 全局状态(比如 { "expression": "(1 + 2) * (3 - 4) / 2", "current_step": 0, "intermediate_result": None, "error": None, "done": False }),然后定义几个节点:

  1. 入口节点:解析用户输入,初始化状态;
  2. 解析下一个运算节点:从状态中取出 expression,解析出优先级最高的子表达式;
  3. 计算子表达式节点:计算子表达式的结果,更新状态中的 intermediate_resultexpression
  4. 验证结果节点:检查子表达式的计算结果是否正确(比如调用另一个 LLM 验证,或者调用数学库验证);
  5. 条件边判断
    • 如果验证通过且 expression 只剩一个数字,设置 done=True,进入终止节点;
    • 如果验证通过但 expression 还有运算,回到解析下一个运算节点;
    • 如果验证不通过且循环次数 < 3,回到计算子表达式节点;
    • 如果验证不通过且循环次数 ≥ 3,设置 error,进入终止节点;
  6. 终止节点:输出结果或错误信息。

可以看到,图编排的结构非常灵活——不管用户输入的表达式有多复杂,都不需要重新设计图的结构;而且还可以对中间结果进行验证和纠错,大大提高了 Agent 的可靠性。

2.3 链式编排 vs 图编排:核心属性维度对比

为了更直观地对比链式编排和图编排的区别,我们可以用一个 markdown 表格 来展示它们在 10 个核心属性维度 上的表现:

核心属性维度 链式编排(Chain) 图编排(Graph)
结构灵活性 极低——只能是线性或简单的分支后线性,不能循环,不能改变顺序 极高——可以有任意数量的节点、边、分支、循环、子图,结构完全自定义
状态管理 半记忆——只能用 Memory 类存储非结构化的对话历史,无法管理结构化的中间结果 全局结构化状态——可以存储任何类型的结构化数据(列表、字典、对象等),所有节点都可以访问和修改
终止条件控制 模糊——要么走到最后一个节点自动结束,要么必须手动设计复杂的条件判断链 明确——可以是“到达某个特定节点”,也可以是“状态满足某个条件”,甚至可以是两者的结合
中间结果验证与纠错 几乎不可能——没有明确的机制来验证中间结果,只能继续往下走 非常容易——可以专门设计验证节点和循环路径,对中间结果进行多次验证和纠错
可解释性 较差——中间变量分散在各个节点中,难以追踪执行路径 极强——可以通过图可视化功能直观地展示 Agent 的执行路径、中间状态、决策依据
可维护性 低——任务步骤越多,代码越臃肿,嵌套越深,修改成本越高 高——可以将复杂任务分解成子图(模块化),每个子图只负责一个小功能,修改成本很低
可复用性 中——预定义的 Chain 可以复用,但自定义的 Chain 复用性较低,因为耦合度太高 极高——子图可以像函数一样被复用,节点也可以被多个图复用
适用场景 简单的静态文本处理任务——问答、翻译、写文案、一次性 RAG 所有类型的 Agent 任务——静态文本处理、动态决策、多轮对话、循环纠错、多工具协同、子任务分解
开发难度 低——入门非常容易,只要会拼预定义的 Chain 就能快速搭建应用 中低——入门比链式稍难(需要理解状态管理和条件边),但开发复杂任务的难度比链式低很多
性能开销 低——没有复杂的状态管理和路径判断机制 中低——有一定的状态管理和路径判断开销,但完全在可接受范围内,而且可以通过优化状态来降低开销

2.4 链式编排与图编排的 ER 实体关系图与交互关系图

为了更清晰地展示链式编排与图编排的概念之间的关系,我们可以用 Mermaid 架构图 来绘制它们的 ER 实体关系图交互关系图

2.4.1 链式编排的 ER 实体关系图

触发执行

包含

读取/写入

调用(可选)

调用(可选)

存储

USER

CHAIN

NODE

MEMORY

TOOL

LLM

MESSAGE

ER 实体关系图说明

  • USER:触发 Chain 执行的用户;
  • CHAIN:链式编排的核心实体,包含多个 Node;
  • NODE:执行具体任务的单元,可以读取/写入 Memory,可以调用 Tool 或 LLM;
  • MEMORY:半记忆的非结构化数据容器,存储多个 Message;
  • MESSAGE:对话历史中的单条消息(用户消息或 AI 消息);
  • TOOL:外部工具(搜索引擎、数学库、API 等);
  • LLM:大语言模型。
2.4.2 图编排的 ER 实体关系图

触发执行

包含

包含

管理

包含(可选)

读取/修改

调用(可选)

调用(可选)

连接

包含(可选)

包含

包含

共享/局部(可选)

USER

GRAPH

NODE

EDGE

STATE

SUBGRAPH

TOOL

LLM

CONDITION

ER 实体关系图说明

  • USER:触发 Graph 执行的用户;
  • GRAPH:图编排的核心实体,包含多个 Node、Edge、可选的 Subgraph,管理全局 State;
  • NODE:执行具体任务的单元,可以读取/修改 State,可以调用 Tool 或 LLM;
  • EDGE:连接 Node 的有向箭头,可选包含 Condition;
  • STATE:全局可访问的结构化数据容器;
  • CONDITION:条件边的判断规则;
  • SUBGRAPH:模块化的子图,可以包含多个 Node、Edge,可选共享或使用局部 State;
  • TOOL:外部工具;
  • LLM:大语言模型。

可以看到,图编排的 ER 实体关系图比链式多了 Edge、Condition、State、Subgraph 四个关键实体,这正是图编排灵活性和可控性的来源。

2.4.3 链式编排的交互关系图
Output Node3 LLM Node2 Memory Node1 Chain User Output Node3 LLM Node2 Memory Node1 Chain User 触发执行(输入用户消息) 执行 Node1 读取对话历史 返回处理后的输入 执行 Node2 调用 LLM 返回 LLM 输出 写入新的对话历史 返回 LLM 输出 执行 Node3 返回最终输出 输出最终结果 展示结果

交互关系图说明

  • 链式编排的交互是 线性的——Chain 只能按顺序执行 Node1、Node2、Node3;
  • Memory 只能被单个 Node 读取/写入,不能被所有 Node 共享(虽然 LangChain 的 Memory 是绑定在 Chain 上的,但实际上每个 Node 只能读取/写入 Memory 中特定的部分);
  • 没有条件判断和循环——Chain 必须走到最后一个 Node 才能结束。
2.4.4 图编排的交互关系图
渲染错误: Mermaid 渲染失败: Parse error on line 31: ... break 终止循环 else 验证通过但表达式还有运算 ----------------------^ Expecting 'SPACE', 'NEWLINE', 'INVALID', 'create', 'box', 'end', 'autonumber', 'activate', 'deactivate', 'title', 'legacy_title', 'acc_title', 'acc_descr', 'acc_descr_multiline_value', 'loop', 'rect', 'opt', 'alt', 'par', 'par_over', 'critical', 'break', 'participant', 'participant_actor', 'destroy', 'note', 'links', 'link', 'properties', 'details', 'ACTOR', got 'else'

交互关系图说明

  • 图编排的交互是 非线性的——可以有循环、分支;
  • State 是 全局共享的——所有 Node 都可以读取/修改 State;
  • 明确的条件判断和终止条件——可以根据 State 自主决定走哪条路径,何时结束。

3. 技术原理与实现

3.1 LangChain 链式编排的技术原理

3.1.1 核心原理:函数链式调用

LangChain 链式编排的核心原理非常简单——就是 Python 中的函数链式调用(或者说装饰器模式的一种变体)。每个 Chain 本质上都是一个实现了 __call__ 方法的类,当你调用一个 Chain 时,它会按顺序执行内部的所有 Node,并将前一个 Node 的输出作为后一个 Node 的输入。

我们可以用一个简单的 Python 伪代码来模拟 LangChain 的 LLMChain 实现:

class LLMChain:
    def __init__(self, llm, prompt_template):
        self.llm = llm  # LLM 实例
        self.prompt_template = prompt_template  # 提示模板
        self.memory = None  # 可选的 Memory 实例

    def __call__(self, inputs):
        # 1. 如果有 Memory,读取对话历史并合并到 inputs 中
        if self.memory:
            chat_history = self.memory.load_memory_variables({})
            inputs = {**inputs, **chat_history}
        
        # 2. 用提示模板格式化 inputs,生成最终的提示
        prompt = self.prompt_template.format(**inputs)
        
        # 3. 调用 LLM,生成输出
        output = self.llm.generate(prompt)
        
        # 4. 如果有 Memory,将新的对话历史写入 Memory
        if self.memory:
            self.memory.save_context(inputs, {"output": output})
        
        # 5. 返回输出
        return {"output": output}
3.1.2 预定义 Chain 的实现逻辑

LangChain 的预定义 Chain(比如 RetrievalQAChainConversationalRetrievalChain)本质上都是由多个简单的 Chain(比如 LLMChainRetrievalChain)通过 SequentialChainRouterChain 串联起来的。

我们可以用一个简单的 Python 伪代码来模拟 ConversationalRetrievalChain 的实现:

class ConversationalRetrievalChain:
    def __init__(self, llm, prompt_template, retriever, memory):
        # 1. 初始化生成检索词的 LLMChain
        self.condense_question_chain = LLMChain(
            llm=llm,
            prompt_template=CondenseQuestionPromptTemplate()  # 预定义的提示模板
        )
        # 2. 初始化检索 Chain
        self.retrieval_chain = RetrievalChain(retriever=retriever)
        # 3. 初始化生成答案的 LLMChain
        self.combine_docs_chain = LLMChain(
            llm=llm,
            prompt_template=CombineDocsPromptTemplate()  # 预定义的提示模板
        )
        # 4. 初始化 Memory
        self.memory = memory

    def __call__(self, inputs):
        # 1. 如果有对话历史,用 condense_question_chain 生成独立的检索词
        if self.memory.load_memory_variables({}).get("chat_history"):
            condensed_question = self.condense_question_chain(inputs)["output"]
            inputs["question"] = condensed_question
        
        # 2. 用 retrieval_chain 检索文档
        docs = self.retrieval_chain(inputs)["docs"]
        inputs["docs"] = docs
        
        # 3. 用 combine_docs_chain 生成答案
        output = self.combine_docs_chain(inputs)["output"]
        
        # 4. 将新的对话历史写入 Memory
        self.memory.save_context(inputs, {"output": output})
        
        # 5. 返回输出
        return {"output": output, "docs": docs}
3.1.3 链式编排的数学模型

链式编排的数学模型非常简单——就是 复合函数。假设我们有一个 Chain 包含 n 个 Node,每个 Node 可以表示为一个函数 fi(x)f_i(x)fi(x),其中 xxx 是输入,fi(x)f_i(x)fi(x) 是输出,那么整个 Chain 可以表示为:
F(x)=fn(fn−1(...f2(f1(x))...)) F(x) = f_n(f_{n-1}(...f_2(f_1(x))...)) F(x)=fn(fn1(...f2(f1(x))...))

如果 Chain 包含 Memory,那么我们可以将 Memory 表示为一个状态变量 sks_ksk,其中 kkk 是第 k 次调用 Chain 的次数,那么第 k 次调用 Chain 的过程可以表示为:
{xk′=g(xk,sk−1)yk=F(xk′)sk=h(sk−1,xk′,yk) \begin{cases} x_k' = g(x_k, s_{k-1}) \\ y_k = F(x_k') \\ s_k = h(s_{k-1}, x_k', y_k) \end{cases} xk=g(xk,sk1)yk=F(xk)sk=h(sk1,xk,yk)
其中:

  • g(xk,sk−1)g(x_k, s_{k-1})g(xk,sk1) 是将输入 xkx_kxk 和上一次的状态 sk−1s_{k-1}sk1 合并的函数;
  • F(xk′)F(x_k')F(xk) 是复合函数(即 Chain 的核心逻辑);
  • h(sk−1,xk′,yk)h(s_{k-1}, x_k', y_k)h(sk1,xk,yk) 是更新状态的函数。

可以看到,链式编排的数学模型是 线性的、单向的——状态 sks_ksk 只能由上一次的状态 sk−1s_{k-1}sk1 和当前的输入输出更新,不能被中间的 Node 修改(虽然 LangChain 的 Memory 允许中间的 Node 修改,但实际上非常麻烦,而且容易出错)。


(本章未完,后续将继续讲解 LangGraph 图编排的技术原理、算法流程图、Python 源代码等内容,预计全文剩余约 9000 字)

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐