Skip to content

第 8 章:LangGraph 工作流

章节定位

到了这一章,AI 学习助手 已经具备链式处理、RAG、工具调用和 Agent 决策能力。下一步不是再增加一个“更聪明的模型”,而是把整个应用组织成可控的工作流。

LangGraph 解决的就是这个问题:当一个 LLM 应用不再是单链式直线流程,而是出现分支、循环、状态更新、重试和人工介入时,用图来管理比用一串链更清晰。

配套示例

  • 目录:examples/chapter-08
  • 入口:examples/chapter-08/main.py
  • 依赖:examples/chapter-08/requirements.txt
  • 运行:cd examples/chapter-08 && python3 main.py

示例层级与边界

  • 层级:教学版
  • 本章重点:理解工作流图里的状态、节点和条件分支,而不是马上依赖真实框架细节。
  • 不要误判:示例优先讲清图式思维,不覆盖持久化状态、人工介入工作台或线上恢复策略。

本章目标

  • 理解为什么复杂应用需要工作流编排
  • 学会 LangGraph 的基本组成
  • 能把 AI 学习助手 改造成带状态的流程
  • 知道条件分支、循环和中断恢复为什么重要
  • 建立“可调试、可恢复、可扩展”的工程思维

前置知识

  • 已理解链、RAG、Tool 和 Agent 的基本用法
  • 知道模型输出具有不确定性
  • 知道复杂流程需要状态管理

核心概念

1. 为什么从链走向图

链适合线性流程,例如:

输入 -> 检索 -> 总结 -> 输出

但真实业务常常更复杂,例如:

  • 如果是课程问题,走资料检索
  • 如果是学习路径问题,走目录查询
  • 如果答案不确定,先追问
  • 如果工具失败,走兜底方案

这类场景已经不是简单线性链可以优雅表达的了。图结构可以把“节点”和“转移规则”分开,让流程更容易理解和维护。

2. LangGraph 的基本组成

LangGraph 常见的四个元素是:

  • State:流程中的共享状态
  • Node:处理状态的节点
  • Edge:节点之间的连接
  • Conditional Edge:根据状态选择下一步

你可以把它理解成“带状态的流程图执行器”。

3. 状态为什么重要

如果没有状态,Agent 每一步都只能依赖当前输入。
如果有状态,系统可以保留:

  • 用户问题
  • 当前处理节点
  • 检索结果
  • 工具调用结果
  • 是否需要人工介入

这对于复杂学习助手非常重要,因为用户的问题往往不是一次性回答完就结束。

分模块讲解

1. 从 AI 学习助手 V3 到 V4

上一章的 AI 学习助手 V3 已经会调用工具,但流程还偏“黑盒”。这一章建议升级成 V4

  • 先分类问题类型
  • 再路由到不同节点
  • 需要检索时走 RAG
  • 需要工具时走 Tool 节点
  • 需要追问时进入澄清节点
  • 需要总结时进入收尾节点

这样系统就不只是“会回答”,而是“会组织回答过程”。

2. 节点设计原则

一个好的节点应该只做一件事:

  • 分类节点只判断任务类型
  • 检索节点只负责找资料
  • 工具节点只负责调用工具
  • 汇总节点只负责整合结果

不要让一个节点同时干分类、检索、总结和格式化。那样虽然看起来省事,但后期会很难维护。

3. 条件分支

条件分支的作用是:根据状态决定下一步去哪里。

例如:

  • 问题属于章节内容 -> 去资料检索
  • 问题属于目录导航 -> 去章节查询
  • 问题属于不明确请求 -> 去追问澄清

这类判断在复杂学习站里非常常见,也是 LangGraph 的核心价值之一。

4. 循环与重试

有时候一次执行不够,需要循环:

  • 检索结果不够好,重新检索
  • 输出格式不符合要求,重新生成
  • 工具调用失败,切换兜底策略

图结构对这种“失败后再走一轮”的场景更自然,而链式应用通常会开始变得笨重。

5. 人工介入

当用户提出模糊请求时,系统不应该硬猜。

例如用户问:

  • “我想快速学会这个项目”

系统可以先进入澄清节点,追问:

  • 你是想先看路线图,还是先看实战示例?

这比直接给一大段长回答更有学习价值。

