点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

前两篇我们用 LangChain.js 搭了 Tool-Calling Agent,也拆解了 ReAct 模式的"想→做→看"循环。

如果你只是做一个简单的"查天气、做计算"的 Agent,前面讲的 AgentExecutor 完全够用。但当需求变复杂——比如要加人工审批、要根据结果走不同分支、出错了要自动重试、甚至多个 Agent 协作——你就会发现 Chain(链式架构)开始"兜不住"了。

这时候就该轮到 LangGraph 登场了。

▎Chain 到底差在哪?

先说清楚 Chain 的问题,你才能理解为什么需要 Graph。

Chain 的执行模式是:A → B → C → D,一条直线走到底。

这就像流水线——每个工位做完自己的事,传给下一个工位。简单、清晰、好理解。但问题是:

Chain vs Graph

问题一:中间断了就废了

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

LangGraph 三件套
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

 + AgentExecutor

多工具 + 多步推理(ReAct)

createReactAgent

(LangGraph 预构建)

需要条件分支、循环重试、错误恢复

LangGraph StateGraph 自定义

需要人工审批、暂停恢复

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 的三件套也很好记:

  1. Node

    ——做事的函数

  2. Edge

    ——决定下一步走哪

  3. State

    ——所有人共享的记事本

如果你之前用 AgentExecutor 觉得"够用但不够灵活",现在可以试试用 LangGraph 的 StateGraph 重写一遍。你会发现,原来 Agent 的工作流可以这么清晰地画出来

推荐资源:

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