01 · 当 AI 学会“按规矩办事“——规范驱动 Agent 工作流总览
01 · 当 AI 学会"按规矩办事"——规范驱动 Agent 工作流总览
系列第 1 篇
学完本篇你能:
- 理解“规范驱动 Agent 工作流”是什么、为什么需要它;
- 用 5 个核心抽象(Run / Stage / Manifest / Gate / Profile)描述任意一个 Agent 流水线;
- 现在就能动手:花 10 分钟搭一个最小可跑的"分阶段 + 可审批"AI 任务执行器。
0. 翻车现场:你肯定见过
场景换一个所有人都熟的——“让 AI 帮我重构一个老 Python 模块”:
“把这个
legacy_order.py文件重构一下,拆成 service / repo 两层,加上类型注解,补单元测试。”
你扔给 Cursor / Claude Code / 任意 AI 助手,结果通常是:
- 直接开冲:上来就写代码,写到一半发现没看
db_session.py的依赖,类型对不上。 - 半截烂尾:80% 写完了,剩下 20% 反复修;你也说不出来是哪一步歪了。
- 越改越乱:你说"这里不对",它改 A 又改坏 B,上下文越来越长。
- 没法复盘:成功了你也复述不出来它到底做了哪些决策。
- 没法暂停:会话一断,下次从零开始,前面 50 轮全丢。
根因只有一个:你把 AI 当"超长 prompt 里的执行器",没把它当"流水线"来设计。
这一篇要回答的问题:怎么把"让 AI 改一个真实仓库"做成可控、可暂停、可回放的工程化流程?
1. 核心立场:流水线,不是聊天框
如果你熟悉 CI/CD(Jenkins、GitHub Actions),那就好办了——规范驱动 Agent 工作流 = 把 AI 编码做成 CI/CD。
❌ 聊天框模式 ✅ 流水线模式 ┌────────────────┐ ┌────────────────────────────┐ │ 一个超长 │ │ 阶段 1 → 阶段 2 → ... → N │ │ system prompt │ │ ↓ ↓ │ │ │ │ 产物 1 产物 2 │ │ AI 自由发挥 │ │ ↓ ↓ │ │ │ │ 审批门 审批门 │ │ 输出 = 一坨 │ │ │ └────────────────┘ │ 状态可查 / 失败可重跑 / │ │ 中断可恢复 / 全程可复盘 │ └────────────────────────────┘CI/CD 的执行体是 shell 脚本,规范驱动 Agent 工作流的执行体是 LLM。别的,全一样——分阶段、出产物、卡审批、可恢复。
动手 30 秒:打开你常用的 AI 编码工具,回想最近一次让它做大型重构的对话。数一下:那次任务跨了几个阶段?每个阶段的产物存在哪?如果中途断了你怎么恢复?答不上来的越多,越说明你需要这套东西。
2. 五个核心抽象:背下来够用一辈子
不管哪个具体的 Agent 框架(Aladdin / LangGraph / CrewAI / 自己写的),看到下面这五个抽象就能秒懂:
| 抽象 | 一句话 | 类比 |
|---|---|---|
| Run | 一次任务执行实例,有唯一 ID | GitHub Actions 的一次 workflow run |
| Stage | run 内部的一个阶段(需求/设计/实现/验证…) | CI 流水线里的一个 job |
| Manifest | 记录这个 run 走到哪了的状态文件 | 数据库里的task表那一行 |
| Gate | 阶段之间的审批闸门 | PR review / CI manual approval |
| Profile | 适配不同技术栈的可换插件 | Docker 镜像变体(python:3.11 vs node:20) |
┌─────────────────────────── 一个 Run ────────────────────────────┐ │ │ │ Stage 1 ─► [Gate] ─► Stage 2 ─► [Gate] ─► ... │ │ ↓ ↓ │ │ 产物 1 产物 2 │ │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Manifest(YAML 文件): │ │ │ │ run_id: 20260630-abc123 │ │ │ │ current_stage: Stage 2 │ │ │ │ lifecycle: AWAITING_APPROVAL │ │ │ │ artifacts: { stage1: ./design.md, stage2: ... } │ │ │ └───────────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ 通过 Profile 注入"这次用什么技术栈" │ │ │ │ └───────────────────────┼──────────────────────────────────────────┘ │ ┌───────┴────────┐ │ Profile (YAML) │ │ - 编译命令 │ │ - 阶段实现者 │ │ - 模板路径 │ └────────────────┘记住这张图。后面 4 篇都是在它的某一格里深挖。
动手 30 秒:在你电脑上随便建个目录,写个
manifest.yaml:run_id:my-first-runcurrent_stage:designlifecycle:RUNNINGartifacts:{}这就是 Run Manifest 的最小形态。一行 Python
yaml.safe_load(open("manifest.yaml"))就能读它。
3. 一个完整流程长什么样
把上面五个抽象串起来,就是一个完整 Agent 流水线。下面是一个通用版(不限技术栈):
┌──────────────────────────────────────────────┐ 用户需求 ──────► │ 规范驱动 Agent 工作流 │ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ S1 │ ─► │ S2 │ ─► │ S3 │ │ │ │ 需求 │ │ 设计 │ │ 任务 │ │ │ │ 整理 │ │ 文档 │ │ 拆解 │ │ │ └────┬───┘ └────┬───┘ └────┬───┘ │ │ │ │ │ │ │ [审批门] [审批门] [审批门] │ │ ▼ ▼ ▼ │ │ └─────►──────┴──────►──────┘ │ │ │ │ ┌────────┐ │ │ │ S4 │ ◄── 唯一允许改业务代码的阶段│ │ │ 实现 │ │ │ └────┬───┘ │ │ ▼ │ │ ┌─────────────── S5 验证回环 ─────────────┐ │ │ │ S5.1 静态审计 ─► S5.2 编译 ─► S5.3 跑│ │ │ └─────────────────────┬─────────────────┘ │ │ │ │ │ ┌─────────┴────────┐ │ │ ▼ ▼ │ │ PASS ─► S6 总结 失败 ─► 回 S4 ─┐│ │ ││ │ 不确定/卡住 ──► HITL(人在回路)──────┘│ └──────────────────────────────────────────────┘ ▲ │ ┌────────────────────┴──────────────────────┐ │ 全程读写:runs/<run_id>/manifest.yaml │ │ (唯一的"项目档案袋",状态机的真相源) │ └───────────────────────────────────────────┘HITL = Human-In-The-Loop(人在回路):AI 拿不准 / 信息不足 / 状态冲突时,主动停下等人,而不是硬猜着往下走。这是这套方法论的灵魂之一。
三个最关键的不变量,记住就行:
- S4 是唯一允许写业务代码的阶段——之前只能产出"规范"(需求文档、设计文档、任务清单)。
- S5 是验证回环——
静态审计 → 编译 → 跑通,必须串行,不能合并。 - 任何阶段之间都可能卡审批,AI 自己不能开闸。
动手 30 秒:拿你最近做过的一个 AI 任务,套到这张图上。哪几步是你心里"默默做了"的?把它们显式地命名出来——这就是开始"工程化"的第一步。
4. 关键设计:把"下一步做什么"从 LLM 手里拿走
这是整套方法论里最值得偷的一招。
反例:把流程描述写在 system prompt 里,让 LLM 自己判断"我现在该走哪一步"。
❌ 反例(vibe coding 常见做法) ┌──────────────────────────────────────┐ │ 超长 system prompt: │ │ "你是 AI 工程师,先写需求, │ │ 再写设计,再写代码…… │ │ 如果失败就回到实现阶段……" │ │ │ │ LLM 自己判断状态 │ │ LLM 自己决定跳转 │ │ │ │ ⚠ 状态飘忽 / 逻辑漂移 / 上下文爆炸│ └──────────────────────────────────────┘正例:把"下一步走哪"做成一个确定性函数,LLM 只能调用、不能改写。
✅ 正例(规范驱动) ┌──────────────────────────────────────┐ │ prompt 只说:"你现在做 S2 设计" │ │ │ │ 下一步走哪?───┐ │ │ ▼ │ │ ┌────────────────────────────────┐ │ │ │ next_action() ← 纯函数 │ │ │ │ 读 manifest.yaml │ │ │ │ 按规则判断 │ │ │ │ 返回枚举值 │ │ │ └────────────────┬───────────────┘ │ │ ▼ │ │ RUN_STAGE / WAIT_APPROVAL / │ │ BLOCKED / COMPLETE │ │ │ │ │ LLM 严格照办 ◄───┘ │ └──────────────────────────────────────┘关键点:返回值是枚举值,不是自然语言。LLM 只能按枚举值行动,不能"理解"出第六种可能。
4.1 看一段简化版的伪代码
下面是一个最小可运行的next_action(),60 行 Python,不依赖任何框架:
# next_action.py - 一个 Agent 流水线状态机的"心脏"importyamlfromenumimportEnumfrompathlibimportPathclassAction(str,Enum):RUN_STAGE="RUN_STAGE"# 可以进入下一阶段WAIT_APPROVAL="WAIT_APPROVAL"# 等待人工审批BLOCKED="BLOCKED"# 状态异常,停下COMPLETE="COMPLETE"# 全部完成# 阶段顺序(写死在状态机里,不让 LLM 自己想)STAGES=["S1_requirements","S2_design","S3_tasks","S4_implement","S5_verify","S6_summary"]defnext_action(run_dir:Path)->dict:manifest=yaml.safe_load((run_dir/"manifest.yaml").read_text())cur=manifest["current_stage"]lifecycle=manifest["lifecycle"]gates=manifest.get("approval_gates",{})# 1. 流程已经完成ifcur==STAGES[-1]andlifecycle=="COMPLETED":return{"action":Action.COMPLETE,"reason":"all stages done"}# 2. 当前阶段还没完成 → 继续干当前阶段iflifecycle=="RUNNING":return{"action":Action.RUN_STAGE,"target_stage":cur,"reason":"stage in progress"}# 3. 当前阶段产物已出,等审批iflifecycle=="AWAITING_APPROVAL":gate_status=gates.get(cur,{}).get("status","pending")ifgate_status=="approved":next_idx=STAGES.index(cur)+1return{"action":Action.RUN_STAGE,"target_stage":STAGES[next_idx],"reason":f"{cur}approved, advance"}else:return{"action":Action.WAIT_APPROVAL,"target_stage":cur,"reason":"gate not approved"}# 4. 状态异常return{"action":Action.BLOCKED,"reason":f"unknown lifecycle:{lifecycle}"}这就是规范驱动 Agent 工作流最核心的那 60 行代码。可以扩展(加更多阶段、加 profile、加重试),但骨架就是这样。
动手 10 分钟(即时满足版):
- 把上面 60 行存成
next_action.py;- 创建
runs/test01/manifest.yaml,内容:current_stage:S1_requirementslifecycle:AWAITING_APPROVALapproval_gates:S1_requirements:{status:approved}- 跑:
frompathlibimportPathfromnext_actionimportnext_actionprint(next_action(Path("runs/test01")))- 你应该看到:
{'action': 'RUN_STAGE', 'target_stage': 'S2_design', 'reason': 'S1_requirements approved, advance'}- 把
status: approved改成pending,再跑一次。状态变成WAIT_APPROVAL。恭喜——你刚才手写了一个 Mini-Aladdin的核心。
5. Core 与 Profile 的边界:“骨头” vs “肌肉”
最后一个概念,第 4 篇会展开。
┌─────────────────────────────────────────┐ │ Core │ ← 骨头 │ (流程不变量 / 状态机 / Approval Gate)│ 所有任务通用 └─────────────────┬───────────────────────┘ │ profile_path 注入 ▼ ┌─────────────────────────────────────────┐ │ Profile │ ← 肌肉 │ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │ │ Python │ │ Java │ │ Go │ │ 不同技术栈 │ │ profile │ │ profile │ │profile │ │ 完全不同 │ └──────────┘ └──────────┘ └────────┘ │ │ - 编译命令:pytest / mvn / go test │ │ - 模板路径:自家代码模板 │ │ - 阶段实现者:调哪个 sub-agent │ └─────────────────────────────────────────┘这就是设计模式里的**开闭原则(OCP)**在 Agent 系统里的体现:核心流程对修改关闭、对扩展开放。新接一个技术栈 = 写一份 profile。Core 一行不动。
动手 30 秒:你现在做的项目,如果要让 AI 跑同一套流程,需要哪些"技术栈相关"的命令?比如
pytest -v是 Python 的,npm run test是前端的——把这些写进 profile,core 永远只调profile.test_command。
6. 核心要点回顾
如果只能记三件事:
- 复杂任务要切阶段:不要一个 prompt 干所有。切成 S1 → S2 → … → S6,每段独立可审批可重跑。
- 状态要有真相源:用文件(YAML/JSON)当唯一真相源,不要让 LLM 自己记。
- 决策权要拿出来:
next_action()这种结构化裁决函数,比让 LLM 自己想"下一步做什么"靠谱十倍。
7. FAQ:你大概率会问的 7 个问题
Q1:这套东西和直接用 LangGraph / CrewAI 有啥区别?
LangGraph / CrewAI 给你的是框架(提供执行引擎、节点定义、消息协议)。这一篇讲的是方法论——它们是正交的。你完全可以用 LangGraph 实现规范驱动 Agent 工作流,关键是要有 Run / Stage / Manifest / Gate / Profile 这五个抽象,而不是只把节点连起来就完事。
Q2:所有 AI 任务都要用这套吗?写个一次性脚本也用?
不用。判断标准:任务是否要跨多轮、产物是否要复用、失败是否要回滚。一次性问答、简单生成不需要。但凡你想第二天还能复盘的任务,都值得上。
Q3:状态机限制了 LLM 的"灵活性",会不会让结果变差?
恰恰相反。LLM 的"灵活"在语义生成上很值钱,在流程控制上是灾难。你让它写代码很灵活,让它判断"现在该编译还是该先 review"它就开始胡说。把它擅长的留给它,把它不擅长的拿走——结果会更好不会更差。
Q4:Approval Gate 每步都卡审批,那不还是人工干活?
可以配auto模式,让审批自动通过;也可以只在关键阶段卡(比如 S4 实现完之后必卡,前面阶段全 auto)。重点不是"必须人工",是"必须留一个能让人介入的口子"。生产环境出 bug 的时候你会感谢这个口子。
Q5:Manifest 文件被 LLM 误改了怎么办?
这就是为什么"所有写入必须通过run_manifest.py"——脚本里做参数校验,schema 不对就拒绝写。把"能改"的入口收窄到一个,LLM 只能调命令、不能直接编辑文件。这一招在工程上叫DAO 模式(Data Access Object),AI 系统里同样有效。
Q6:我自己怎么从 0 搭一个?
三步走:
- 第一周:把第 4 节的 60 行代码扩到 200 行,加上
init/stage-start/stage-complete/gate-event命令; - 第二周:接一个 LLM(Claude / GPT),让它每次行动前先调
next_action(),根据返回值决定 prompt; - 第三周:加 profile 系统、加 sub-agent,开始拆"S5 验证回环"。
后面 4 篇会带你过一遍。
Q7:这套东西有什么明显的缺点 / 不适合的场景?
三个:
- 冷启动成本高:写 profile、定义阶段、调 prompt,前期投入比 vibe coding 大几倍。任务简单或一次性的别上。
- 调试链路长:流程出错时你要查 manifest、查 gate、查 stage artifacts 三个地方,比看一坨对话窗口累。
- 不擅长开放式探索:阶段是预定义的。如果任务本身需要"先到处看看再决定怎么做",硬切阶段反而拧巴。
8. 下一篇预告
02 · 状态机与 Run Manifest——把"下一步做什么"从 prompt 手里拿走
我们会把第 4 节那个 60 行的
next_action()扩展到150 行的完整最小版,包括:
init/stage-start/stage-complete/gate-event/sync-result五个命令的具体实现;- 状态字段
current_stage / lifecycle / waiting_for / resume_from的语义和读写时机;- 如何让 LLM "读懂"状态——一个让 prompt 看得懂 manifest 的小技巧。
读完你能直接在自己项目里跑起来一个最小版状态机。
系列写作承诺(Prompt 工程线)
除了架构拆解,每一篇额外开一节"Prompt 拆解"——挑当篇主题对应的源码 prompt 段落,逐句注释为什么这么写。读完整个系列你会同时学到两件事:
- 架构线:怎么设计一个 Agent 流水线(Run / Stage / Manifest / Gate / Profile)
- Prompt 线:怎么把工程规则写进 prompt,让 LLM 真的听话(意图分类、硬边界、必读顺序、不变量声明……)
