从 Chain 到 Graph:为什么复杂 Agent 需要 LangGraph
点击上方 前端Q,关注公众号
回复加群,加入前端Q技术交流群
前两篇我们用 LangChain.js 搭了 Tool-Calling Agent,也拆解了 ReAct 模式的"想→做→看"循环。
如果你只是做一个简单的"查天气、做计算"的 Agent,前面讲的 AgentExecutor 完全够用。但当需求变复杂——比如要加人工审批、要根据结果走不同分支、出错了要自动重试、甚至多个 Agent 协作——你就会发现 Chain(链式架构)开始"兜不住"了。
这时候就该轮到 LangGraph 登场了。
▎Chain 到底差在哪?
先说清楚 Chain 的问题,你才能理解为什么需要 Graph。
Chain 的执行模式是:A → B → C → D,一条直线走到底。
这就像流水线——每个工位做完自己的事,传给下一个工位。简单、清晰、好理解。但问题是:
问题一:中间断了就废了
B 步骤调用 API 超时了?整个链路直接崩。没有重试机制,没有备选路径,只能从头再来。
问题二:不能回头
LLM 生成的代码有 bug,你想让它"看看报错然后修改"——对不起,Chain 只能往前走不能往回走。ReAct 的循环其实就是在 Chain 上硬套的 while 循环,但本质上还是受限的。
问题三:不能分叉
用户的问题是技术类的走 A 路径,是闲聊的走 B 路径——在 Chain 里做条件分支非常别扭,代码会变成一坨 if-else。
问题四:没法暂停
想在 Agent 执行某个危险操作前停下来等人工确认?Chain 的架构里根本没有"暂停"这个概念。
一句话总结:Chain 假设一切都会顺利——但真实世界哪有那么听话。
▎LangGraph 是什么:用前端思维来理解
LangGraph 本质上就是一个状态机——或者说,一个可以有循环、有分支、有条件跳转的工作流引擎。
如果你写过前端,这些概念你一定不陌生:
|
LangGraph 概念 |
前端类比 |
|---|---|
| Node(节点) |
React 组件 / 中间件函数 |
| Edge(边) |
React Router 的路由规则 |
| State(状态) |
Redux / Zustand 的 Store |
| Conditional Edge |
路由守卫 / 条件渲染 |
| Graph 编译 |
Webpack 打包——定义好了才能跑 |
所以你可以这样理解 LangGraph:它就是给 AI Agent 用的"React Router + Redux"。
Router 决定"下一步走哪个节点",Redux 管理"当前整个工作流的状态",每个节点就是一个处理函数。
▎LangGraph 三件套:Node、Edge、State
1. Node(节点)—— 做事的人
每个 Node 就是一个函数,接收当前 State,做一些处理,然后返回更新后的 State。
typescript
// 一个最简单的 Node
function greetNode(state: AgentState): Partial<AgentState> {
return {
messages: [...state.messages, { role: "assistant", content: "你好!有什么能帮你的?" }],
};
}
Node 可以是:
- ▸
调用 LLM 生成回答
- ▸
执行工具(查 API、读数据库)
- ▸
做逻辑判断(检查结果是否合格)
- ▸
纯数据处理(格式转换、过滤)
2. Edge(边)—— 谁接谁
Edge 定义了 Node 之间的连接关系。有两种:
固定边:A 做完一定走 B,没有悬念。
typescript
graph.addEdge("nodeA", "nodeB");
条件边:根据当前状态决定走哪条路。
typescript
graph.addConditionalEdges("agent", (state) => {
// 如果 LLM 要求调用工具 → 走 tools 节点
if (state.messages.at(-1)?.tool_calls?.length > 0) {
return "tools";
}
// 否则 → 结束
return "__end__";
});
条件边是 LangGraph 最强大的部分——它让工作流不再是"一条线",而是可以"拐弯、回头、分叉"。
3. State(状态)—— 共享的记事本
State 是整个 Graph 里所有 Node 共用的一份数据。每个 Node 执行时都能读取当前 State,处理完后更新 State,下一个 Node 拿到的就是最新的 State。
typescript
import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";
// 定义状态结构
const AgentState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (prev, next) => [...prev, ...next], // 消息追加,不覆盖
}),
currentStep: Annotation<string>(),
retryCount: Annotation<number>(),
});
注意那个 reducer——它定义了状态怎么合并。messages 用追加模式(新消息加到老消息后面),这个设计跟 Redux 的 reducer 一模一样。
▎动手:用 LangGraph 重写 ReAct Agent
来个实战对比。同样的 ReAct Agent,看看 LangGraph 写出来跟之前的 AgentExecutor 有什么不同。
typescript
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { BaseMessage, HumanMessage } from "@langchain/core/messages";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
// 1. 定义工具
const weatherTool = tool(
async ({ city }) => {
const data: Record<string, string> = {
"北京": "晴,25°C", "上海": "多云,28°C",
};
return data[city] || <span style="color:#<span style=;" color:#66d9ef"="">E6DB74">`暂不支持 ${city}`;
},
{
name: "get_weather",
description: "查询城市天气",
schema: z.object({ city: z.string() }),
}
);
const tools = [weatherTool];
const llm = new ChatOpenAI({ modelName: "gpt-4o", temperature: 0 }).bindTools(tools);
// 2. 定义状态
constAgentState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (prev, next) => [...prev, ...next],
}),
});
// 3. 定义节点
asyncfunctionagentNode(state: typeofAgentState.State) {
const response = await llm.invoke(state.messages);
return { messages: [response] };
}
const toolNode = newToolNode(tools);
// 4. 定义路由逻辑
functionshouldCallTool(state: typeofAgentState.State) {
const lastMessage = state.messages.at(-1);
if (lastMessage?.tool_calls && lastMessage.tool_calls.length > 0) {
return "tools";
}
returnEND;
}
// 5. 组装 Graph
const graph = newStateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldCallTool)
.addEdge("tools", "agent") // 工具执行完回到 agent 继续思考
.compile();
// 6. 运行
const result = await graph.invoke({
messages: [newHumanMessage("北京天气怎么样?")],
});
看起来代码变多了?没错。但你获得了什么?
你获得了完整的控制权。 每一步走哪、什么条件走哪条路、状态怎么变化,全都是你自己定义的,不再是 AgentExecutor 内部的黑盒。
▎Graph 能做,Chain 做不到的 4 件事
1. 循环重试
LLM 写的代码有语法错误?让它看到报错后自动修改,最多重试 3 次:
typescript
function shouldRetry(state: typeofAgentState.State) {
if (state.hasError && state.retryCount < 3) {
return "fix_code"; // 回到修复节点
}
if (state.hasError) {
return "give_up"; // 超过 3 次,放弃
}
returnEND; // 没问题,结束
}
Chain 里做这个?要么用 while 循环硬套,要么改得面目全非。Graph 里天然支持——加一条从 check 指回 fix_code 的条件边就行。
2. 条件分支
根据用户问题的类型走不同处理路径:
typescript
function routeByType(state: typeofAgentState.State) {
const intent = state.classifiedIntent;
if (intent === "technical") return "tech_agent";
if (intent === "creative") return "creative_agent";
return "general_agent";
}
graph
.addNode("classifier", classifyNode)
.addNode("tech_agent", techAgentNode)
.addNode("creative_agent", creativeAgentNode)
.addNode("general_agent", generalAgentNode)
.addConditionalEdges("classifier", routeByType);
3. 人工审批(Human-in-the-Loop)
在 Agent 执行某个操作前暂停,等人工确认后再继续:
typescript
const graph = newStateGraph(AgentState)
.addNode("draft", draftNode)
.addNode("review", reviewNode) // 这个节点会暂停等人工输入
.addNode("publish", publishNode)
.addEdge("draft", "review")
.addConditionalEdges("review", (state) => {
return state.approved ? "publish" : "draft"; // 批准了就发布,否则打回重写
});
LangGraph 支持 interrupt 机制,可以在任意节点暂停 Graph 执行,等到外部输入后再恢复。这在 Chain 架构里几乎不可能做到。
4. 多 Agent 协作
多个专家 Agent 各自负责一块,由一个 Supervisor 来调度:
START → Supervisor
├── 让 Research Agent 去搜索资料
├── 让 Writer Agent 写初稿
├── 让 Reviewer Agent 审核
└── 都搞定了 → END
每个 Agent 就是 Graph 里的一个 Node,Supervisor 是一个带条件边的路由节点。这就是后面"多 Agent 协作"文章要详细展开的内容。
▎什么时候该用 LangGraph?
不是所有场景都需要 LangGraph。这里给一个清晰的判断标准:
|
你的需求 |
用什么 |
|---|---|
|
简单的聊天机器人,一问一答 |
LangChain 基础 API 就够 |
|
单工具调用(查个天气、算个数) |
createToolCallingAgent
+ |
|
多工具 + 多步推理(ReAct) |
createReactAgent
(LangGraph 预构建) |
|
需要条件分支、循环重试、错误恢复 |
LangGraph |
|
需要人工审批、暂停恢复 |
LangGraph + interrupt |
|
多 Agent 协作 |
LangGraph + Supervisor / Multi-Agent |
简单说:如果你发现自己在 AgentExecutor 里写了很多 hack 来绕过它的限制,那就是该切 LangGraph 的信号。
▎AgentExecutor 已经"退休"了
顺便说一句:LangChain 官方已经把 AgentExecutor 标记为 deprecated(弃用),推荐所有新项目直接用 LangGraph。
但这不是说前面两篇白学了——createToolCallingAgent 和 ReAct 的概念是通用的,只是执行引擎从 AgentExecutor 换成了 LangGraph。你理解了 Agent 的原理,切过来只是换个"壳"的事。
迁移也很简单。原来的:
typescript
// 旧写法
const agent = createToolCallingAgent({ llm, tools, prompt });
const executor = newAgentExecutor({ agent, tools });
const result = await executor.invoke({ input: "..." });
新写法:
typescript
// 新写法(LangGraph 预构建 ReActAgent)
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const agent = createReactAgent({ llm, tools });
const result = await agent.invoke({
messages: [{ role: "user", content: "..." }],
});
API 风格变了,但核心逻辑没变——还是 LLM + 工具 + 循环。
▎生产环境的加分项:Checkpointer
LangGraph 有一个生产级的杀手功能——Checkpointer(检查点)。
它能把 Graph 执行过程中的每一个状态快照都存下来(存到 SQLite、PostgreSQL、Redis 等)。这意味着:
- ▸断点恢复
:Graph 执行到一半出错了,修完 bug 后可以从断点继续,不用从头跑
- ▸时间旅行调试
:回到任何一个历史状态,查看那个时刻 Agent 看到了什么、做了什么
- ▸人工审批
:暂停 Graph,等人审批后从暂停点继续
typescript
import { MemorySaver } from "@langchain/langgraph";
const checkpointer = new MemorySaver(); // 内存版,开发调试用
// 生产环境用 PostgresSaver / SqliteSaver
const graph = newStateGraph(AgentState)
// ...定义节点和边...
.compile({ checkpointer });
// 每次调用带个 thread_id,状态就自动持久化了
const result = await graph.invoke(
{ messages: [newHumanMessage("...")] },
{ configurable: { thread_id: "user-123-session-1" } }
);
这个在 Chain 架构里完全没有——Chain 跑完就完了,中间过程全丢了。
▎总结
Chain 和 Graph 不是"谁好谁坏"的关系,而是"简单场景"和"复杂场景"的分水岭:
- ▸Chain
:一条线走到底,简单清晰,适合线性流程
- ▸Graph
:可以循环、分叉、暂停、回溯,适合需要"应变"的 Agent 场景
LangGraph 的三件套也很好记:
- Node
——做事的函数
- Edge
——决定下一步走哪
- State
——所有人共享的记事本
如果你之前用 AgentExecutor 觉得"够用但不够灵活",现在可以试试用 LangGraph 的 StateGraph 重写一遍。你会发现,原来 Agent 的工作流可以这么清晰地画出来。
推荐资源:
- ▸LangGraph.js 官方文档
:https://langchain-ai.github.io/langgraphjs/
- ▸LangGraph 设计理念
:https://blog.langchain.com/building-langgraph/
- ▸从 AgentExecutor 迁移指南
:https://js.langchain.com/docs/how_to/migrate_agent/

往期推荐
AI Agent 是怎么"想一步做一步"的?拆解 ReAct 模式

从零开始:用 LangChain.js 构建你的第一个 Tool-Calling Agent


最后

点个在看
支持我吧
-
-
欢迎加我微信,拉你进技术群,长期交流学习...
-
欢迎关注「前端Q」,认真学前端,做个专业的技术人...
-
更多推荐



所有评论(0)