[控场AI]
· 9 分钟阅读· 4,838 字

LangGraph 实战指南:构建有状态的多步骤 AI Agent

LangGraph 实战指南:构建有状态的多步骤 AI Agent

LangGraph 用图编排把循环、状态与人工介入收敛为统一抽象,让复杂 Agent 工作流可控可恢复。

本文基于一份实操指南,系统拆解了 LangGraph 在构建 AI Agent 时的核心设计。文章先对比 LCEL Chain 与 LangGraph,指出后者在循环、分支、状态持久化和人工介入上的原生优势;随后介绍 State、Node、Edge 三大抽象,并给出 ReAct Agent 的六步搭建流程。在记忆部分,文章区分了以 MemorySaver 配合 thread_id 实现的线程级短期记忆,和以 SqliteSaver 为代表的跨会话长期记忆。人工介入通过 interrupt 原语实现,多 Agent 协作则依赖 next_agent 字段加 router 条件边组成专家流水线。最后给出涵盖重试、限流、超时、Pydantic 校验与 LangSmith 追踪的生产化清单,并强调图结构只是让流程可控,真正的智力仍来自模型与工具设计。

构建能够推理、调用工具并在复杂工作流中保持上下文的 AI Agent,是当前工程落地的核心挑战。LangChain 生态中的 LangGraph 通过**图编排(graph-based orchestration)**给出了一个务实的答案——它让循环、分支和持久化状态成为一等公民,而不是需要自己拼装的补丁。本文基于一份实操指南,拆解 LangGraph 的核心概念、ReAct Agent 实现、记忆持久化、人工审批与多 Agent 协作等关键模式。

LangGraph 实战指南封面

为什么不用 Chain,而要用 Graph?

传统 LCEL Chain 适合线性流水线,但一旦遇到需要"回退重试、条件分支、暂停等待人工确认"的场景,就会变得笨拙。LangGraph 的设计取向是补齐这些短板:

能力LCEL ChainsLangGraph
循环/回路不支持支持
分支控制有限完全可控
状态持久化需手动内置
人工介入实现复杂原生支持
调试可视化较难图结构可视化
并行执行不支持支持

这张对比表的潜台词是:当你需要 Agent "反复思考—调用工具—再思考"时,图结构才是自然的表达方式。Chain 是流水线,Graph 是状态机。

LCEL(LangChain Expression Language) 是 LangChain 早期推出的声明式编排语法,用管道符 | 把 Prompt、LLM、输出解析器等组件串成 Runnable 序列,例如 prompt | llm | parser。它的执行模型是有向无环图(DAG)的线性简化版:数据单向流动,前一个组件的输出就是后一个组件的输入,因此对"循环、回退、动态分叉"缺乏原生表达力。LCEL 的优势在于简洁、可组合、支持流式与异步,适合 RAG 检索问答、固定步骤的数据处理等场景。LangGraph 并非取代 LCEL,而是复用了 LCEL 的 Runnable 接口作为节点内部实现,在其之上补上一层可循环、可持久化的状态机外壳。理解这层关系有助于判断:简单流水线继续用 LCEL 即可,只有出现环路或需要挂起恢复时,迁移到 LangGraph 才有意义。

三大核心概念:State、Node、Edge

LangGraph 的抽象非常克制,理解这三个概念基本就掌握了它的骨架。

State:贯穿全图的数据结构

State 是流经所有节点的共享数据结构,通常继承 MessagesState 并扩展自定义字段:

class AgentState(MessagesState):
    user_id: str
    session_data: dict
    tool_call_count: int = 0

把 user_id、工具调用计数这类业务字段放进 State,意味着节点之间无需通过函数参数传递上下文,状态天然可追溯、可检查点化。

Node:处理状态并返回更新

节点本质上是接收 State、返回增量更新的函数。它可以是 LLM 调用、工具执行、数据校验,甚至纯业务逻辑。

Edge:控制流向

边分为无条件边和条件边。条件边的返回值决定下一个节点,这是实现"工具调用→回到 Agent→再判断"这种循环的关键:

def should_continue(state: AgentState) -> str:
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "tools"
    return "end"

这三者组合,就是一个可运行、可视化的 Agent 骨架。

用 ReAct 模式搭一个带工具的 Agent

ReAct(推理+行动)是 Agent 最常见的形态。LangGraph 提供了 ToolNode 预置组件,省去了手写工具执行循环的麻烦。完整流程分六步:

  1. 定义工具:用 @tool 装饰器包装搜索、计算、天气等函数,工具的 docstring 会被 LLM 用来判断调用时机。
  2. 定义 State:最简单的场景直接继承 MessagesState。
  3. 绑定工具:llm.bind_tools(tools) 让模型知道有哪些工具可用。
  4. 定义节点:agent 节点调用 LLM,ToolNode 负责执行工具调用。
  5. 构建图:START → agent → (条件边) → tools → agent,形成闭环。
  6. 运行:用 app.stream() 观察每个节点的实时输出。

你可能没注意到,指南中的 calculator 工具用了一个字符白名单来实现"安全 eval",这在演示场景可行,但生产环境应替换为 AST 解析或专用计算库。这类示例代码与实际生产之间的差距,是每个落地团队必须自己补齐的。

