这两个框架经常被混在一起讲,但其实它们解决的是不同层次的问题。总体上看,

LangChain解决「怎么方便地调用 LLM 和各种外部资源」——是一层组件抽象与胶水
LangGraph解决「怎么编排一个可控、可持久、可循环的 agent 工作流」——是一个状态机运行时


一、LangChain 的原理

1. 它要解决的根本问题

直接调 LLM API 时会遇到一堆琐碎问题:不同厂商(OpenAI/Anthropic/本地模型)接口不一样、提示词模板拼接麻烦、输出是字符串但要解析成结构化数据、想串多步调用…… LangChain 的思路是:给这些共性环节定义统一接口,让组件可以互相拼接

2. 核心抽象

< 1.> Runnable 接口与 LCEL(表达式语言)

这是现代 LangChain 的心脏。所有组件(模型、提示词模板、解析器、工具)都实现同一个 Runnable 协议:

class Runnable:
    def invoke(input) -> output          # 同步单次调用
    async def ainvoke(input)             # 异步
    def stream(input) -> iterator        # 流式
    def batch(inputs) -> list            # 批量

因为接口统一,就能用 | 管道符把组件串成链(借鉴 Unix pipe 的思想):

chain = prompt | model | output_parser
# 等价于:output = parser.parse(model.generate(prompt.format(input)))

| 产生的是 RunnableSequence:上一步的输出自动作为下一步的输入。整个链本身也是一个 Runnable,所以链可以嵌套链。

< 2.> PromptTemplate:把提示词从硬编码字符串变成带变量的模板,运行时注入变量。

< 3.> ChatModel 适配层:对各家 API 做统一封装。你写 model.invoke(messages),底层自动翻译成 OpenAI 的 chat.completions 或 Anthropic 的 messages 格式。切换模型只改一行。

< 4.> OutputParser:把模型的自由文本解析成结构化对象(JSON、Pydantic 模型),失败时可重试。

< 5.> Tool / Tool Calling:模型输出结构化的 tool_calls(函数名+参数 JSON),LangChain 负责调度对应的 Python 函数执行、把结果包成 ToolMessage。注意:模型从来不"执行"工具,它只是输出一个调用请求,执行权在框架手里——理解这一点是理解所有 agent 框架的关键。

<6.> Memory / Retriever:对话历史的存取、RAG 的检索器,同样抽象为统一接口。

3. 底层数据模型

一切围绕消息类型流转:SystemMessage / HumanMessage / AIMessage / ToolMessage。一次调用就是「消息列表进 → AIMessage 出」。AIMessage 里除了文本还有 tool_calls 字段,这是 agent 循环的驱动信号。

4. LangChain 的局限(为什么需要 LangGraph)

LangChain 的 Chain 是有向无环图(DAG):数据从 A 流到 B 到 C,一条路走到底。但 agent 需要:

  • 循环:LLM → 工具 → LLM → 工具 → …… 直到完成
  • 条件分支:根据模型输出动态决定走哪条路
  • 状态持久化:长任务要中断、恢复
  • 人类介入:危险操作前暂停等批准

用 Chain 表达循环会非常别扭(早年 LangChain 的 AgentExecutor 就是一个封装死的 while 循环,内部逻辑不可控、不可定制)。这就是 LangGraph 诞生的原因。


二、LangGraph 的原理

1. 核心思想:把 agent 建模为「状态机/图」

LangGraph 从三个经典计算模型吸取灵感:

  • 状态机:系统在有限个状态间转移,每一步由当前状态决定下一步去哪
  • 数据流图(dataflow):节点是计算,边是数据流向
  • Actor 模型 / Pregel(Google 的图计算框架):节点接收消息、执行、产生新消息,按"超步(superstep)"迭代推进

2. 三个基本概念

StateGraph(状态Schema)
 ├── 节点 Node:一个函数,接收 State,返回 State 的部分更新
 ├── 边 Edge:节点间的连接(固定边 / 条件边)
 └── 状态 State:一个 TypedDict,全图共享的"黑板"

< 1.> State——全局共享状态

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]   # ← reducer
    step_count: int

