主题
LangGraph 思维模式:5 步构建 AI Agent 方法论
简单来说
LangGraph 就是教你如何把一个复杂的 AI 任务拆成"流水线工序",每个工序干一件事、共享一块"公告板"来传递信息,然后按规则决定下一步干啥。本质上是让你的 AI Agent 从"一团乱麻"变成"井井有条的流程图"。
🎯 本节目标
学完本节,你将能够:
- 掌握 LangGraph 的 5 步构建方法论
- 理解 Node、State、Edge、Command 的设计原则
- 学会 4 种错误处理策略
- 能够独立设计一个完整的 Agent 架构
核心痛点与解决方案
痛点:AI 做复杂任务时像没头苍蝇
想象一下:你让一个 AI 处理客服邮件,它要同时考虑:
- 这封邮件在说什么?
- 紧急程度如何?
- 要不要查文档?
- 要不要创建 Bug 工单?
- 需不需要人工介入?
- 最后怎么回复?
如果不做任何架构设计,你的代码可能会变成这样:
typescript
if (这个) {
then 那个
} else if (另一个) {
then 又一个
} else if (...) {
// 无限嵌套...
}典型的"面条代码",调试噩梦。
| 痛点 | 具体表现 |
|---|---|
| 逻辑混乱 | 一个巨大的 if-else 函数,改一处动全身 |
| 状态乱飞 | 中间数据到处传,不知道谁用了什么 |
| 无法恢复 | 出错了只能从头再来,浪费算力 |
| 调试困难 | 打断点打到怀疑人生 |
解决:LangGraph 的分而治之
| 痛点 | LangGraph 的解法 |
|---|---|
| 逻辑混乱 | 把流程拆成独立的 Node(节点),每个节点只干一件事 |
| 状态乱飞 | 用统一的 State(状态) 作为"公告板",所有数据都贴在这 |
| 无法恢复 | 用 Checkpointer(检查点) 保存进度,失败可从断点恢复 |
| 调试困难 | 每个节点的输入输出清清楚楚,配合 LangSmith 可视化 |

生活化类比:专业客服中心
想象你开了一家客服中心,每天收到海量客户邮件。你不可能让一个人从头到尾处理所有事情,所以你设计了一套流水线:
[ 分拣员小王 ]
│
┌────────────────────┼────────────────────┐
│ │ │
[ 查文档的小李 ] [ 记 Bug 的小张 ] [ 主管老陈 ]
│ │ │
└────────────────────┼────────────────────┘
│
[ 写回复的小美 ]
│
┌───────────────┴───────────────┐
│ │
[ 主管老陈审批 ] [ 直接发送 ]LangGraph 概念对照表:
| LangGraph 概念 | 客服中心类比 | 具体作用 |
|---|---|---|
| Node(节点) | 每个工位上的员工 | 小王负责分类、小李负责查文档、小美负责写回复 |
| State(状态) | 流转的工单夹 | 里面夹着客户邮件原文、分类结果、查到的资料、草稿回复 |
| Edge(边) | 工单传递路线 | 小王分类完给小李/小张/老陈 |
| Command(命令) | 工单上的批注 | "分类完毕,这是技术问题,转给小李查文档" |
| interrupt() | 等待主管签字 | 主管不在?先把工单放着,等主管回来继续 |
| Checkpointer | 每个工位的存档柜 | 万一停电了,恢复后从最近完成的工位继续 |

5 步构建方法论
Step 1:画出工作流程图
首先,把你要自动化的流程可视化出来。
示例需求:构建一个客服邮件处理 Agent
用户需求:
1. 读取客户邮件
2. 分类(紧急程度、问题类型)
3. 简单问题 → 查文档
4. Bug 报告 → 创建工单
5. 紧急/复杂问题 → 人工介入
6. 起草回复
7. 高优先级需审核后发送画成流程图:
START
│
▼
┌──────────┐
│ 读取邮件 │
└────┬─────┘
│
▼
┌──────────┐
│ 分类意图 │
└────┬─────┘
│
┌────┼────┬────────────┐
│ │ │ │
▼ ▼ ▼ ▼
查文档 Bug 人工审核 直接起草
│ 工单 │ │
│ │ │ │
└────┴──────┴───────────┘
│
▼
┌──────────┐
│ 起草回复 │
└────┬─────┘
│
┌──────┴──────┐
│ │
▼ ▼
人工审核 直接发送
│ │
└──────┬──────┘
│
▼
END💡 关键洞察:
- 有些节点做决策(分类意图、起草回复决定是否需要审核)
- 有些节点做执行(查文档、发送邮件)
- 先画图,再写代码

