LangGraph:用状态机思维重构 AI 问答系统的工作流编排

当你的 AI 系统需要根据结果动态决策、支持循环重试、协调多个 Agent 协作时, 线性 Chain 就到了它的边界。LangGraph 把 Agent 工作流建模为有向图, 让复杂的 AI 应用获得真正的状态感知能力。这是我们在 AI 智能问答平台落地的完整记录。

↓40%
无效 LLM 调用减少
3层
条件路由节点
10+
专业化子 Agent

从一个真实问题说起

我们在构建 AI 智能问答平台的早期,用的是最典型的 LangChain 线性 Chain 方案: 用户输入 → Prompt 拼装 → LLM 生成 → 输出结果。 这个方案跑通 Demo 很快,但上线后问题接踵而至。

有一类用户的咨询是这样的:"我上个月买的套餐还没发货,我想换货, 顺便问一下积分怎么用?"——这里面混杂了订单查询、换货申请、积分说明三类意图。 线性 Chain 只能选一个主意图处理,剩下的要么忽略要么乱答。

更麻烦的是评估环节。当 LLM 生成的答案质量不达标时,我们需要让系统 自动重试——调整 Prompt、换个策略、再试一次。 线性 Chain 做不到:它不记得自己跑到哪一步了,也不知道该回哪个节点重试。

核心矛盾
线性 Chain 假设每一步都会成功,但真实 AI 应用充满条件判断、分支跳转与重试循环。 你需要的不是一条流水线,而是一张可以感知状态、动态导航的地图

LangGraph 是什么

LangGraph 是 LangChain 团队于 2024 年推出的 Agent 编排框架, 其核心思想是:把 LLM 工作流建模为有向图(Directed Graph)

图中的每个节点(Node)是一个独立的处理单元——可以是一次 LLM 调用、 一个工具函数、一段业务逻辑,或一个子 Agent。 节点之间通过边(Edge)连接,边可以是无条件的顺序跳转, 也可以是根据当前状态动态决策的条件边(Conditional Edge)。 整个图共享一个全局状态对象(State),每个节点读取状态、执行任务、 写回状态,驱动整条工作流向前推进。

相比 LangChain 的 Chain,LangGraph 带来了三个关键能力的飞跃:

能力维度 LangChain Chain LangGraph
流程结构 线性顺序,固定步骤 有向图,支持分支 / 并行 / 循环
状态管理 无持久化状态,靠传参 全局 State 对象,节点间共享
动态路由 不支持,路径预先固定 条件边,根据运行时结果跳转
重试 / 循环 需要手写外层循环逻辑 图原生支持回边与循环节点
多 Agent 协作 需要大量胶水代码 子图嵌套,天然支持 Agent 编排
Human-in-the-loop 基本不支持 Checkpoint 机制,随时暂停等待人工干预

三个核心概念深度理解

1. State:工作流的"记忆载体"

LangGraph 的 State 是一个类型化的字典,贯穿整个图的执行生命周期。 每个节点都可以读取 State 中的任意字段,执行完后写入新字段或更新已有字段。 这意味着——后续节点永远能看到前面节点做了什么

在我们的问答系统中,State 包含这些关键字段: 用户原始输入、意图识别结果、已调用工具列表、历史消息、当前轮次答案草稿、 评估器评分,以及重试计数器。有了这个 State, 评估器节点可以看到"这是第几次重试",从而在达到上限时强制终止,而不是无限循环。

# 简化的 State 定义示例(Python)
from typing import TypedDict, List, Optional

class QAState(TypedDict):
    user_input: str          # 用户原始输入
    intent: Optional[str]   # 意图识别结果
    strategy: Optional[str] # 选择的回答策略
    tools_called: List[str] # 已调用的工具列表
    draft_answer: Optional[str]  # 答案草稿
    eval_score: Optional[int]    # 评估分数 (0-100)
    retry_count: int        # 当前重试次数
    final_answer: Optional[str]  # 最终输出答案

2. Node:工作流的"执行单元"

Node 是纯函数:接收当前 State,返回更新后的 State(或 State 的差量)。 这个设计让节点天然可测试——你可以为每个节点单独写单元测试, 不需要跑完整个流程。

节点可以做任何事:调用 LLM、查数据库、执行 Python 代码、 调用外部 API,或者启动一个嵌套子图(Sub-graph)。 在我们的实现里,每个专业化子 Agent(订单 Agent、积分 Agent、物流 Agent) 本身就是一个 LangGraph 子图,被作为节点嵌入主流程图中。

3. Edge:工作流的"决策路径"

普通 Edge 是静态的,表示"A 执行完后必定跳到 B"。 而Conditional Edge(条件边)才是 LangGraph 最强大的地方—— 它接受一个路由函数,该函数读取当前 State,返回下一个节点的名称。

评估器的重试逻辑正是依赖条件边实现的:评估分数 ≥ 75 跳到 END, 评估分数 < 60 跳回生成节点重试,中间值则触发人工审核流程。 整个逻辑完全在图的结构里,不需要在业务代码里写 if/else。

在 AI 问答系统中的完整落地

下面是我们 AI 智能问答平台的 LangGraph 工作流全貌。 整个流程从用户输入出发,经过意图识别、策略路由、多 Agent 执行、 质量评估,最终输出答案。

AI 问答系统 · LangGraph 工作流
START
意图识别节点
分析输入 / 拆分多意图
策略路由
条件边 →
↓ ↓ ↓
精确匹配
FAQ
检索节点
关键词匹配
RAG
检索节点
复杂意图
Agent
编排节点
↓ 合并
答案生成节点
LLM 生成 / 格式化
质量评估节点
评分 0–100 / 7类模板
条件边
↓ (<60 → 回答案生成节点重试 / ≥75 → END)
END · 输出最终答案

