📖 本章学习目标

  • ✅ 理解为什么传统LLM调用和简单Agent不够用
  • ✅ 掌握 LangGraph 的核心定位:低级编排框架
  • ✅ 了解 LangGraph 的五大核心能力
  • ✅ 认识 LangGraph 在 LangChain 生态中的位置
  • ✅ 完成第一个可运行的 LangGraph 程序
  • ✅ 建立正确的技术选型思维

一、背景说明

《深入浅出LangChain》中,我们已经详细介绍了:

  • ✅ AI Agent的核心概念(感知-推理-行动循环)
  • ✅ ReAct Agent的工作原理
  • ✅ 工具调用的基本模式
  • ✅ 简单的Agent实现示例

如果你还没有阅读那部分内容,强烈建议先阅读相关章节。

本章将在此基础上,聚焦于一个关键问题:当Agent逻辑变得复杂时,如何优雅地编排和管理? 这是一个从简单Agent到复杂工作流的挑战。

真实业务场景中,简单的ReAct模式往往不够用。想象你是一家电商公司的技术负责人,你想用 AI 构建一个客服系统。这个系统需要:

  • 接收用户咨询(“我的订单在哪?”)
  • 查询订单数据库
  • 如果发现异常,触发售后流程
  • 生成个性化回复
  • 在必要时转接人工客服
  • 记住用户的偏好和历史交互
  • 支持多轮对话和上下文理解
  • 在关键节点等待人工审核
  • 故障后能够从断点恢复

这已经不是简单的 “感知-推理-行动” 循环了,这是一个复杂的、有状态的、需要持久化和人工干预的工作流。面临如下挑战:

  • ❌ 单次 LLM 调用无法完成多步骤推理
  • ❌ 没有状态管理,每次调用都是「失忆」的
  • ❌ 无法处理复杂的条件分支和循环逻辑
  • ❌ 工具调用失败后难以重试和恢复
  • ❌ 无法在关键节点引入人工审核
  • ❌ 长时任务崩溃后无法恢复

❌ 简单Agent(ReAct)

用户提问

LLM推理

调用工具

完成?

输出结果

✅ 复杂工作流(LangGraph)

查询订单

售后申请

一般咨询

用户目标

意图识别

需要什么?

查询数据库

触发审批流程

直接回复

异常?

人工审核

生成回复

结束

LangGraph 的诞生就是为解决上述问题而生的。它是 LangChain 团队推出的低级别 Agent 编排框架,让你用 图(Graph) 的方式描述 Agent 的行为逻辑。你可以把 LangGraph 想象成 AI Agent 的 “工作流引擎” 。就像公司的审批流程图一样,每个节点是一个处理步骤,箭头(边)决定执行路径,整个系统有状态、可追踪、可恢复。


二、核心概念详解

1、什么是 AI Agent(快速回顾)

完整内容(Agent的实现原理、ReAct模式、工具调用等)请参考《深入浅出LangChain》相关章节,这里只做简要回顾:

AI Agent(智能体):一个能够感知环境、自主做决策、采取行动以完成目标的 AI 系统。

Agent核心循环

🧠 感知
接收输入/工具结果

💭 推理
LLM 分析与规划

⚡ 行动
调用工具/执行任务

✅ 目标达成?

📤 输出结果

  • 感知(Perception):接收用户输入,读取工具返回结果
  • 推理(Reasoning):LLM 分析当前状态,决定下一步行动
  • 行动(Action):调用工具、搜索信息、执行代码
  • 循环(Loop):重复以上过程,直到任务完成

2、LangGraph 的图模型

LangGraph 将 Agent 的执行逻辑建模为一张有向图(Directed Graph):

条件边

条件边

▶ START

节点 A
意图识别

节点 B
工具调用

节点 C
直接回复

⏹ END

图中有三个核心元素:

元素 英文 类比 作用 示例
节点 Node 工序/工作站 执行具体逻辑(LLM 调用、工具执行等) 意图识别、订单查询
Edge 流向箭头 决定执行路径(普通边/条件边) 如果A则走B,否则走C
状态 State 共享白板 在节点间传递和共享数据 对话历史、提取的实体

三种边的类型:

并行边(同时执行)

节点A

节点B

节点C

汇聚节点

条件边(动态路由)

条件1

条件2

节点A

节点B

节点C

普通边(固定路径)

节点A

节点B

3、LangGraph 的五大核心能力

LangGraph
核心能力

持久化执行

故障恢复

长时任务支持

检查点机制

人机交互

中断机制

人工审核

状态修改后继续

记忆系统

会话内

长期跨会话记忆

向量检索

流式输出

Token级流式

中间步骤可见

实时响应

生产部署

LangSmith监控

可扩展架构

API服务封装

(1) 持久化执行(Durable Execution)

Agent 运行中途崩溃?没关系,从最近的检查点恢复,无需从头重来。

应用场景:

  • 长时任务(如数据分析,可能运行数小时)
  • 网络不稳定环境
  • 需要人工审核的流程
(2) 人机交互(Human-in-the-loop)

在关键节点暂停,等待人工审核或修改后继续,适合高风险决策场景。

应用场景:

  • 金融交易审批
  • 医疗诊断建议
  • 法律文件生成
(3) 记忆系统(Comprehensive Memory)

区分会话内的短期记忆和跨会话的长期记忆,让 Agent 真正“记住”用户。

应用场景:

  • 个性化推荐
  • 用户偏好学习
  • 历史对话引用
(4)流式输出(Streaming)

不必等待完整结果,LLM 生成的每个 Token、每个中间步骤都可以实时推送给用户。

应用场景:

  • 聊天界面实时显示
  • 进度条展示
  • 降低用户等待焦虑
(5) 生产级部署

配合 LangSmith 实现全链路追踪和监控,支持水平扩展的生产环境部署。

应用场景:

  • 性能监控
  • 错误告警
  • A/B测试

4、LangGraph 在生态中的位置

LangChain生态系统

LangChain Core
基础抽象层
(模型、提示词、链)

LangGraph
智能体编排层
(图、状态、工作流)

LangSmith
观测与调试层
(追踪、评估、监控)

LangGraph Platform
部署与扩展层
(云部署、API服务)

⚠️ 重要提示: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 用英文回复
  • 要求:
    1. callLLM 节点中添加系统提示词(SystemMessage)
    2. 系统提示词要求"请用英文回复"
    3. 保持其他代码不变
  • 验收标准:运行后输出为英文

练习 2:添加打印节点

  • 目标:在 LLM 节点之前添加一个「打印输入」的节点
  • 要求:
    1. 创建新节点 printInput,只打印 state.messages
    2. 不修改状态(返回空对象 {})
    3. 调整边的连接:START → printInput → llm → END
  • 验收标准:运行时控制台先打印输入消息,再打印 LLM 回复

练习 3:多轮对话

  • 目标:让 Agent 支持多轮对话
  • 要求:
    1. 第一次调用:invoke({messages: [HumanMessage("我叫小明")]})
    2. 第二次调用:传入第一次的完整消息历史 + 新问题
    3. 验证 Agent 能记住第一轮的内容
  • 验收标准:第二轮回复中提到"小明"这个名字

练习 4:探索不同类型节点

  • 目标:创建一个包含两种节点的图
  • 要求:
    1. 节点1:添加时间戳到状态
    2. 节点2:基于时间戳生成问候语
    3. 使用自定义 State(非 MessagesAnnotation)
  • 验收标准:输出包含"早上好/下午好/晚上好"等时间相关问候

📚 延伸阅读


下一章:第 2 章 —— 环境搭建与第一个完整 Agent

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