ReAct 出自 2022 年论文《ReAct: Synergizing Reasoning and Acting in Language Models》,核心思想是让模型在"思考(Reason)"与"行动(Act)"之间交替:先输出一段推理,再决定调用哪个工具并给出参数,拿到工具返回的观察结果(Observation)后继续下一轮推理,直到认为可以给出最终答案。这种循环正是 LangGraph 图结构擅长表达的部分——agent 节点负责推理,工具节点负责行动,条件边判断是否继续循环。相比一次性生成答案,ReAct 让模型能借助外部信息纠错,但也带来步数膨胀、工具误调用等风险,因此实践中通常需要设置最大循环次数和工具调用白名单来兜底。

记忆与持久化:短期 vs 长期

Agent 如果没有记忆,每次对话都从零开始。LangGraph 通过 checkpointer 机制解决这个问题,并清晰区分了两个层次:

  • 短期记忆(线程级):使用 MemorySaver,配合 thread_id 配置。同一 thread 内的多轮对话共享上下文,例如第一轮说"我叫 Alice",第二轮问"我叫什么",Agent 能正确回答。
  • 长期记忆(跨会话):使用 SqliteSaver 或生产级存储,让状态在进程重启后依然保留。
config = {"configurable": {"thread_id": "user-123"}}
result = app.invoke({"messages": [HumanMessage(content="My name is Alice")]}, config=config)

这种"用 thread_id 隔离会话"的设计,本质上是把记忆管理从 Prompt 工程转移到了基础设施层,更可靠也更易扩展。

Checkpointer(检查点器) 是 LangGraph 持久化机制的核心:图在执行时,每经过一个节点都会把当前 State 序列化并保存为一个 checkpoint,同时记录版本和父子关系,形成一棵可回溯的状态树。这正是它支持时间旅行(回退到历史状态重跑)、断点恢复和 Human-in-the-Loop 暂停的底层原因——interrupt 本质上就是保存一个 checkpoint 然后抛出中断。thread_id 是 checkpoint 的分组键,相同 thread_id 的多次调用会接着上次的状态继续。MemorySaver 把 checkpoint 放在进程内存里,重启即丢失;SqliteSaver、PostgresSaver 等则落到数据库,支持多进程共享和跨会话长期记忆。选择哪种取决于你是否需要跨重启、跨实例的一致性保障。

人工介入与多 Agent 协作

Human-in-the-Loop

对于发邮件、下单等敏感操作,LangGraph 提供 interrupt 原语:节点执行到关键位置时暂停,等待人工做出批准或拒绝的决策,再通过 Command(resume=...) 恢复执行。这比在业务代码里塞"确认弹窗"要干净得多,因为暂停点、恢复数据、状态快照都由框架统一管理。

多 Agent 工作流

通过扩展 State 增加一个 next_agent 字段,并配合 router 条件边,就能实现"研究者→写作者→编辑者"这样的专家流水线:

class MultiAgentState(MessagesState):
    next_agent: str

每个专家节点只负责自己的职责,完成后更新 next_agent 指向下一个角色,router 负责实际跳转。这种模式把复杂任务分解为职责单一的协作体,比让单个 Agent 包揽一切更容易调试和评估。

生产化必备清单

示例代码能跑通不代表能上线。指南给出的生产检查清单值得逐条对照:

  • 错误处理:节点包裹 try/catch,关键节点配置 RetryPolicy(max_attempts=3, backoff_factor=2)
  • 限流:通过 RunnableConfig 管理 API 调用频率
  • 可观测性:接入 LangSmith 做链路追踪
  • 输入校验:用 Pydantic 严格校验工具输入输出
  • 超时控制:为每个节点设置执行超时
  • 状态 Schema:用 Pydantic 约束状态结构,避免脏数据扩散
  • 独立测试:节点应可脱离图单独做单元测试

围绕部署,指南还给出了 FastAPI 封装 /chat 接口和 Dockerfile 的示例,把 thread_id 作为请求参数传入,即可实现多用户会话隔离。

LangSmith 是 LangChain 官方推出的可观测性与评估平台,定位类似 LLM 应用版的 APM。它通过回调机制自动捕获每次 LLM 调用、工具执行、图节点跳转的输入输出、耗时、Token 消耗与错误堆栈,并以树状 Trace 形式展示整条调用链,便于定位是哪一步推理出错或哪次工具调用拖慢了响应。除追踪外,它还提供数据集管理、离线评估和在线反馈(用户点赞/点踩回流)等能力,可用于回归测试 Prompt 或模型版本变更。对于生产环境,Trace 数据本身也可能包含敏感信息,接入时通常需要配合脱敏策略或自托管部署。

一个务实的判断

LangGraph 的价值不在于它发明了新概念,而在于它把"循环、状态、人工介入"这些做 Agent 迟早要面对的工程问题,收敛成了一套统一的图抽象。如果你正在用 Chain 拼装复杂 Agent,并且已经感受到条件分支和状态管理的痛苦,那么迁移到 LangGraph 的收益会比较明显。但也要清醒:图结构本身不会让 Agent 变聪明,它只是让复杂流程变得可控、可观测、可恢复。真正的智力,仍然来自模型和工具的设计。

#LangGraph#AI Agent#LangChain#多步骤工作流#ReAct Agent#Human-in-the-Loop#多Agent系统
分享:

相关推荐