关键节点详解

意图识别节点是整个流程的入口。我们用一个轻量 LLM 调用 (而非完整的 GPT-4 级模型)对用户输入做分类和拆解: 单意图直接输出意图类型,多意图则拆分成有序的子任务列表,写入 State。 这里选用小模型有意为之——意图分类是分类任务,不需要生成能力, 用 GPT-3.5 级别就够,省下 token 成本和延迟。

策略路由节点是第一个条件边的触发点。路由函数读取意图类型, 按照我们设计的三层策略优先级:精确匹配(<100ms)→ 关键词 + RAG 检索 → 多 Agent LLM 生成,返回对应的下一节点名称。 这个优先级设计的背后逻辑是:能不用 LLM 就不用 LLM, 既节省成本,也降低延迟。

Agent 编排节点是最复杂的部分。当意图识别出需要调用外部系统时 (查订单、查积分、查物流),这里会展开一个子图: 编排器 Agent 根据子任务列表,并行或串行调用对应的专业子 Agent, 每个子 Agent 有自己的工具调用权限和知识域边界。 所有子 Agent 的结果汇聚回主图的 State,供答案生成节点使用。

质量评估节点是第二个条件边的触发点,也是整个系统与众不同的地方。 我们定义了 7 类评估模板(事实性、相关性、完整性、格式合规、安全合规等), 评估器节点用另一个 LLM 实例对生成答案打分(0–100)。 路由函数的逻辑是:≥75 输出采用,60–74 进入人工审核队列,<60 写入失败原因后 回跳到答案生成节点重试(State 里的 retry_count 确保最多重试 2 次)。

落地效果
引入 LangGraph 重构工作流后,无效 LLM 调用(因质量差被丢弃的生成) 减少了约 40%。更重要的是,工作流的每一步都有明确的 State 快照, 排查问题从"看日志猜执行路径"变成了"直接回放 State 时间线", 调试效率提升显著。

LangGraph vs 手写状态机:为什么选框架

有人会问:用 if/else 手写一个状态机不也能实现类似的效果吗? 能,但有代价。

手写状态机在节点数量超过 5 个后,代码复杂度会非线性增长—— 每加一条分支,所有相关路径都要手动处理。 LangGraph 把这个复杂度"压"进图结构: 节点是独立函数,边是显式声明的路由, 新增一个分支只需要加一个节点 + 修改路由函数,其他节点完全不受影响。

LangGraph 还提供了开箱即用的 Checkpoint 机制: 每个节点执行完后,State 会被持久化(可以存 Redis 或数据库)。 这意味着你可以随时暂停工作流、等待人工审核、然后从断点恢复—— 这在需要人工介入的企业 AI 场景里价值巨大。

一个判断标准
如果你的 AI 工作流需要满足以下任意一条,就应该认真考虑 LangGraph: 动态路由(不同输入走不同路径)、循环重试、多 Agent 协作、 Human-in-the-loop、或者超过 3 个顺序步骤且步骤间有状态依赖。

踩过的坑与经验沉淀

坑一:State 字段膨胀。 随着节点增加,State 里的字段越来越多,最终变成一个几十个键的大字典, 很难搞清楚哪个字段是哪个节点在用。我们后来引入了分组命名约定 (intent_*eval_*agent_*), 并在文档里明确每个字段的"写入方"和"读取方",才把这个问题控制住。

坑二:子图的 State 继承问题。 子 Agent 以子图形式嵌入主图时,子图有自己的 State 定义, 和主图的 State 需要显式做字段映射,否则数据在子图内部修改后无法同步到主图。 LangGraph 提供了 input/output 映射机制解决这个问题, 但刚上手时很容易忽略,导致诡异的"数据丢失"问题。

坑三:条件边的调试难度。 当系统有多个条件边时,排查"为什么跳到了这个节点"比较费时。 我们的解决方案是在每个路由函数里加结构化日志, 记录路由函数的输入 State 快照和输出节点名称, 再接入统一的链路追踪系统,让每次请求的完整执行路径一目了然。

经验:节点粒度要恰当。 LangGraph 鼓励你把逻辑切成很细的节点,但节点太细会导致图结构复杂难以维护。 我们的经验是:以"一个节点只做一件能被单独测试的事"为原则, 但把高度耦合的子步骤放在同一个节点里的一个私有函数里,不要为了"纯粹"而过度拆分。

写在最后

LangGraph 不是银弹。如果你的 AI 应用是简单的"输入 → 调 LLM → 输出", 用普通 Chain 甚至直接调 API 就够了,引入 LangGraph 是过度设计。

但当你的系统开始需要动态决策、多 Agent 协作、或者质量评估 + 重试时, LangGraph 提供的图结构 + 共享状态 + 条件路由三件套, 会让复杂的 Agent 工作流变得可理解、可调试、可维护。

我们在 AI 问答平台上的实践证明了这一点: 从意图识别到多 Agent 编排,从质量评估到自动重试, 整个工作流用 LangGraph 建模后,团队里不熟悉底层实现的成员 也能通过看图结构理解系统在做什么——这才是好的工程设计应该达到的效果。

好的 AI 系统架构,应该让工作流本身就是文档。 LangGraph 的图结构做到了这一点。

如果你对 LangGraph 的具体实现细节、或者我们的多 Agent 编排平台有兴趣, 欢迎通过邮件和我交流。项目相关的架构设计可以看 项目案例 页面。