LangChain 与 LangGraph 的原理与分工
这两个框架经常被混在一起讲,但其实它们解决的是不同层次的问题。总体上看,
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)
每个超步之间:
- 节点输出经 reducer 合并进状态
- Checkpointer 把整个状态快照存下来
- 根据边计算下一批激活节点(同一超步内多个节点可并行)
4. 由执行模型衍生出的关键能力
< 1.> Checkpoint / 持久化
每个超步后状态被完整快照(内存/SQLite/Postgres),用 thread_id 标识会话。于是天然获得:
- 会话恢复:进程崩了,从最后一个快照继续
- Time Travel:
get_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 的图把它们连成带检查点和权限中断的循环状态机。
更多推荐

所有评论(0)