深入浅出 LangGraph —— 第1章:初识LangGraph
📖 本章学习目标
- ✅ 理解为什么传统LLM调用和简单Agent不够用
- ✅ 掌握 LangGraph 的核心定位:低级编排框架
- ✅ 了解 LangGraph 的五大核心能力
- ✅ 认识 LangGraph 在 LangChain 生态中的位置
- ✅ 完成第一个可运行的 LangGraph 程序
- ✅ 建立正确的技术选型思维
一、背景说明
在《深入浅出LangChain》中,我们已经详细介绍了:
- ✅ AI Agent的核心概念(感知-推理-行动循环)
- ✅ ReAct Agent的工作原理
- ✅ 工具调用的基本模式
- ✅ 简单的Agent实现示例
如果你还没有阅读那部分内容,强烈建议先阅读相关章节。
本章将在此基础上,聚焦于一个关键问题:当Agent逻辑变得复杂时,如何优雅地编排和管理? 这是一个从简单Agent到复杂工作流的挑战。
真实业务场景中,简单的ReAct模式往往不够用。想象你是一家电商公司的技术负责人,你想用 AI 构建一个客服系统。这个系统需要:
- 接收用户咨询(“我的订单在哪?”)
- 查询订单数据库
- 如果发现异常,触发售后流程
- 生成个性化回复
- 在必要时转接人工客服
- 记住用户的偏好和历史交互
- 支持多轮对话和上下文理解
- 在关键节点等待人工审核
- 故障后能够从断点恢复
这已经不是简单的 “感知-推理-行动” 循环了,这是一个复杂的、有状态的、需要持久化和人工干预的工作流。面临如下挑战:
- ❌ 单次 LLM 调用无法完成多步骤推理
- ❌ 没有状态管理,每次调用都是「失忆」的
- ❌ 无法处理复杂的条件分支和循环逻辑
- ❌ 工具调用失败后难以重试和恢复
- ❌ 无法在关键节点引入人工审核
- ❌ 长时任务崩溃后无法恢复
LangGraph 的诞生就是为解决上述问题而生的。它是 LangChain 团队推出的低级别 Agent 编排框架,让你用 图(Graph) 的方式描述 Agent 的行为逻辑。你可以把 LangGraph 想象成 AI Agent 的 “工作流引擎” 。就像公司的审批流程图一样,每个节点是一个处理步骤,箭头(边)决定执行路径,整个系统有状态、可追踪、可恢复。
二、核心概念详解
1、什么是 AI Agent(快速回顾)
完整内容(Agent的实现原理、ReAct模式、工具调用等)请参考《深入浅出LangChain》相关章节,这里只做简要回顾:
AI Agent(智能体):一个能够感知环境、自主做决策、采取行动以完成目标的 AI 系统。
- 感知(Perception):接收用户输入,读取工具返回结果
- 推理(Reasoning):LLM 分析当前状态,决定下一步行动
- 行动(Action):调用工具、搜索信息、执行代码
- 循环(Loop):重复以上过程,直到任务完成
2、LangGraph 的图模型
LangGraph 将 Agent 的执行逻辑建模为一张有向图(Directed Graph):
图中有三个核心元素:
| 元素 | 英文 | 类比 | 作用 | 示例 |
|---|---|---|---|---|
| 节点 | Node | 工序/工作站 | 执行具体逻辑(LLM 调用、工具执行等) | 意图识别、订单查询 |
| 边 | Edge | 流向箭头 | 决定执行路径(普通边/条件边) | 如果A则走B,否则走C |
| 状态 | State | 共享白板 | 在节点间传递和共享数据 | 对话历史、提取的实体 |
三种边的类型:
3、LangGraph 的五大核心能力
(1) 持久化执行(Durable Execution)
Agent 运行中途崩溃?没关系,从最近的检查点恢复,无需从头重来。
应用场景:
- 长时任务(如数据分析,可能运行数小时)
- 网络不稳定环境
- 需要人工审核的流程
(2) 人机交互(Human-in-the-loop)
在关键节点暂停,等待人工审核或修改后继续,适合高风险决策场景。
应用场景:
- 金融交易审批
- 医疗诊断建议
- 法律文件生成
(3) 记忆系统(Comprehensive Memory)
区分会话内的短期记忆和跨会话的长期记忆,让 Agent 真正“记住”用户。
应用场景:
- 个性化推荐
- 用户偏好学习
- 历史对话引用
(4)流式输出(Streaming)
不必等待完整结果,LLM 生成的每个 Token、每个中间步骤都可以实时推送给用户。
应用场景:
- 聊天界面实时显示
- 进度条展示
- 降低用户等待焦虑
(5) 生产级部署
配合 LangSmith 实现全链路追踪和监控,支持水平扩展的生产环境部署。
应用场景:
- 性能监控
- 错误告警
- A/B测试
4、LangGraph 在生态中的位置
⚠️ 重要提示:LangGraph 可以独立使用,不依赖 LangChain。但配合 LangChain 使用可以获得更多预构建组件,大幅减少工作量。
各层职责说明:
| 层级 | 核心功能 | 典型用途 | 是否必须 |
|---|---|---|---|
| LangChain Core | 模型抽象、提示词模板、工具定义 | 统一不同LLM的调用接口 | 可选 |
| LangGraph | 图编排、状态管理、工作流 | 构建复杂Agent逻辑 | 核心 |
| LangSmith | 追踪、评估、监控、数据集 | 生产环境可观测性 | 推荐 |
| LangGraph Platform | 云部署、API服务、扩展 | 企业级部署 | 可选 |
与其他框架对比:
| 特性 | LangGraph | AutoGen | CrewAI | Semantic Kernel |
|---|---|---|---|---|
| 语言 | JS/TS + Python | Python | Python | C# + Python |
| 编排方式 | 图结构 | 对话驱动 | 角色分工 | 插件系统 |
| 状态管理 | ✅ 内置 | ❌ 需自行实现 | ❌ 需自行实现 | ⚠️ 有限支持 |
| 持久化 | ✅ 检查点机制 | ❌ | ❌ | ⚠️ 部分支持 |
| 人机交互 | ✅ 中断/审核 | ⚠️ 有限 | ❌ | ❌ |
| 学习曲线 | 中等 | 陡峭 | 平缓 | 中等 |
| 适用场景 | 生产级复杂工作流 | 研究实验 | 简单多Agent | 企业应用集成 |
三、实践示例:第一个 LangGraph 程序
我们来构建一个最简单的 LangGraph 程序:一个能够决定是否需要搜索的“路由 Agent”。
步骤 1:安装依赖
npm install @langchain/langgraph @langchain/core @langchain/openai dotenv
说明: 这四个包是大多数 LangGraph 项目的标配:
@langchain/langgraph:核心框架@langchain/core:基础抽象(消息、工具等)@langchain/openai:OpenAI 模型集成dotenv:环境变量管理
步骤 2:配置环境变量
创建 .env 文件:
OPENAI_API_KEY=sk-your-api-key-here
加载环境变量:
import * as dotenv from 'dotenv';
dotenv.config();
✅ 推荐:永远不要在代码中硬编码 API Key,使用环境变量是基本安全实践。
步骤 3:定义 State(状态)
import { Annotation, MessagesAnnotation } from "@langchain/langgraph";
// 定义图的状态结构
const AgentState = Annotation.Root({
// 继承消息状态(内置,支持消息追加)
...MessagesAnnotation.spec,
});
/**
* 代码解读:
* - Annotation.Root:定义状态 Schema 的方法
* - MessagesAnnotation.spec:内置的消息状态,支持消息列表的自动追加
* - 后续章节会详细介绍如何自定义复杂状态
*
* MessagesAnnotation 包含:
* - messages: BaseMessage[] 数组
* - 自动使用 messagesStateReducer 进行追加
*/
步骤 4:定义节点函数
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
// 节点:调用 LLM 生成回复
async function callLLM(state: typeof AgentState.State) {
const response = await llm.invoke(state.messages);
// 返回更新的状态(自动追加到 messages 列表)
return { messages: [response] };
}
/**
* 代码解读:
* - 节点函数接收当前 state,返回状态更新
* - state.messages 包含完整的对话历史
* - 返回的 messages 会被自动追加(而非覆盖)到状态中
* - 注意:只返回需要更新的字段,不是整个state对象
*/
步骤 5:构建并运行图
import { StateGraph, START, END } from "@langchain/langgraph";
// 构建图
const graph = new StateGraph(AgentState)
.addNode("llm", callLLM) // 添加节点
.addEdge(START, "llm") // 入口:START → llm
.addEdge("llm", END) // 出口:llm → END
.compile(); // 编译为可执行图
/**
* 代码解读:
* - addNode:注册节点,第一参数是节点名(字符串ID)
* - addEdge:添加固定边(无条件流转)
* - compile():将图配置编译为可执行的运行时
* - invoke():同步执行图,返回最终状态
*/
运行图:
// 运行图
const result = await graph.invoke({
messages: [new HumanMessage("你好!什么是 LangGraph?")],
});
console.log(result.messages.at(-1)?.content);
步骤 6:运行结果
你好!LangGraph 是 LangChain 团队开发的一个低级别 Agent 编排框架,
它让你能够以图(Graph)的方式构建复杂的有状态 AI Agent 工作流...
✅ 恭喜!你已经运行了第一个 LangGraph 程序!
完整代码:
import * as dotenv from 'dotenv';
dotenv.config();
import { Annotation, MessagesAnnotation, StateGraph, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
// 1. 定义状态
const AgentState = Annotation.Root({
...MessagesAnnotation.spec,
});
// 2. 初始化模型
const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
// 3. 定义节点
async function callLLM(state: typeof AgentState.State) {
const response = await llm.invoke(state.messages);
return { messages: [response] };
}
// 4. 构建图
const graph = new StateGraph(AgentState)
.addNode("llm", callLLM)
.addEdge(START, "llm")
.addEdge("llm", END)
.compile();
// 5. 运行
const result = await graph.invoke({
messages: [new HumanMessage("你好!什么是 LangGraph?")],
});
console.log(result.messages.at(-1)?.content);
四、最佳实践和踩坑指南
💡 实践 1:TypeScript 而非 JavaScript
❌ 不好的做法:
// 使用纯 JavaScript,没有类型提示
const state = Annotation.Root({
messages: MessagesAnnotation.spec.messages
});
✅ 推荐做法:
// 使用 TypeScript,获得完整类型推断
const AgentState = Annotation.Root({
...MessagesAnnotation.spec,
});
// 类型:typeof AgentState.State 可以安全使用
原因: LangGraph 是用 TypeScript 编写的,类型推断能帮你在编译期发现错误,大幅提升开发效率。
💡 实践 2:始终使用 .env 管理密钥
❌ 不好的做法:
const llm = new ChatOpenAI({
apiKey: "sk-proj-xxx..." // 硬编码!绝对禁止
});
✅ 推荐做法:
import "dotenv/config";
const llm = new ChatOpenAI({
model: "gpt-4o-mini"
// apiKey 自动从 process.env.OPENAI_API_KEY 读取
});
原因: 硬编码的 API Key 一旦提交到代码仓库,会造成严重安全风险和意外费用。
💡 实践 3:节点函数只返回更新字段
❌ 不好的做法:
async function badNode(state: typeof AgentState.State) {
// 错误:返回整个state对象
return state;
}
✅ 推荐做法:
async function goodNode(state: typeof AgentState.State) {
// 正确:只返回需要更新的字段
return { messages: [new AIMessage("回复")] };
}
原因: LangGraph 会自动合并返回的字段到现有 state,返回整个对象会导致性能浪费。
⚠️ 常见陷阱
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
忘记 .compile() |
调用 invoke() 时报错 |
构建图后必须调用 .compile() |
| State 返回格式错误 | 节点函数返回整个 state 对象 | 只返回需要更新的字段 |
| 消息被覆盖 | 对话历史丢失 | 使用 MessagesAnnotation 而非普通数组 |
忘记 await |
Promise 对象未解析 | 所有图操作都是异步的,需要 await |
| 节点命名冲突 | 行为异常 | 节点名必须唯一,避免特殊字符 |
| 循环无限执行 | 程序卡死 | 设置最大迭代次数或明确的终止条件 |
📝 本章小结
核心知识点回顾
| 知识点 | 关键要点 | 应用场景 |
|---|---|---|
| AI Agent | 感知→推理→行动的自主循环 | 任何需要多步骤执行的 AI 任务 |
| LangGraph 定位 | 低级别 Agent 编排框架 | 构建复杂有状态工作流 |
| 图模型 | 节点(Node) + 边(Edge) + 状态(State) | 描述 Agent 执行逻辑 |
| StateGraph | 图的构建类,.compile() 生成运行时 | 所有 LangGraph 程序的起点 |
| 五大能力 | 持久化、人机交互、记忆、流式、部署 | 生产级 Agent 的关键特性 |
| 生态系统 | LangChain Core → LangGraph → LangSmith | 分层架构,按需选择 |
🎯 动手练习
练习 1:修改回复语言
- 目标:让 Agent 用英文回复
- 要求:
- 在
callLLM节点中添加系统提示词(SystemMessage) - 系统提示词要求"请用英文回复"
- 保持其他代码不变
- 在
- 验收标准:运行后输出为英文
练习 2:添加打印节点
- 目标:在 LLM 节点之前添加一个「打印输入」的节点
- 要求:
- 创建新节点
printInput,只打印state.messages - 不修改状态(返回空对象
{}) - 调整边的连接:START → printInput → llm → END
- 创建新节点
- 验收标准:运行时控制台先打印输入消息,再打印 LLM 回复
练习 3:多轮对话
- 目标:让 Agent 支持多轮对话
- 要求:
- 第一次调用:
invoke({messages: [HumanMessage("我叫小明")]}) - 第二次调用:传入第一次的完整消息历史 + 新问题
- 验证 Agent 能记住第一轮的内容
- 第一次调用:
- 验收标准:第二轮回复中提到"小明"这个名字
练习 4:探索不同类型节点
- 目标:创建一个包含两种节点的图
- 要求:
- 节点1:添加时间戳到状态
- 节点2:基于时间戳生成问候语
- 使用自定义 State(非 MessagesAnnotation)
- 验收标准:输出包含"早上好/下午好/晚上好"等时间相关问候
📚 延伸阅读
下一章:第 2 章 —— 环境搭建与第一个完整 Agent
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)