框架与编排:LangGraph / CrewAI / 多智能体 · 里程碑长文:同一只 Agent,换一种骨架
这个里程碑要做什么
把第二阶段那个手写的知识库问答 Agent 用 LangGraph 完整重构一遍。功能保持不变,但要加两个新能力:
- 审批节点(human-in-the-loop):当 Agent 判断检索置信度低、或问题触及你预设的敏感范围时,暂停执行,等人工确认后再从断点继续;
- 失败重试:检索或生成失败时,按你定义的策略重试(换关键词、降级模型、最终报错),重试必须有次数上限与兜底。
「功能不变」是刻意的——重构的价值在于对比:同一只 Agent,手写版对状态机版,各自好在哪、痛在哪。
为什么是它(设计意图)
「重构」而不是「新写」,是为了精确隔离框架带来的变量:同样的提示词、同样的检索逻辑,只换编排层。这样你才能回答那个关键问题——框架到底替我做了什么,又向我收了多少税。
LangGraph 把 Agent 建模为状态机:节点是步骤,边是转移条件。审批节点和重试策略在手写版里要靠一堆标志位硬撑,在状态机里是天然的一等公民——这就是「何时该用框架」的最佳体感。Human-in-the-loop 是从玩具到生产的分界线,亲手实现一次「中断 → 人工确认 → 断点恢复」,你就理解了所有生产级 Agent 审批流的本质。同时你会摸到框架的代价:概念税、版本漂移、调试栈变深。摸到代价,才算学会了。
配套资源怎么用
- 先读「宪法」:Anthropic《Building Effective Agents》(2024-12-19 发布)。重点读 workflow 与 agent 的区分,以及「最成功的实现用的是简单可组合模式,而非复杂框架」的主张——带着「我是不是在过度设计」的警觉开始重构。
- 施工图纸:LangGraph 官方文档,重点看 persistence / checkpointer(审批节点的暂停与恢复依赖它)与 human-in-the-loop 章节,以当前版本为准。
- 快速入门:DeepLearning.AI 的两门短课——AI Agents in LangGraph 与 Multi Agent Systems with crewAI。前者是本里程碑的直接参照;后者用来对照「角色分工」范式,理解 LangGraph 与 CrewAI 各自的适用边界。
- 卡壳时:看 Sam Witteveen 的 LangGraph 实战系列——他跟进框架版本快,官方文档和你本地版本对不上时往往能救命。
- 理论印证:Berkeley LLM Agents MOOC 的多智能体专题讲次(视频仍可在官网看),理解为什么每加一个 Agent 就多一个错误传播面——本阶段你只需单 Agent + 审批,但要知道拆分多 Agent 的决策依据。
- 学术视角:CMU 11-711(Graham Neubig)的 prompting 与 agents 讲次,作系统化补充。
常见翻车点
- 把重构做成重写:忍不住顺手「优化」了检索逻辑和提示词,结果无法对比框架本身的得失。控制住变量。
- State 设计成万能大 dict:什么都往里塞,两周后没人看得懂。State 的字段就是这个系统的 API,克制地设计。
- 假审批:interrupt 之后默认通过,人工确认形同虚设。审批必须有拒绝路径,且拒绝后有明确的后续处理。
- 无限重试:没有次数上限、没有退避,模型持续报错时烧 token 如流水。重试策略 = 上限 + 退避 + 最终兜底。
- checkpointer 没用对:内存版进程一停状态全丢,审批等到天荒地老。要持久化(如 SQLite checkpointer)才配得上「生产预演」。
- 版本漂移:照抄三个月前的教程代码,踩中已废弃接口——一切以官方文档当前版本为准。
- 急着上多智能体:单 Agent 还没打磨到极限就拆分成「检索员 + 写手 + 校对」,结果错误沿角色链传播、定位困难。多智能体不是人多力量大,先把单 Agent 的天花板摸到再谈拆分。
验收清单
- LangGraph 版功能与 s2 手写版对齐,能回答同样的问题集
- 状态图能完整画出来(节点、边、条件),并能向他人讲清每一跳的触发条件
- 审批节点真实生效:低置信度时暂停,人工确认后从断点继续,人工拒绝走拒绝路径
- 重试有次数上限与最终兜底,构造失败场景能观察到完整重试链
- 状态可持久化:进程重启后,暂停中的会话能恢复
- 写出一段 200 字以内的对比总结:框架替你做了什么、代价是什么、下次什么情况会再选它
延伸建议
用 CrewAI 的思路把同一问题重新建模一次(不写代码也行,画出角色分工图),体会「状态机」与「角色分工」两种范式的差异。把审批节点抽象成可复用模式——未来任何 Agent 上线前先问一句「哪个节点需要人」。有余力去翻翻 LangGraph interrupt 的实现,理解它本质是「持久化 + 断点续跑」,这个认知会迁移到所有工作流引擎。