从 Chain 到 Graph:线性思维的终点
最早我们都是这么写 LLM 应用的:
# 经典 Chain: input → prompt → LLM → parser → output from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI chain = ChatPromptTemplate.from_template("回答:{q}") | ChatOpenAI() answer = chain.invoke({"q": "法国首都?"})
但凡你的应用需要做下面任意一件事,Chain 就开始出问题:
- 循环:LLM 调工具 → 看结果 → 再调工具,这是"思考-行动-观察"循环
- 分支:按意图走不同流程,"退款"走 A 路径,"查询"走 B 路径
- 回溯:Agent 走错了,要退回上一步重新选择
- 持久化:对话被打断,明天继续时必须恢复 state
- 并行:同时调 3 个工具,谁快谁先返回
- 人工介入:某些步骤必须等人审批才能继续
这些需求的共同特征:需要状态、需要控制流、需要"图"而非"链"。
LangGraph 的定位
LangGraph 是 LangChain 团队推出的、专门处理"有状态 LLM 工作流"的低层库。它不替代 LangChain,而是解决 Chain 搞不定的图拓扑问题。学完你会发现,连 OpenAI Agents SDK、CrewAI 底层的核心思想都与它相通。
LangGraph 是 LangChain 团队推出的、专门处理"有状态 LLM 工作流"的低层库。它不替代 LangChain,而是解决 Chain 搞不定的图拓扑问题。学完你会发现,连 OpenAI Agents SDK、CrewAI 底层的核心思想都与它相通。
一个直观对比:ReAct Agent
同样实现"LLM 决定要不要调工具"的循环,看两种写法:
Chain 风格(难看、难维护)
def run_agent(query, max_steps=5): messages = [{"role": "user", "content": query}] for _ in range(max_steps): rsp = llm.invoke(messages, tools=TOOLS) messages.append(rsp) if not rsp.tool_calls: return rsp.content for call in rsp.tool_calls: result = run_tool(call) messages.append({"role": "tool", "content": result}) return "超出步数限制"
看起来能跑,但问题是:
- 状态只在函数局部,断开就丢
- 想加个"审批节点"?要改函数结构
- 想看每一步的 trace?要手工打 log
- 想从第 3 步重跑?不可能
- 想并行两个工具?再嵌套一层 loop
LangGraph 风格(声明式、可组合)
from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode, tools_condition from typing import TypedDict, Annotated from langgraph.graph.message import add_messages class State(TypedDict): messages: Annotated[list, add_messages] def call_llm(state): return {"messages": [llm.invoke(state["messages"], tools=TOOLS)]} g = StateGraph(State) g.add_node("llm", call_llm) g.add_node("tools", ToolNode(TOOLS)) g.add_edge(START, "llm") g.add_conditional_edges("llm", tools_condition) # 有 tool_call → tools, 否则 END g.add_edge("tools", "llm") # 工具返回后回到 LLM app = g.compile()
同样的 Agent,LangGraph 版给你免费送了:
- 状态自动持久化(加一个 checkpointer 就行)
- 任何一步都能中断、修改、恢复
- 自动 trace 每一步(配合 LangSmith 零成本)
- 能画出来(mermaid/png 一行生成)
- 要并行、要审批、要回溯都只是"加节点改边"
图结构的心智模型
LangGraph 的核心 3 个概念,就这么点:
State(状态)
整张图共享的可变数据。通常是 TypedDict,描述"目前为止 Agent 知道什么"。每个节点读 state、返回对 state 的 增量更新。
Node(节点)
一个函数:接收 state,返回 dict(要更新的字段)。可以是 LLM 调用、工具调用、业务逻辑、人工审批等。
Edge(边)
节点之间的跳转规则。有静态边(A→B 无条件)和条件边(根据 state 决定去哪)。START 和 END 是两个特殊节点。
可视化:ReAct Agent 的图
┌───────┐
│ START │
└───┬───┘
▼
┌───────┐ tool_calls 非空
│ llm │─────────────────────┐
└───┬───┘ ▼
│ tool_calls 为空 ┌───────┐
▼ │ tools │
┌───────┐ └───┬───┘
│ END │◀─────────────────────┘
└───────┘ (返回 llm)
LangGraph 解决的 6 个真实痛点
| 痛点 | Chain 方案 | LangGraph 方案 |
|---|---|---|
| 多轮对话续上下文 | 手工传 history list | Checkpointer + thread_id 自动持久化 |
| Agent 走到一半被打断 | 从头重跑 | 从最近 checkpoint 恢复 |
| 某些步骤需审批 | 改代码加 input() | interrupt_before,前端能实现审批 UI |
| 多 Agent 协作 | 硬编码 router | Subgraph + Supervisor,可组合 |
| 复盘失败 case | 搞不清楚哪一步错 | get_state_history 完整回放 |
| 流式输出中间步骤 | 每个 chain 自己 stream | stream_mode=updates 天然支持 |
什么时候不要用 LangGraph
别过度设计
如果你的应用就是单次"prompt → LLM → 输出",用 LangGraph 是杀鸡用牛刀。判断标准:
✅ 有循环 / 分支 / 回溯 / 审批 / 多 Agent / 持久化中任何一个需求 → 上 LangGraph
❌ 只是个一次性的文本生成或分类 → 直接 LangChain 或原生 SDK 就够
✅ 有循环 / 分支 / 回溯 / 审批 / 多 Agent / 持久化中任何一个需求 → 上 LangGraph
❌ 只是个一次性的文本生成或分类 → 直接 LangChain 或原生 SDK 就够
学习路径
- Chapter 2:写出你的第一个 graph,理解 State/Node/Edge 三件套
- Chapter 3:条件路由与循环,让图能"思考"
- Chapter 4:ReAct Agent,工具调用标准模式
- Chapter 5-6:持久化与人工介入,生产化必修
- Chapter 7-8:流式与 Multi-Agent,复杂场景
- Chapter 9-10:可观测性 + 端到端落地
环境准备
pip install -U langgraph langchain langchain-openai # 可选(推荐): LangSmith 观测 export LANGSMITH_TRACING=true export LANGSMITH_API_KEY=ls_... export OPENAI_API_KEY=sk_...
本书的代码在 LangGraph 0.2+ 和 Python 3.10+ 下验证通过。
本章小结
- Chain 擅长"线性流水线",撑不住有状态 Agent 的循环、分支、回溯
- LangGraph = State + Node + Edge,把工作流画成有向图
- ReAct、Multi-Agent、人工介入等复杂场景在图结构下都是"加节点改边"
- 免费送 checkpoint、trace、可视化、流式、并行
- 判断是否要上 LangGraph:有循环/分支/回溯/审批/持久化任一 → 上