认识 LangGraph
用状态图构建可靠、可调试、可暂停恢复的 AI 智能体
从"裸调 LLM"到"工程化智能体"的第一步为什么需要它
你有没有试过直接写代码调 LLM 来做复杂任务?
裸调 LLM 的困境
- 多轮对话的上下文散落在各处,没有统一的状态管理
- 流程中途崩了?从头再来——没有持久化
- 想让人审一下中间结果?只能复制粘贴——没有内置的人机交互
- 出 bug 了只能 console.log 猜——没有可视化调试
- LLM 调用、工具调用、条件分支全缠在一起,代码变成意大利面条
LangGraph 的解法
- 所有节点共享一个 State 对象,状态一目了然
- 每一步自动存快照,崩了从上次位置继续
- 随时
interrupt()暂停,等人工确认后恢复 - LangSmith 可视化追踪每一次调用和状态变更
- 节点 + 边 + 条件路由,流程即代码
开始之前
你需要会什么
- TypeScript / JavaScript 基础(函数、类型、async/await)
- 了解 LLM 的基本概念(prompt、token、tool calling)
- 会用 npm 初始化项目、安装依赖
- 接触过至少一个 LLM API(OpenAI、Anthropic 都行)
读完后你应该能
- 说清 LangGraph 是什么、解决什么问题
- 写出第一个能跑的 StateGraph Hello World
- 理解节点、边、状态这三大核心概念
- 掌握"五步法"来设计一个完整智能体
- 知道什么时候该用 LangGraph,什么时候不该用
本章不会深入
- 具体的工作流模式(链式、并行、路由等)
- 持久化与 checkpointer 的实现细节
- 流式处理、中断、时间旅行等进阶功能
- 生产环境部署方案
$ npm install @langchain/langgraph @langchain/core
LangGraph 是什么
一个低级编排框架和运行时,用于构建长时间运行的、有状态的智能体
说白了,LangGraph 帮你把一个复杂的 AI 任务拆成一个个节点(函数),用边(条件分支)连起来,再给它们一个共享的状态笔记本。整个执行过程可追踪、可暂停、可恢复。已被 Klarna、Uber、J.P. Morgan 等公司投入生产使用。

