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-run current_stage: design lifecycle: RUNNING artifacts: {}这就是 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 流水线状态机的"心脏"
import yaml
from enum import Enum
from pathlib import Path
class Action(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"]
def next_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. 流程已经完成
if cur == STAGES[-1] and lifecycle == "COMPLETED":
return {"action": Action.COMPLETE, "reason": "all stages done"}
# 2. 当前阶段还没完成 → 继续干当前阶段
if lifecycle == "RUNNING":
return {"action": Action.RUN_STAGE,
"target_stage": cur,
"reason": "stage in progress"}
# 3. 当前阶段产物已出,等审批
if lifecycle == "AWAITING_APPROVAL":
gate_status = gates.get(cur, {}).get("status", "pending")
if gate_status == "approved":
next_idx = STAGES.index(cur) + 1
return {"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_requirements lifecycle: AWAITING_APPROVAL approval_gates: S1_requirements: { status: approved }- 跑:
from pathlib import Path from next_action import next_action print(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 真的听话(意图分类、硬边界、必读顺序、不变量声明……)
更多推荐



所有评论(0)