Step 2:识别每个节点的类型
不同类型的节点需要不同的处理方式:
| 节点类型 | 特点 | 示例 |
|---|---|---|
| LLM 步骤 | 需要理解、分析、生成文本 | 分类意图、起草回复 |
| 数据步骤 | 从外部获取信息 | 查文档、查客户历史 |
| 动作步骤 | 执行外部操作 | 发送邮件、创建工单 |
| 人工步骤 | 需要人类介入 | 审核高优先级回复 |
为每个节点定义:
typescript
// LLM 步骤
classifyIntent: {
静态上下文: "分类标准、紧急程度定义",
动态上下文: "邮件内容、发件人信息",
期望输出: "结构化的分类结果"
}
// 数据步骤
searchDocumentation: {
参数: "从意图和主题构建查询",
重试策略: "是,指数退避",
缓存: "常见查询可缓存"
}
// 动作步骤
sendReply: {
执行时机: "审核通过后",
重试策略: "是,网络问题重试",
不可缓存: "每次发送都是唯一操作"
}
// 人工步骤
humanReview: {
上下文: "原邮件、草稿回复、紧急程度",
期望输入: "通过/拒绝 + 可选的修改内容",
触发条件: "高优先级、复杂问题"
}
Step 3:设计 State(状态)
State 是所有节点共享的"公告板",是整个系统的核心。
设计原则:只存原始数据,不存格式化文本
typescript
import { StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const EmailClassificationSchema = z.object({
intent: z.enum(["question", "bug", "billing", "feature", "complex"]),
urgency: z.enum(["low", "medium", "high", "critical"]),
topic: z.string(),
summary: z.string(),
});
const EmailAgentState = new StateSchema({
emailContent: z.string(),
senderEmail: z.string(),
emailId: z.string(),
classification: EmailClassificationSchema.optional(),
searchResults: z.array(z.string()).optional(),
customerHistory: z.record(z.string(), z.any()).optional(),
responseText: z.string().optional(),
});💡 人话解读:
"我定义了一个工单夹,里面可以放:邮件原文、发件人、分类结果、查到的资料、草稿回复。有些格子一开始是空的(optional),后面的工位会填上。"
为什么只存原始数据?
| 存原始数据 | 存格式化文本 |
|---|---|
| 不同节点可以用不同方式展示 | 格式固定,难以复用 |
| 改提示词不用改状态结构 | 改格式要改状态定义 |
| 调试时看到真实数据 | 调试时看到加工后的内容 |
Step 4:构建节点函数
节点就是一个简单的函数:读取状态 → 干活 → 返回更新
分类节点示例:
typescript
import { Command, GraphNode } from "@langchain/langgraph";
import { ChatAnthropic } from "@langchain/anthropic";
const llm = new ChatAnthropic({ model: "claude-sonnet-4-5-20250929" });
const classifyIntent: GraphNode<typeof EmailAgentState> = async (state) => {
const structuredLlm = llm.withStructuredOutput(EmailClassificationSchema);
const classificationPrompt = `
分析这封客户邮件并分类:
邮件内容: ${state.emailContent}
发件人: ${state.senderEmail}
请提供意图、紧急程度、主题和摘要。
`;
const classification = await structuredLlm.invoke(classificationPrompt);
let nextNode: string;
if (classification.intent === "billing" || classification.urgency === "critical") {
nextNode = "humanReview";
} else if (classification.intent === "question" || classification.intent === "feature") {
nextNode = "searchDocumentation";
} else if (classification.intent === "bug") {
nextNode = "bugTracking";
} else {
nextNode = "draftResponse";
}
return new Command({
update: { classification },
goto: nextNode,
});
};💡 人话解读:
"分拣员小王的工作:用 AI 判断这封邮件是什么类型、紧不紧急。然后根据情况派单:
- 紧急账单问题?直接找主管!
- 普通问题?先去查查资料
- 说是 Bug?建个工单追踪
- 其他情况?直接写回复"
人工审核节点示例:
typescript
import { interrupt } from "@langchain/langgraph";
const humanReview: GraphNode<typeof EmailAgentState> = async (state) => {
const humanDecision = interrupt({
originalEmail: state.emailContent,
draftResponse: state.responseText,
urgency: state.classification?.urgency,
action: "请审核这封回复",
});
if (humanDecision.approved) {
return new Command({
update: { responseText: humanDecision.editedResponse || state.responseText },
goto: "sendReply",
});
} else {
return new Command({ update: {}, goto: END });
}
};💡 人话解读:
"到了主管老陈这里:
- 先把工单放他桌上(interrupt),然后整个流水线暂停
- 老陈啥时候回来、看完、签字,流水线才继续
- 签字同意?往下送去发送
- 不同意?这单结束,他自己处理"

Step 5:连接节点成图
最后一步:把所有节点连起来。
typescript
import { StateGraph, START, END, MemorySaver } from "@langchain/langgraph";
const workflow = new StateGraph(EmailAgentState)
.addNode("readEmail", readEmail)
.addNode("classifyIntent", classifyIntent)
.addNode("searchDocumentation", searchDocumentation, {
retryPolicy: { maxAttempts: 3 }
})
.addNode("bugTracking", bugTracking)
.addNode("draftResponse", draftResponse)
.addNode("humanReview", humanReview)
.addNode("sendReply", sendReply)
.addEdge(START, "readEmail")
.addEdge("readEmail", "classifyIntent")
.addEdge("sendReply", END);
const memory = new MemorySaver();
const app = workflow.compile({ checkpointer: memory });💡 人话解读:
"把所有工位连起来:
- 起点 → 读邮件 → 分类(分类后各自决定去哪)
- 发送完毕 → 终点
- 查文档工位如果网络抖动,自动重试 3 次
- 整条线开启自动存档功能"
注意:图结构很简洁,因为路由逻辑在节点内部通过 Command 处理。
错误处理四象限
不同的错误需要不同的处理策略:
| 错误类型 | 谁来修 | 策略 | 代码示例 |
|---|---|---|---|
| 短暂错误 | 系统自动 | 重试策略 | 网络抖动、API 限流 |
| LLM 可恢复 | AI 自己 | 存错误到 State,让 AI 重试 | 工具调用失败 |
| 需要用户 | 人类 | interrupt() 暂停 | 缺少账户 ID |
| 意外错误 | 开发者 | 直接抛出 | 未知 Bug |
1. 短暂错误:自动重试
typescript
workflow.addNode(
"searchDocumentation",
searchDocumentation,
{ retryPolicy: { maxAttempts: 3, initialInterval: 1.0 } }
);2. LLM 可恢复:存错误让 AI 看到
typescript
const executeTool: GraphNode<typeof State> = async (state) => {
try {
const result = await runTool(state.toolCall);
return new Command({
update: { toolResult: result },
goto: "agent",
});
} catch (error) {
return new Command({
update: { toolResult: `工具错误: ${error}` },
goto: "agent"
});
}
};3. 需要用户:暂停等待
typescript
const lookupCustomer: GraphNode<typeof State> = async (state) => {
if (!state.customerId) {
const userInput = interrupt({
message: "需要客户 ID",
request: "请提供客户的账户 ID",
});
return new Command({
update: { customerId: userInput.customerId },
goto: "lookupCustomer",
});
}
// 继续处理...
};4. 意外错误:直接抛出
typescript
const sendReply: GraphNode<typeof State> = async (state) => {
try {
await emailService.send(state.responseText);
} catch (error) {
throw error; // 让开发者去修
}
};
真实场景案例
场景 1:简单问题(全自动)
用户:"怎么重置密码?"
读邮件 → 分类(问题类) → 查FAQ文档 → 起草回复 → 直接发送全程自动,3 秒搞定。
场景 2:紧急问题(人工介入)
用户:"我被扣了两次钱!!!"
读邮件 → 分类(紧急+账单) → 【暂停等主管】
│
主管上线 → 审核/修改回复 → 恢复发送系统不会傻傻地自动回复敏感问题,而是等人工介入。
场景 3:服务器故障恢复
正在查文档时,数据库宕机了
读邮件 [存档] → 分类 [存档] → 查文档 [失败!]
│
服务器恢复后
│
从"查文档"重试(不用从头来)这就是 Checkpointer 的威力。
设计原则总结
原则 1:一个节点只干一件事
好处:
- 调试方便(知道是哪个环节出问题)
- 恢复精确(不用重做已完成的工作)
- 可复用(同一个查文档节点可以用在多个流程)
原则 2:State 只存原始数据
好处:
- 灵活格式化(不同节点可以用不同方式展示同一数据)
- 便于调试(看到的是原始数据,不是加工后的提示词)
- 易于演进(改提示词不用改状态结构)
原则 3:节点自己决定下一步
好处:
- 逻辑内聚(分类逻辑和路由逻辑放一起)
- 图结构简洁(不用画一堆条件边)
- 可追踪(看节点代码就知道它能去哪)
一图总结
┌─────────────────────────────────────────────────────────────┐
│ LangGraph 5 步方法论 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Step 1: 画流程图 │
│ ↓ │
│ Step 2: 识别节点类型(LLM/数据/动作/人工) │
│ ↓ │
│ Step 3: 设计 State(只存原始数据) │
│ ↓ │
│ Step 4: 实现节点函数(Command 控制路由) │
│ ↓ │
│ Step 5: 连接成图(addEdge + compile) │
│ │
├─────────────────────────────────────────────────────────────┤
│ 错误处理策略 │
│ │
│ 短暂错误 ──> retryPolicy(自动重试) │
│ LLM 错误 ──> 存进 State,让 AI 看到 │
│ 需要人工 ──> interrupt()(暂停等待) │
│ 意外错误 ──> throw(开发者修复) │
│ │
└─────────────────────────────────────────────────────────────┘
核心要点回顾
- 5 步方法论:画图 → 识别类型 → 设计 State → 实现节点 → 连接成图
- 节点原则:每个节点只做一件事,通过 Command 决定下一步
- 状态原则:只存原始数据,在节点内按需格式化
- 错误四象限:不同错误不同处理,让对的人/系统修对的错误
- 核心思想:分而治之,让复杂系统变得可控、可调试、可恢复
下一步学习
掌握了思维模式,接下来深入 API:
- 🔧 06-Graph API 详解:StateGraph 核心 API 完全指南
- 🎯 07-Functional API:另一种编程风格
- 🔄 08-工作流与 Agent:6 种核心工作流模式
📅 更新时间:2026-02-22