图:Hello World StateGraph——最简有向无环图
【AI 生图提示词】请生成一张中文技术流程图,风格简洁、配色柔和、适合教学: - 画面主体:一条从左到右的横向流程,共 3 个节点,用粗箭头依次连接 - 节点 1(圆形,绿色边框,标注 START):起点——图的入口,接收用户输入 - 节点 2(圆角矩形,橙色边框,标注 mock_llm):LLM 节点——接收 State(messages),返回 AI 回复,内部示意 { messages: [{ role: "ai", content: "hello world" }] } - 节点 3(圆形,红色边框,标注 END):终点——图的出口,返回最终结果 - 在流程下方画一个共享状态对象(虚线圆角矩形,蓝色边框,标注 State / messages),用虚线双向箭头连接到 mock_llm 节点,表示该节点读写共享状态 - 各元素关系:START → mock_llm → END 是线性串联的有向边;State 对象与 mock_llm 节点之间是读写关系(虚线) - 关键标注:在 START 上方标注 invoke({ messages: [...] }),在 END 下方标注 result.messages 最后一条即 AI 回复 - 配色建议:浅米色背景,节点使用白色填充,START 绿色边框,mock_llm 橙色(#D06519)边框,END 红色边框,State 蓝色虚线边框,箭头使用深灰色,说明文字用深灰色 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色、小到看不清的文字
// 安装依赖
$ npm install @langchain/langgraph @langchain/core
// Hello World —— 最简 StateGraph
import { StateSchema, MessagesValue, GraphNode, StateGraph, START, END } from "@langchain/langgraph";
const State = new StateSchema({
messages: MessagesValue,
});
// 定义一个节点:接收状态,返回更新
const mockLlm: GraphNode<typeof State> = (state) => {
return { messages: [{ role: "ai", content: "hello world" }] };
};
// 构建图:起点 → 节点 → 终点
const graph = new StateGraph(State)
.addNode("mock_llm", mockLlm)
.addEdge(START, "mock_llm")
.addEdge("mock_llm", END)
.compile();
// 运行
const result = await graph.invoke({
messages: [{ role: "user", content: "hi!" }]
});
// result.messages 最后一条: { role: "ai", content: "hello world" }
ERR_MODULE_NOT_FOUND→ 先安装依赖:$ npm install @langchain/langgraph @langchain/coreTypeError: StateSchema is not a constructor→ 检查是否使用 ESM("type": "module"inpackage.json)或用.mjs后缀
StateSchema(定义共享状态)、addNode(添加节点函数)、addEdge(连线)。这就是 LangGraph 的最小知识集。
核心优势
LangGraph 不抽象你的提示词或架构,它提供的是底层基础设施
| 优势 | 说明 | 什么时候需要 |
|---|---|---|
| 持久化执行 | 每步自动存快照,任务崩了能从上次位置恢复 | 长时间运行的任务、LLM 调用可能超时的场景 |
| 人机交互 | 在任意节点暂停执行,等人审批或修改后再继续 | 高风险操作(发邮件、改数据)需要人工确认时 |
| 全面记忆 | 短期记忆(线程内)+ 长期记忆(跨会话) | 多轮对话、记住用户偏好 |
| LangSmith 调试 | 可视化追踪执行路径、状态变化、token 消耗 | 调试复杂智能体行为、评估输出质量 |
| 生产部署 | 专为有状态、长时间运行工作流设计的扩展基础设施 | 从原型走向生产、需要稳定性和可观测性时 |
灵感来源
LangGraph 受 Pregel(Google 图计算模型)、Apache Beam(大数据处理)、NetworkX(图网络库)启发,由 LangChain Inc. 构建,但可以不依赖 LangChain 使用。
产品生态
| 产品 | 做什么 |
|---|---|
| LangSmith Observability | 追踪请求、评估输出、监控部署——一个地方看清全貌 |
| LangSmith Deployment | 专为长时间运行、有状态工作流构建的部署和扩展平台 |
| LangChain | 模型和工具集成层,包含基于 LangGraph 构建的预置智能体 |
三者都是可选的——LangGraph 本身完全独立,可以单独使用。
五个步骤
用 LangGraph 构建智能体的通用方法论

图:LangGraph 五步构建法——从需求到可运行智能体
【AI 生图提示词】请生成一张中文技术流程图,风格简洁、配色柔和、适合教学: - 画面主体:一条从左到右的横向流水线,共 5 个大节点,用粗箭头依次连接,每个节点内部有简短说明文字 - 节点 1(圆角矩形,橙色边框):拆步骤——把工作流映射为离散的节点,每个节点只做一件事 - 节点 2(圆角矩形,橙色边框):分类型——判断每个节点是 LLM 调用、数据获取、外部操作还是用户输入 - 节点 3(圆角矩形,橙色边框):设计状态——定义共享的 State 对象,存储原始数据而非格式化文本 - 节点 4(圆角矩形,橙色边框):写节点——将每个步骤实现为接收状态、返回更新的函数 - 节点 5(圆角矩形,橙色边框):连线——用边和条件边把节点连成完整的图 - 在流水线下方标注:State 对象由第 3 步定义,被第 4-5 步使用(用虚线从底部 State 连接到节点 3、4、5) - 各元素关系:五个节点是线性串联关系,前一步的产出是后一步的输入;底部 State 是节点 3 的产出物,节点 4 基于它写函数,节点 5 基于它连边(虚线表示依赖关系,不是运行时读写) - 关键标注:每个节点内标注序号和一句话说明,底部 State 标注"第 3 步定义,第 4-5 步使用" - 配色建议:浅米色背景,节点使用白色圆角矩形配橙色(#D06519)边框,箭头使用深橙色,说明文字用深灰色 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色、小到看不清的文字
1. 拆步骤
把工作流映射为离散节点
2. 分类型
LLM / 数据 / 操作 / 用户
3. 设计状态
定义共享 State 对象
4. 写节点
实现为函数 + 错误处理
5. 连线
边 + 条件边 + Command
第一步:拆步骤
把工作流映射为离散步骤——每个节点只做一件事
假设你要构建一个邮件处理智能体,需求如下:

图:邮件处理智能体——工作流全景
【AI 生图提示词】请生成一张中文技术流程图,风格简洁、配色柔和、适合教学: - 画面主体:一条从上到下的纵向流程图,共 8 个节点,用箭头依次连接 - 节点 1(圆形,绿色边框,标注 START):入口 - 节点 2(圆角矩形,蓝色边框,标注 Read Email):读取邮件——提取并解析客户邮件内容 - 节点 3(圆角矩形,紫色边框,标注 Classify Intent):分类——用 LLM 按紧急程度和主题分类 - 从 Classify Intent 分出三条条件边(用菱形判断标记,标注"条件路由"): - 左分支 → 节点 4a(圆角矩形,蓝色边框,标注 Doc Search):文档搜索——在知识库查询相关信息 - 中分支 → 节点 4b(圆角矩形,橙色边框,标注 Bug Track):Bug 跟踪——在跟踪系统中创建 Bug - 右分支 → 节点 4c(圆角矩形,灰色边框,标注 Human Review):人工审核——升级给人工处理 - 节点 4a 和 4b 的箭头汇聚到节点 5(圆角矩形,紫色边框,标注 Draft Reply):起草回复——生成适当的回复邮件 - 从 Draft Reply 分出两条条件边: - 一条 → Send Reply(已确认无需人工) - 一条 → Human Review(需要人工审批) - Human Review 的输出可回到 Draft Reply(修改后重新起草)或到 Send Reply - 节点 6(圆角矩形,橙色边框,标注 Send Reply):发送回复——发送邮件 - 节点 7(圆形,红色边框,标注 END):终点 - 在流程左侧画一个共享状态对象(虚线圆角矩形,蓝色边框,标注 State),用虚线连接到所有节点 - 各元素关系:START → Read Email → Classify Intent →(条件分支)→ Doc Search / Bug Track → Draft Reply →(条件分支)→ Send Reply → END;Human Review 可回到 Draft Reply 或直达 Send Reply - 关键标注:条件分支处用菱形标记"条件路由",汇聚处标注"合并";每个节点内标注名称和一句话说明 - 配色建议:浅米色背景,LLM 节点紫色(#7C3AED)边框,数据节点蓝色(#0284C7)边框,操作节点橙色(#D06519)边框,人工节点灰色边框,START 绿色,END 红色,State 蓝色虚线,箭头深灰色 - 画面比例:9:16(纵向) - 避免:过度装饰、3D 效果、杂乱的颜色、小到看不清的文字
智能体应该:
- 阅读收到的客户邮件
- 按紧急程度和主题进行分类
- 搜索相关文档以回答问题
- 起草适当的回复
- 将复杂问题升级给人工处理
拆成节点之后,每个节点就是一个只做一件事的函数:
| 节点 | 职责 | 下一步 |
|---|---|---|
Read Email | 提取并解析邮件内容 | → Classify Intent(固定) |
Classify Intent | 用 LLM 分类紧急程度和主题 | → 视情况路由(条件) |
Doc Search | 在知识库查询相关信息 | → Draft Reply(固定) |
Bug Track | 在跟踪系统中创建或更新 Bug | → Draft Reply(固定) |
Draft Reply | 生成适当的回复 | → 视情况路由(条件) |
Human Review | 升级给人工审批或处理 | → 视情况路由(条件) |
Send Reply | 发送邮件回复 | → END |
第二步:分类型
判断每个节点属于哪种类型——决定了实现方式
拆完步骤之后,你需要搞清楚每个节点到底在做什么。不同的类型,实现方式完全不同。
LLM 步骤
需要理解、分析、生成文本或做推理决策。
示例:Classify Intent、Draft Reply
调用 LLM API
数据步骤
需要从外部源检索信息。
示例:Document Search、Customer History Lookup
调用数据库 / API
操作步骤
需要执行外部操作(发邮件、建 Bug)。
示例:Send Reply、Bug Track
执行副作用
用户输入步骤
需要人工干预或确认。
示例:Human Review
调用 interrupt()
第三步:设计状态
状态是所有节点共享的笔记本——存储原始数据,不存格式化文本
说白了,State 就是一个对象,每个节点都能读它、往里写东西。设计状态的核心原则是:
应该放入状态
- 需要在步骤间持久化的数据
- 多个下游节点都需要的信息
- 重新获取代价高的数据
- 调试时需要查看的中间结果
不应该放入状态
- 可以从其他数据推导出来的(需要时再算)
- 格式化后的提示词文本(在节点内按需格式化)
- 临时变量(节点函数内部处理)
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(),
// 原始搜索/API 结果(重新获取代价高)
searchResults: z.array(z.string()).optional(),
// 生成的内容
responseText: z.string().optional(),
});
第四步:写节点
节点就是一个函数——接收当前状态,返回对状态的更新
说白了,每个节点就是 (state) => { return { key: newValue } }。返回值会合并到状态里,传给下一个节点。
// 分类节点:调用 LLM 分析邮件意图
const classifyIntent: GraphNode<typeof EmailAgentState> = async (state) => {
const prompt = `请分析以下邮件的意图和紧急程度:
邮件内容:${state.emailContent}
发件人:${state.senderEmail}`;
const result = await llm.invoke(prompt);
return { classification: result };
};
// 文档搜索节点:从知识库查询
const searchDocs: GraphNode<typeof EmailAgentState> = async (state) => {
const results = await knowledgeBase.search(state.classification?.topic);
return { searchResults: results };
};
错误处理策略——不同错误,不同解法
| 错误类型 | 谁来修 | 策略 | LangGraph 怎么做 |
|---|---|---|---|
| 瞬态错误(网络抖动、限流) | 系统自动 | 自动重试 | retryPolicy: { maxAttempts: 3 } |
| LLM 可恢复(工具失败、解析出错) | LLM | 把错误写进状态,LLM 自行调整 | 循环回 LLM 节点重试 |
| 用户可修复(信息缺失、指令不清) | 人类 | 暂停,等人输入 | interrupt() 暂停等待 |
| 意外错误 | 开发者 | 向上冒泡 | 让错误抛出,检查日志 |
// 给容易出网络问题的节点加重试策略
workflow.addNode(
"searchDocumentation",
searchDocumentation,
{ retryPolicy: { maxAttempts: 3, initialInterval: 1.0 } }
);
第五步:连线
用边、条件边和 Command 对象把节点连成完整的图
最后一步,把所有节点连起来。LangGraph 提供三种连线方式:
普通边 addEdge
A 执行完一定到 B,没有分支。
addEdge(START, "readEmail")
条件边 addConditionalEdges
A 执行完后,根据状态决定去哪个节点。
addConditionalEdges("classify", router)
Command 对象
节点内部直接指定下一个节点 + 状态更新。路由逻辑在节点内完成。
return new Command({ goto: "nodeX" })
import { MemorySaver, StateGraph, START, END } from "@langchain/langgraph";
// 创建图并连线
const workflow = new StateGraph(EmailAgentState)
// 添加节点
.addNode("readEmail", readEmail)
.addNode("classifyIntent", classifyIntent)
.addNode("searchDocumentation", searchDocumentation, {
retryPolicy: { maxAttempts: 3 } // 瞬态故障自动重试
})
.addNode("draftResponse", draftResponse)
.addNode("humanReview", humanReview)
.addNode("sendReply", sendReply)
// 连线——只连必要的边
.addEdge(START, "readEmail")
.addEdge("readEmail", "classifyIntent")
.addEdge("sendReply", END);
// 用 checkpointer 编译,启用持久化
const memory = new MemorySaver();
const app = workflow.compile({ checkpointer: memory });
动手练习
设计一个简单的文档处理智能体
现在轮到你了。停下来,用刚学的五步法设计一个文档处理智能体。需求如下:
智能体应该:
1. 接收用户上传的文档(纯文本)
2. 分析文档类型(合同、报告、邮件、其他)
3. 提取关键信息(日期、金额、人名等)
4. 生成结构化摘要
5. 如果信息不确定,暂停询问用户
6. 输出最终的 JSON 结果
任务 1:拆节点
- 这个流程需要几个节点?
- 每个节点的职责是什么?
- 哪些是 LLM 步骤?哪些是用户输入步骤?
任务 2:设计状态
- State 里需要哪些字段?
- 哪些字段用
.optional()? - 想想"原始数据 vs 格式化文本"原则
任务 3:画连线
- 哪些边是固定的?
- 哪些需要条件边?
- 在哪里需要 interrupt?
参考答案
一种可行的设计方案——不是唯一答案,但逻辑合理
| 节点 | 类型 | 职责 | 下一步 |
|---|---|---|---|
receiveDoc | 数据 | 接收并预处理文档文本 | → classifyDoc(固定) |
classifyDoc | LLM | 分析文档类型 | → extractInfo(固定) |
extractInfo | LLM | 提取关键信息 | → 视置信度(条件) |
confirmInfo | 用户输入 | 不确定时询问用户 | → generateSummary(固定) |
generateSummary | LLM | 生成结构化摘要 | → END |
import { StateSchema, StateGraph, START, END, MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
// 状态设计——只存原始数据
const DocState = new StateSchema({
rawText: z.string(), // 原始文档文本
docType: z.string().optional(), // 文档类型
extractedInfo: z.record(z.any()).optional(), // 提取的关键信息
needsConfirmation: z.boolean().optional(), // 是否需要用户确认
finalJson: z.string().optional(), // 最终 JSON 输出
});
// 构建图
const docAgent = new StateGraph(DocState)
.addNode("receiveDoc", receiveDoc)
.addNode("classifyDoc", classifyDoc)
.addNode("extractInfo", extractInfo)
.addNode("confirmInfo", confirmInfo)
.addNode("generateSummary", generateSummary)
.addEdge(START, "receiveDoc")
.addEdge("receiveDoc", "classifyDoc")
.addEdge("classifyDoc", "extractInfo")
.addConditionalEdges("extractInfo", (state) => {
return state.needsConfirmation ? "confirmInfo" : "generateSummary";
})
.addEdge("confirmInfo", "generateSummary")
.addEdge("generateSummary", END)
.compile({ checkpointer: new MemorySaver() });
本章回顾
到这里,你已经掌握了 LangGraph 的全貌
你学到了什么
- LangGraph 是一个低级编排框架,用于构建有状态的智能体
- 三大核心概念:节点(函数)、边(流程)、状态(共享记忆)
- 五步构建法:拆步骤 → 分类型 → 设计状态 → 写节点 → 连线
- 状态存原始数据,提示词在节点内按需格式化
- 四种错误处理策略:自动重试 / LLM 自愈 / 人工介入 / 向上冒泡
- 图结构精简,路由逻辑在节点内完成
后续章节预告
- 基础模式:提示词链、并行化、路由、编排者-工作者、评估者-优化者
- 快速上手:完整实现一个带工具调用的计算器智能体
- 持久化:checkpointer、线程、检查点的深入理解
- 流式处理:token 级流式输出、自定义数据流
- 中断与人机交互:interrupt 和 Command 的完整用法
- 记忆管理:短期记忆 + 长期记忆 + 语义搜索
延伸阅读
继续本课程
- 下一章:基础工作流模式 — 提示词链、并行化、路由等经典模式
- 快速上手 — 完整实现一个带工具调用的智能体
官方文档
- LangGraph.js 官方文档 — 完整的 API 参考和教程
- LangSmith — 可视化调试和部署平台
- GitHub 仓库 — 源码和示例