关键机制是 Reducer(归约器)。每个节点执行后返回一个 dict,框架要把这个更新合并进全局状态。怎么合并?由字段上的 reducer 决定:

  • 无 reducer:直接覆盖(step_count
  • Annotated[list, add_messages]:追加而不是覆盖,且能处理消息去重、按 ID 更新

这就是消息历史能在循环里不断累积而不被冲掉的原理。

< 2.> Node——纯函数式的计算单元

def agent_node(state: AgentState) -> dict:
    response = llm.invoke(state["messages"])
    return {"messages": [response]}   # 只返回"增量",不是完整状态

节点是「读状态 → 算 → 返回增量更新」的函数,reducer 负责合并。这种设计让状态演化可预测、可回放。

< 3.> Edge——控制流

  • 普通边:add_edge("a", "b") —— a 执行完必走 b
  • 条件边:一个路由函数读状态,返回下一个节点名 —— 这就是 agent 的"决策"所在
def router(state):
    if state["messages"][-1].tool_calls:
        return "tools"    # 模型想调工具 → 去工具节点
    return END            # 否则结束

循环就是边指回前面的节点tools → agent → tools → agent → …,直到条件边走向 END。这突破了 Chain 的 DAG 限制。

3. 执行引擎:Superstep 与消息传递

compile() 后变成一个 Pregel 运行时,执行模型是超步迭代

Superstep 0: START → 激活 "agent" 节点,执行,产出状态更新 + 路由结果
Superstep 1: 路由到 "tools",执行所有工具调用
Superstep 2: 边 "tools→agent",再次执行 agent
...
直到没有节点被激活(到达 END)

每个超步之间:

  1. 节点输出经 reducer 合并进状态
  2. Checkpointer 把整个状态快照存下来
  3. 根据边计算下一批激活节点(同一超步内多个节点可并行)

4. 由执行模型衍生出的关键能力

< 1.> Checkpoint / 持久化

每个超步后状态被完整快照(内存/SQLite/Postgres),用 thread_id 标识会话。于是天然获得:

  • 会话恢复:进程崩了,从最后一个快照继续
  • Time Travelget_state_history() 能列出所有历史快照,可以从任意历史点分叉重跑——这对调试 agent 极其有用

< 2.> Human-in-the-loop(interrupt)

interrupt() 或在节点前设 interrupt_before:图执行到这里时把状态序列化后挂起,等外部(人类)通过 Command(resume=...) 注入输入,图从断点精确恢复。OpenCode 式的「执行 bash 前等用户批准」就是这个机制的简单应用。

< 3.> Streaming 多层流式

astream(stream_mode=...) 暴露不同粒度:

  • "messages":LLM 逐 token 输出
  • "updates":每个节点执行完的状态增量
  • "values":每个超步后的完整状态
  • "events":所有内部事件(最细粒度)

TUI 界面就是订阅这些流来实时渲染的。

< 4.> Subgraph

一个编译好的图可以作为另一个图的节点。主 agent 调子 agent(如派一个"代码探索 agent")= 嵌套子图,状态可以映射传递。

5. LangGraph 的运转总体框图

        ┌────────────── State (带 reducer 的 TypedDict) ──────────────┐
        │                                                             │
START → [agent 节点] ──条件边──→ [tools 节点] ──固定边──→ [agent 节点] ──→ END
              │                     │                         ▲
              └── LLM 决定调工具 ───┘   └── 结果写回 state,循环 ──┘

每个箭头 = 一个 superstep = 一次 checkpoint(可暂停/恢复/回放)

三、两者的关系与分工

LangChain LangGraph
抽象层 组件层:模型、提示词、工具、解析器 编排层:状态、节点、边、运行时
计算模型 管道 / DAG(无环) 状态机 / 图(有环)
控制流 线性,开发者预先写死 动态,可由 LLM 输出决定走向
状态 链内隐式传递 显式全局 State + reducer
持久化 无内建机制 Checkpoint 一等公民
类比 乐高积木 电路板/交通调度系统

实践中它们是配合使用的:节点函数内部用 LangChain 的模型、工具、LCEL(prompt | model | parser),而节点之间的流转、循环、状态持久化、人机介入由 LangGraph 管理。LangGraph 自己甚至不强依赖 LangChain——节点里直接调原生 OpenAI SDK 也完全可行。

用 LangGraph 写编码 agent,本质就是——用 LangChain 的组件实现"agent 节点"和"工具节点",用 LangGraph 的图把它们连成带检查点和权限中断的循环状态机

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