最小示例说明

下面是一个简化的结构示意,帮助你理解 LangGraph 的组织方式:

python
state = {
    "question": "我想先学第 7 章,需要什么前置知识?",
    "route": None,
    "context": None,
}

graph = build_graph(
    nodes=[classify, retrieve, call_tool, summarize],
    edges=[...],
)

result = graph.invoke(state)

这个示例里最重要的不是语法,而是思路:

  • 输入先进入状态
  • 状态驱动节点执行
  • 节点执行后更新状态
  • 根据状态决定下一步

这比单纯的 prompt | model | parser 更适合复杂系统。

真实框架版对照

examples/chapter-08/main.py 现在除了教学版,还补了一版真实 StateGraph 对照。

它把同一个思路映射成更接近实际框架接口的写法:

  • TypedDict 明确定义状态结构
  • StateGraph 注册分类、目录查询、RAG、澄清这些节点
  • 用条件边把 route 显式映射到下一跳

这样你在学 LangGraph 时,就不会停留在“图的概念”,而能直接看到这些概念在真实代码里的落点。

本章实践

建议把本章实践做成 AI 学习助手 V4

实践目标

  • 用户输入问题后,系统先分类
  • 如果是课程目录问题,走目录节点
  • 如果是知识问答,走 RAG 节点
  • 如果是工具问题,走 Tool 节点
  • 如果判断不明确,先追问用户

实践步骤

  1. 定义统一状态结构
  2. 写一个分类节点
  3. 写一个检索节点
  4. 写一个工具节点
  5. 写一个总结节点
  6. 用条件边把这些节点串起来

实践判断标准

  • 任意一次执行都能看出路径
  • 中间状态可以打印和调试
  • 某一步失败后不至于整个流程崩掉
  • 流程修改时不需要重写整个系统

常见坑

  • 把所有逻辑堆进一个节点里,结果图结构失去意义
  • 状态字段设计过多,后面没人知道每个字段干什么
  • 条件分支太多,但没有统一命名规则
  • 过分依赖“看起来很智能”的自动流转,忽略可调试性
  • 没有给失败路径设计兜底,导致系统一出错就中断

练习题

  1. AI 学习助手 设计一个最小状态结构,至少包含问题、路由、检索结果和最终答案。
  2. 设计三个节点:分类、检索、总结,并说明每个节点只负责什么。
  3. 思考一下:什么时候应该使用条件分支,什么时候应该直接调用固定链?
  4. 如果一个节点失败,应该如何设计兜底路径,保证用户还能得到可理解的反馈?

本章总结

LangGraph 的核心价值不是“让模型更聪明”,而是“让复杂 AI 应用更可控”。

AI 学习助手 发展到需要分支、循环、重试和人工介入时,继续用普通链会越来越难维护。LangGraph 提供的是一种更适合复杂应用的组织方式:把流程显式化,把状态显式化,把转移规则显式化。

如果说前面的章节是在训练“怎么让模型做事”,这一章就是在训练“怎么让一个 AI 系统稳定地做完整件事”。

学完本章,你现在应该会

  • 解释为什么复杂 AI 系统需要状态、节点和显式转移
  • AI 学习助手 V4 设计一个最小状态结构
  • 判断哪些步骤应该拆成节点,哪些仍然适合留在单个节点内部
  • 给失败路径设计一个可理解的兜底输出

最小验收 checklist

  • [ ] 我能画出一条包含分类、检索或工具、总结的最小工作流
  • [ ] 我定义的状态字段不多,但足够支撑路由和调试
  • [ ] 我能解释至少一个条件分支为什么存在
  • [ ] 我已经考虑某个节点失败时,系统如何继续给用户反馈

建议你动手改一版

  • 先只做 3 个节点版本:分类、执行、总结,避免一开始把图做太大
  • 给状态增加一个 tracesteps 字段,用来记录走过哪些节点
  • 故意制造一个节点失败场景,看看你的兜底路径是否还能返回可理解说明

卡住时先回看这里

下一章预告

下一章会进入工程化收尾,讨论评估、观测和调试。因为一旦系统变成链、Agent 和图的组合,最难的就不是“能不能跑”,而是“怎么知道哪里出了问题、怎么判断效果是否真的变好”。