当我们谈论大语言模型应用开发时,链式调用(Chain)已经无法满足复杂业务场景的诉求。LangGraph作为LangChain生态中的"图编排"层,正成为构建生产级Agentic Workflow的事实标准。本文将从架构设计、运行机制、状态管理与工程实践四个维度,深入剖析LangGraph的技术内核。
一、为什么需要LangGraph:从Chain到Graph的范式跃迁
在LLM应用开发的早期阶段,LLMChain、SequentialChain等线性编排工具占据主导位置。开发者将提示模板、模型调用、输出解析器按顺序串联,组成一条"管道"。这种方式在单轮对话、简单 RAG(检索增强生成)场景下表现良好,但一旦涉及以下需求便捉襟见肘:
- 循环与回溯:Agent 需要"思考—行动—观察"循环(ReAct 范式),需多次访问同一节点;
- 条件分支:根据中间结果决定下一步走向(路由决策),而非死板地按顺序执行;
- 多角色协作:规划器、检索器、批判器、执行器并行或交替参与;
- 人机协同(HITL):在关键节点暂停,等待人类反馈再继续;
- 持久化与时间旅行:长任务需要在断点处保存状态,并能回溯到任意历史步骤。
LangGraph 应运而生。它由 LangChain 团队于 2024 年推出,定位为用于构建有状态、多角色 LLM 应用的编排框架。其核心理念借鉴了经典图论与 Pregel 分布式图计算模型,将整个 Agent 流程抽象为一张有向图(Directed Graph)——节点代表计算单元,边代表控制流走向,状态在节点间流动并被持久化。
核心定位:LangGraph ≠ LangChain 替代品,而是 LangChain 体系内专注于"复杂控制流 + 状态管理"的子项目。
二、图结构剖析:节点、边与条件路由
下图是一张典型的 LangGraph 状态机示意图,清晰展示了其工作流的拓扑结构:
┌─────┐ │START│ └─┬─┬─┘ │ │ ┌──────┘ └───────┐ ▼ ▼ ┌──────┐ ┌──────┐ │node1 │ │node2 │ └──┬───┘ └──┬───┘ │ │ ▼ ▼ ┌──────┐ ┌──────┐ │node3 │ │node4 │ └──┬───┘ └──┬───┘ │ │ └─────┐ ┌──────┘ ▼ ▼ ┌──────┐ │node5 │ └──┬───┘ ▼ ┌───┐ │END│ └───┘从图中我们可以看到 LangGraph 的几个关键结构特征:
| 元素 | 含义 | 工程作用 |
|---|---|---|
START | 虚拟入口节点 | 图执行起点,注入初始状态 |
END | 虚拟出口节点 | 图终止条件,标识流程结束 |
node1~node5 | 业务节点 | 封装具体计算逻辑(LLM调用、工具执行等) |
| 有向边 | 控制流走向 | 决定下一跳节点 |
| 汇聚边 | 多源汇合 | node3和node4同时指向node5,体现 Join 语义 |
| 分支边 | 一源多目标 | START同时指向node1和node2,实现并发/路由 |
2.1 节点(Node):计算的原子单元
节点是 LangGraph 中"做事的地方"。每个节点本质是一个纯函数(或可调用对象),签名为:
def my_node(state: State) -> dict: # 读取当前状态 user_input = state["messages"][-1] # 执行计算(LLM/工具/业务逻辑) response = llm.invoke(user_input) # 返回状态增量 return {"messages": [response]}关键设计原则有三:
- 节点无副作用:节点只描述"如何从输入状态计算到输出状态",不直接修改全局变量;
- 返回增量而非全量:节点返回的字典会被
Reducer函数合并进全局状态,避免并发冲突; - 可组合性:节点本身可以是一个 LangChain Runnable、Tool、Agent 或其他图(支持图嵌套)。
2.2 边(Edge)与条件路由
边定义节点间的转移关系,LangGraph 支持三种边:
① 静态边(Static Edge)——固定下一个节点:
graph.add_edge("node1", "node3")② 条件边(Conditional Edge)——根据运行时状态决策:
def route_after_planning(state): if state["plan_ready"]: return "execute" else: return "replan" graph.add_conditional_edges( "planner", route_after_planning, {"execute": "executor", "replan": "planner"} # path map )条件边是 LangGraph 实现 Agent 行为的核心机制。ReAct、Plan-and-Execute、Router 等主流 Agent 范式,本质上都是通过条件边实现循环跳转的。
③ 入口边(Entry Point)——指定 START 后的第一跳:
graph.set_entry_point("node1")对应到上面图示,START → node1与START → node2同时存在,意味着这是一个并行分支起点(也可能是条件路由结果),这种结构非常适合"多路并发执行后汇聚"的场景,例如:
- 多源信息并行检索(向量库、SQL、Web Search)后合并;
- 多个候选答案生成后选择最优;
- 多角色辩论(Pro/Con 双方独立推理后由 Judge 决策)。
2.3 Pregel 风格的执行模型
LangGraph 的运行时借鉴了 Google Pregel 的BSP(Bulk Synchronous Parallel)超级步模型:
- 每个 superstep:所有活跃节点被调度执行,接收上一轮的状态;
- 节点并行运行:相互独立的节点可以并发执行(边连接顺序决定依赖);
- 消息传递:节点向其下游节点发送"消息"(状态增量);
- 同步屏障:当所有节点完成本轮计算后,进入下一 superstep;
- 终止判定:当没有活跃节点指向下一节点时,图进入终止状态。
这种"同步并行 + 消息驱动"的模型天然适配分布式部署,也使得 LangGraph 能够优雅地处理循环、汇合、分支等复杂拓扑。
三、状态管理:有状态应用的核心难题
如果说图结构是 LangGraph 的骨架,那**状态(State)**就是其灵魂。一个真实的 Agent 应用通常需要在多个节点间共享数据:
- 用户的原始问题与多轮对话历史;
- 中间推理步骤、工具调用记录;
- 检索到的文档片段;
- 任务执行计划与进度;
- 错误信息与回滚日志。
3.1 Schema 定义:TypedDict + Reducer
LangGraph 使用 Python 的TypedDict声明状态结构,并通过Annotated字段指定归约函数(Reducer):
from typing import TypedDict, Annotated, List from langgraph.graph import add_messages from langchain_core.messages import BaseMessage class AgentState(TypedDict): messages: Annotated[List[BaseMessage], add_messages] plan: str step_count: Annotated[int, lambda x, y: x + y] # 自定义 reducer retry_count: intadd_messages是 LangGraph 内置的核心 Reducer,它能智能合并消息列表:
- 同 ID 的消息会覆盖(模拟消息更新);
- 新 ID 的消息会追加;
- 这恰好对应 OpenAI/Anthropic API 中"assistant 消息先发出去再修正"的模式。
3.2 Checkpointer:持久化与时间旅行
对于长任务(小时级甚至天级),我们需要把状态写入持久化存储,以便:
- 断点续传:进程崩溃后能从最后状态恢复;
- 人机协同:暂停在某个节点,等待人类审核后再恢复;
- 时间旅行调试:回滚到任意历史 superstep 重放执行。
LangGraph 通过Checkpointer抽象提供这一能力:
from langgraph.checkpoint.memory import MemorySaver from langgraph.checkpoint.postgres import PostgresSaver # 内存版(开发调试) memory_saver = MemorySaver() # PostgreSQL 版(生产环境) postgres_saver = PostgresSaver.from_conn_string(DB_URL) graph = builder.compile(checkpointer=postgres_saver) # 执行时通过 thread_id 关联会话 config = {"configurable": {"thread_id": "user-12345"}} result = graph.invoke({"messages": [HumanMessage("你好")]}, config) # 时间旅行:获取历史状态 state_history = graph.get_state_history(config) for snapshot in state_history: print(snapshot.values, snapshot.next)这种设计让 LangGraph 天生具备生产级 Agent所需的可观测性、可恢复性与可审计性。
3.3 Send / Map-Reduce 模式
对于"动态扇出"场景(节点运行时才知道需要分几个子任务),LangGraph 提供了SendAPI:
from langgraph.constants import Send def route_to_workers(state): """根据待处理任务列表动态创建 worker""" return [Send("worker", {"task": t}) for t in state["tasks"]] graph.add_conditional_edges("supervisor", route_to_workers, ["worker"])配合 Reducer 模式,可以实现经典的Map-Reduce编排:supervisor 节点动态创建 N 个 worker 并行执行,reducer 节点汇总所有结果。
四、LangGraph 与 LangChain:协作而非替代
很多初学者会问:"我已经用了 LangChain,为什么还需要 LangGraph?" 这里的关键是理解二者的职责分层:
| 维度 | LangChain | LangGraph |
|---|---|---|
| 定位 | LLM 应用开发工具集(LCEL Runnable、Chain、Tool、Retriever 等) | 复杂工作流编排框架 |
| 抽象层级 | 单次调用的"动作"(如 RAG Chain、Tool) | 多步协作的"剧本"(状态机) |
| 控制流 | 主要线性(LCEL|操作符串行) | 任意图结构(含循环、分支、汇合) |
| 状态管理 | 通常无状态或由调用方管理 | 内置 Schema、Reducer、Checkpointer |
| 持久化 | 无内置 | 内置 Checkpointer(Memory/Postgres/SQLite) |
| 人机协同 | 需自行实现 | 内置interrupt()API |
| 调试能力 | LangSmith Trace | LangSmith + 时间旅行 + State History |
4.1 它们如何协作
LangGraph 并不是要取代 LangChain,而是站在 LangChain 肩膀上:
- 节点可以是一个
Runnable(LCEL 链),复用所有 LangChain 组件; - 状态中的
messages字段直接兼容 LangChain Message 类型; - Retriever、Tool、Memory 等 LangChain 抽象都可以作为节点使用。
一个典型组合是:
[LangChain Retriever] → 节点 [LangChain RAG Chain] → 节点 [LangChain Agent] → 节点 [LangGraph] → 编排框架,串联上述节点4.2 LangGraph 的差异化优势
总结起来,LangGraph 提供了 LangChain 缺失的四项关键能力:
- 循环与回溯:原生支持节点被多次访问(如 ReAct 的"思考—行动—观察"循环);
- 细粒度控制:条件边让 LLM 决定走向成为一等公民;
- 生产级持久化:内置 Checkpointer 让"长任务 + 崩溃恢复"开箱即用;
- 时间旅行调试:
get_state_history+update_state让"回到过去"成为可能。
对于简单场景(如单轮 RAG),LangChain LCEL 完全够用;但只要涉及多步推理、Agent 协作、长流程管理,LangGraph 几乎是当前最成熟的选择。
五、底层运行机制:源码层面的执行流程
理解了概念之后,让我们深入 LangGraph 源码层,看看一次graph.invoke()背后发生了什么:
5.1 编译期:StateGraph → CompiledGraph
from langgraph.graph import StateGraph, END builder = StateGraph(AgentState) builder.add_node("agent", call_model) builder.add_node("tools", tool_node) builder.add_conditional_edges("agent", should_continue, {"continue": "tools", "end": END}) builder.add_edge("tools", "agent") builder.set_entry_point("agent") graph = builder.compile() # ← 关键:编译compile()内部完成:
- 拓扑校验:检测环路、孤立节点、缺失入口;
- 构建通道(Channels):每个状态字段对应一个 Channel,绑定 Reducer;
- 生成 Pregel 任务图:将节点映射为可并行执行的任务;
- 绑定 Checkpointer:建立状态持久化通道。
5.2 运行期:PregelLoop 主循环
CompiledGraph.invoke()内部启动PregelLoop,其主循环伪代码如下:
while not done: # 1. 准备本轮输入:从 Channel 读取活跃任务的状态 tasks = prepare_tasks(next_nodes, config) # 2. 并行执行任务(节点函数调用) for task in tasks: writes = task.run(state) # 节点返回状态增量 # 3. 应用 Reducer,将写入合并进 Channel apply_writes(writes, config) # 4. 计算下一轮活跃节点 next_nodes = route(conditional_edges, current_state) # 5. 检查终止条件(next 为空或指向 END) done = not next_nodes5.3 关键设计模式
- Producer-Consumer:节点是 Producer,Channel 是 Consumer 队列;
- Event-driven:通过
eventsAPI(graph.stream_events)订阅 token 级别事件,实现流式输出; - Idempotent Tasks:每个任务通过
(task_id, channel_version)标识,支持重试与幂等。
六、典型应用场景
6.1 多轮 ReAct Agent
最经典的场景是 ReAct(Reason + Act)循环:
graph.add_conditional_edges( "agent", should_continue, {"continue": "tools", "end": END} ) graph.add_edge("tools", "agent") # 循环回 agentLLM 在agent节点判断"是否需要调用工具",如果需要则跳到tools,执行完后再回到agent。这正是 ReAct 论文的工程实现。
6.2 Plan-and-Execute
对于复杂任务,先规划后执行能显著提升准确率:
START → planner → executor → [反思/补充] → END ↑___________|planner节点生成步骤列表,executor节点逐步执行,每步结果反馈给planner决定下一步。
6.3 多 Agent 协作(Multi-Agent Supervisor)
LangGraph 官方示例中的"主管-工人"模式:
supervisor → [worker_A, worker_B, worker_C] → supervisor主管 Agent 分配任务给专业 Worker,汇总结果后决定继续委派还是终止。
6.4 人机协同工作流
def approval_node(state): # 触发 interrupt,等待人类决策 human_decision = interrupt({"plan": state["plan"]}) return {"decision": human_decision}在关键决策点暂停,把"审批"动作交给人类,特别适用于:
- 金融交易确认;
- 内容发布前审核;
- 医疗诊断复核。
七、总结与展望
LangGraph 之所以在 2024-2026 年迅速走红,本质是因为它击中了 LLM 应用从 Demo 走向生产的关键痛点:
| 痛点 | LangGraph 方案 |
|---|---|
| 复杂控制流难表达 | 有向图 + 条件边 |
| 长任务易崩溃 | Checkpointer + 断点续传 |
| 调试 Agent 像黑盒 | State History + 时间旅行 |
| 多 Agent 难协作 | Send API + 并行 + Reducer |
| 关键节点需人类把关 | interrupt()+ Command API |
从架构演进角度看,LangGraph 代表了一种趋势:LLM 应用正在从"Prompt Engineering"走向"Workflow Engineering"。提示词只是冰山一角,真正决定系统质量的是背后那张编排图——节点如何划分、状态如何流动、错误如何恢复、人类如何参与。