OpenSpec + Codex 多 Agent 工程化:从规范定义到自动验收的闭环实践
“当 AI 能写代码,管理 AI 的工程方法就成了核心竞争力。规范驱动不是束缚,而是让十个 Agent 朝同一个方向开枪的指挥系统。”
过去一年,我见过的 AI 编码团队大概分成三类:
第一类,vibe coding 派。 打开 Cursor,把需求往 chat 里一扔,AI 就开始写。写完了跑一下,红了就丢回去让 AI 修,绿了合并。三个月后代码库变成了一座谁也不认识的迷宫。
第二类,AI 辅助派。 把 Copilot 当高级自动补全,AI 写代码,人做 review。比第一类好,但 review 越来越敷衍——谁有耐心逐行审查 AI 吐出来的 500 行代码?
第三类,规范驱动派。 先写 spec,再让 AI 按 spec 开发,最后用 spec 自动验收。开发过程中 spec 是唯一的真值来源,代码只是 spec 的实现物。
这篇文章,我想聊聊第三类。不是理论,是我们团队跑了大半年的实战经验。
问题:为什么单 Agent 编码不可持续?
先泼一盆冷水:让一个大模型同时扮演 PM、架构师、开发、QA,等于让一个人同时当厨师、服务员、收银员和清洁工。 它能做,但每一角色都在为其他角色擦屁股。
单 Agent 模式的死穴:
- 上下文膨胀:一个 Agent 既要理解业务需求,又要处理实现细节,还要考虑测试覆盖。上下文窗口再大,也装不下三层抽象。
- 标准漂移:第一轮对话里的设计原则,到第十轮早就被遗忘。AI 不会主动说”等等,这违反了我们在第三步约定的一致性原则”。
- 验收真空:代码写完了,谁来验收?再让同一个 Agent 验收自己的产出?那是自检,不是验收。
- 知识黑洞:对话历史散落在几十条消息里,项目交接时新成员面对的是一片废墟。
多 Agent 不是炫技,是责任分离。PM Agent 定方向,Dev Agent 实现,QA Agent 验收——每个 Agent 有明确的输入、输出和成功标准。
核心架构:OpenSpec 驱动的四阶段闭环
整个工作流分为四个阶段:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 规范定义 │ ──▶ │ 多 Agent │ ──▶ │ 自动验收 │ ──▶ │ Spec 归档 │
│ (Spec) │ │ 协作开发 │ │ (Verify) │ │ (Archive) │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
▲ │
│ │
└──────────────────── 可追溯、可复用 ──────────────────────────┘
关键原则:Spec 是贯穿始终的唯一真值(Single Source of Truth)。它定义了”做什么”(功能)和”怎么算对”(验收标准)。代码只是 spec 的物化实现。
Phase 1:规范定义 — OpenSpec 怎么写
我们内部用的不是一份自由文本的需求文档,而是一个结构化的 OpenSpec 文件。它本质上是一份机器可读、人类可审的契约。
OpenSpec 文件结构
# openspec.yaml
spec_version: "1.0"
feature_id: "user-auth-v2"
title: "用户认证系统重构"
owner: "platform-team"
priority: P0
# ── 业务上下文 ──
context:
background: "现有认证系统存在 session 不一致问题..."
stakeholders: ["移动端", "Web端", "管理后台"]
constraints:
- "必须兼容现有 JWT token 格式"
- "不能引入新的外部依赖"
- "P95 响应时间 < 100ms"
# ── 功能规格 ──
requirements:
- id: R1
title: "统一登录入口"
description: |
所有客户端通过 /api/v2/auth/login 进行认证,
支持 email + password 和 OAuth 2.0 两种模式。
acceptance_criteria:
- "email/password 登录返回 access_token 和 refresh_token"
- "OAuth 登录支持 Google 和 GitHub"
- "错误的密码返回 401,格式错误的请求返回 400"
- "access_token 有效期 15 分钟,refresh_token 有效期 7 天"
test_cases:
- id: T1
scenario: "有效凭证登录"
given: "用户存在且密码正确"
when: "POST /api/v2/auth/login"
then: "返回 200,包含 access_token"
- id: T2
scenario: "错误密码登录"
given: "用户存在但密码错误"
when: "POST /api/v2/auth/login"
then: "返回 401,错误信息不包含密码细节"
- id: R2
title: "Token 刷新机制"
description: "通过 refresh_token 获取新的 access_token"
acceptance_criteria:
- "有效的 refresh_token 返回新的 token pair"
- "过期的 refresh_token 返回 403"
- "刷新后旧的 refresh_token 立即失效(token rotation)"
test_cases:
- id: T3
scenario: "正常刷新"
given: "refresh_token 未过期且未被使用"
when: "POST /api/v2/auth/refresh"
then: "返回 200 和新 token pair"
# ── 架构约束 ──
architecture:
patterns:
- "Repository Pattern"
- "Dependency Injection"
forbidden:
- "不允许在 handler 里直接操作数据库"
- "不允许在业务代码里使用全局变量"
dependencies:
- name: "bcrypt"
version: "^5.1"
reason: "密码哈希"
- name: "jsonwebtoken"
version: "^9.0"
reason: "JWT 实现"
# ── 接口契约 ──
api_contract:
- path: "/api/v2/auth/login"
method: POST
request:
content_type: "application/json"
schema:
email: { type: string, format: email, required: true }
password: { type: string, min_length: 8, required: true }
response:
200:
content_type: "application/json"
schema:
access_token: { type: string }
refresh_token: { type: string }
expires_in: { type: integer }
400:
schema:
error: { type: string }
code: { type: string }
401:
schema:
error: { type: string }
# ── 验收定义 ──
verification:
auto_tests:
coverage_target: "90%"
required_paths: ["/api/v2/auth/*"]
manual_checklist:
- "登录后 15 分钟 token 自动过期"
- "并发刷新时不会出现 race condition"
performance:
- metric: "p95_latency"
target: "< 100ms"
test: "k6 load test with 1000 RPS"
为什么用 YAML 而不是自然语言?
三个原因:
- 结构化让 AI 理解更精准:自然语言有歧义,YAML 的键值对是强约束。你给 Dev Agent 喂 YAML,它不会”误解”你的验收标准。
- 可验证:测试框架可以直接解析 acceptance_criteria 和 test_cases,自动生成测试代码。
- 版本控制友好:diff 清晰,code review 时一眼看出改了哪些验收标准。
谁来写 Spec?
PM Agent + 人类 PM 共同完成。
流程是:
1. 人类 PM 写初稿(业务背景、核心需求)
2. PM Agent 补全技术细节(接口 schema、边界条件、异常场景)
3. 人类 PM 审阅、修正
4. 技术负责人签字(确认架构约束和依赖选择)
平均一个中等复杂度的 feature,spec 写 30-40 分钟。但省去了后续 3 小时的”AI 你到底要我做什么”的拉锯战。
Phase 2:多 Agent 协作开发
Spec 确定后,进入开发阶段。这里的关键是每个 Agent 只读自己的输入、只写自己的输出,通过 Spec 文件和代码仓库进行状态同步。
Agent 角色定义
| Agent | 职责 | 输入 | 输出 | 成功标准 |
|---|---|---|---|---|
| PM Agent | 需求澄清、规格细化、验收标准补充 | 用户故事、业务背景 | OpenSpec YAML | spec 被技术负责人 approve |
| Dev Agent | 代码实现 | OpenSpec + 现有代码库 | 代码 PR + 实现笔记 | 所有 acceptance criteria 被实现 |
| QA Agent | 测试生成、验收执行 | OpenSpec + 代码 PR | 测试报告 + 验收结果 | 所有 test cases 通过 |
| Guard Agent | 规范检查、架构守护 | 代码 PR + OpenSpec | 审查报告 | 无 forbidden pattern 违反 |
协作流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 人类PM │───▶│ PM Agent │───▶│ Dev Agent│───▶│ QA Agent │
│ 写初稿 │ │ 补全规格 │ │ 实现代码 │ │ 生成+执行 │
└──────────┘ └──────────┘ └──────────┘ │ 测试 │
▲ └────┬─────┘
│ │
└────────────── 循环直到验收通过 ────────────────┘
Dev Agent 的工作模式
Dev Agent 不是”把 spec 翻译成代码”那么简单。它需要:
- 阅读现有代码库:通过 RAG(检索增强生成)索引现有代码,确保新代码和旧代码风格一致。
- 渐进式实现:不是一次性生成 1000 行代码,而是按 spec 的 requirement 逐个实现,每个 requirement 完成后提交一次 commit。
- 自我检查:每完成一个 requirement,Dev Agent 运行一次 lint + type check + 单元测试,红了就修,红了就修,直到绿了才继续下一个。
- 写实现笔记:在 PR description 里解释每个关键决策——为什么选择这个设计、哪里和 spec 有出入(如果有的话)、需要 review 时特别注意什么。
Dev Agent 的 prompt 核心逻辑:
你是一位资深后端工程师,擅长 Go 和微服务架构。
你的任务是根据以下 OpenSpec 实现代码。
规则:
1. 每次只实现一个 requirement(R1 → R2 → R3...)
2. 实现完成后必须运行 `make test` 和 `make lint`
3. 如果测试失败,修复后再继续
4. 禁止修改 spec 中定义的接口契约
5. 如果必须偏离 spec,在实现笔记中说明原因
输入:
- OpenSpec: [openspec.yaml 内容]
- 现有代码: [通过 RAG 检索的相关代码片段]
- Requirement 当前目标: R2
QA Agent 的工作模式
QA Agent 不是简单跑测试。它的核心能力是从 spec 生成测试。
流程:
1. 解析 OpenSpec 中的 test_cases
2. 生成对应的自动化测试代码(单元测试、集成测试、e2e 测试)
3. 运行测试,生成报告
4. 如果测试失败,分析是代码 bug 还是测试本身的问题
5. 将结果写回 PR 评论
QA Agent 还会做边界扫描:基于 spec 中的 acceptance_criteria,自动补充一些边界测试——比如输入为空字符串、超长字符串、特殊字符、并发请求等——这些往往是人类写 spec 时遗漏的。
Guard Agent:架构守护者
这是最容易被忽略但最关键的 Agent。
Guard Agent 的职责:
– 检查代码是否违反了 spec 中的 forbidden 规则
– 检查新引入的依赖是否在 dependencies 白名单内
– 检查代码复杂度(圈复杂度、函数长度)
– 检查测试覆盖率是否达到 verification.auto_tests.coverage_target
Guard Agent 是一票否决权。它没过,PR 不能合并。
Phase 3:自动验收 — Spec 即测试
这是整个闭环的灵魂所在:验收标准不是写在文档里束之高阁的,而是被自动执行的。
三层验收金字塔
┌─────────────┐
│ E2E 测试 │ ← 用户视角,验证完整流程
│ (Playwright)│
├─────────────┤
│ 集成测试 │ ← API 视角,验证接口契约
│ (API Test) │
├─────────────┤
│ 单元测试 │ ← 开发者视角,验证逻辑分支
│ (Unit Test) │
└─────────────┘
从 Spec 自动生成测试
我们用了一个内部工具(可以叫 SpecTest)来解析 OpenSpec:
# spec_to_test.py 核心逻辑
def generate_tests(spec: OpenSpec) -> TestSuite:
suite = TestSuite()
for req in spec.requirements:
for tc in req.test_cases:
if tc.is_api_test():
suite.add(generate_api_test(tc, spec.api_contract))
elif tc.is_unit_test():
suite.add(generate_unit_test(tc, req.implementation_hint))
# 边界扫描:基于 acceptance_criteria 自动补充
for ac in spec.all_acceptance_criteria():
suite.add(generate_boundary_tests(ac))
return suite
接口契约的强制验证
OpenSpec 中定义的 api_contract 不是装饰。我们把它和 OpenAPI/Swagger 打通,做两件事:
- 静态验证:用 OpenAPI schema 校验代码中的 handler 定义是否和 spec 一致。如果 spec 说
/api/v2/auth/login返回的 JSON 必须包含access_token,但代码返回的是token——编译时就报错。 - 动态验证:集成测试运行时,所有 API 响应都经过 schema 校验。响应结构对不上?测试自动失败。
性能验收
spec 中的 verification.performance 不是建议,是硬指标。CI 流水线里跑 k6 压测,如果 P95 延迟超过 100ms,PR 被 block。
// k6 性能测试脚本(由 QA Agent 从 spec 生成)
import http from 'k6/http';
import { check } from 'k6';
export const options = {
thresholds: {
http_req_duration: ['p(95)<100'], // 来自 spec
},
stages: [
{ duration: '1m', target: 1000 },
],
};
export default function () {
const res = http.post('http://localhost:8080/api/v2/auth/login', {
email: 'test@example.com',
password: 'correct-password',
});
check(res, {
'status is 200': (r) => r.status === 200,
'has access_token': (r) => JSON.parse(r.body).access_token !== undefined,
});
}
Phase 4:Spec 归档 — 从一次性文档到组织资产
很多团队的 spec 写完就扔。我们的做法是:spec 是活的,而且越用越值钱。
Spec 的版本化管理
每个 OpenSpec 文件和它对应的代码一起存在 Git 里:
features/
user-auth-v2/
openspec.yaml # 规范
openspec.lock # 规范签名(防篡改)
implementation/ # 代码
tests/ # 测试
reports/
2025-01-15-e2e.html
2025-01-15-perf.json
当 feature 验收通过、PR 合并后,整个目录被打上 archive/v1.0.0 标签。后续任何改动都必须更新 spec 并走同样的四阶段流程。
Spec 知识库
所有归档的 spec 被索引到一个向量数据库中。当新的需求来临时,PM Agent 先检索历史 spec:
“用户想做一个文件上传功能”
PM Agent 检索:找到之前
file-upload-v1的 spec,里面有文件大小限制、MIME 类型白名单、病毒扫描接入点…PM Agent:”建议复用
file-upload-v1的规范,只需修改存储后端为 S3″
复用不是复制粘贴,是基于历史的智能推荐。 新 spec 的初稿 80% 由历史 spec 组成,PM 只需要调整差异部分。
Spec 健康度仪表盘
我们有一个内部看板,追踪每个 spec 的:
- 验收通过率:spec 的 test cases 在 CI 中持续通过的比例
- 偏差率:代码实现偏离 spec 的次数(由 Guard Agent 记录)
- 复用次数:该 spec 被其他 feature 引用的次数
- 技术债务指数:基于代码复杂度和 spec 修改频率计算
这些数据反过来指导 PM Agent 写更好的 spec——哪些验收标准经常导致测试失败?哪些架构约束经常被违反?spec 本身也在进化。
实战:一个 Feature 的完整生命周期
走一遍真实流程。
场景:给现有订单系统加”批量退款”功能
Day 1, 上午:人类 PM 写初稿
# 初稿
feature_id: "batch-refund-v1"
title: "订单批量退款"
context:
background: "客服需要一次处理多个订单的退款..."
requirements:
- id: R1
title: "批量退款接口"
description: "接收订单 ID 列表,执行批量退款"
Day 1, 下午:PM Agent 补全
PM Agent 检索到历史 single-refund-v1 的 spec,自动补全:
– 接口契约(请求/响应 schema)
– 幂等性要求(同一批退款不能重复执行)
– 部分退款失败的回滚策略
– 并发控制(防止同一订单被同时退款)
– 审计日志要求
人类 PM 审阅,删掉”并发控制”——因为订单系统已经有分布式锁。加了一条”最大批量 50 条”。
Day 2:Dev Agent 开发
Dev Agent 按 R1 → R2 → R3 逐个实现:
1. R1 接口骨架:写 handler + router
2. R2 退款逻辑:调用支付网关 + 更新订单状态
3. R3 幂等性:基于 refund_batch_id 实现幂等
每完成一个 requirement,运行测试。R2 第一次跑的时候红了——支付网关返回的 error format 变了。Dev Agent 自动调整解析逻辑,重跑,绿了,继续。
PR 提交,附实现笔记:
“支付网关的错误格式从
{'error': 'msg'}变成了{'errors': [{'message': 'msg'}]}。
已兼容新旧两种格式,建议下季度 spec 里要求统一错误格式。”
Day 3:QA Agent 验收
QA Agent 解析 spec 生成测试:
– 正常批量退款(3 个订单)✅
– 部分失败(第 2 个订单支付网关拒绝)✅
– 幂等性测试(同一 batch_id 执行两次,第二次返回上次结果)✅
– 边界测试(51 个订单 → 返回 400)✅
– 并发测试(两个请求同时退同一订单)✅
– 性能测试(50 个订单 P95 < 500ms)✅
全部通过。Guard Agent 扫描:没有 forbidden pattern,测试覆盖率 94%(> 90% 目标)。
Day 3 下午:合并 + 归档
PR 合并,features/batch-refund-v1 目录被打上 archive/v1.0.0。spec 进入知识库,下次有类似需求时会优先被推荐。
工具链建议
我们内部的组合,你可以参考:
| 环节 | 工具 | 替代方案 |
|---|---|---|
| Spec 编写/解析 | YAML + 自定义 schema | Markdown + frontmatter, TOML |
| Spec 版本管理 | Git + 目录约定 | Notion API, Confluence |
| Agent 编排 | 自建编排器(基于 LangChain) | CrewAI, AutoGen, Dify |
| 代码生成 | Claude 3.5 Sonnet / GPT-4 | Cursor Composer, GitHub Copilot Workspace |
| 接口契约验证 | OpenAPI + Swagger-codegen | Protocol Buffers, JSON Schema |
| 测试生成 | 自建 SpecTest + Pytest | Playwright codegen, Jest snapshot |
| 性能测试 | k6 | Artillery, Locust |
| 知识库 | 自建向量库(pgvector) | Pinecone, Weaviate |
| CI/CD | GitHub Actions + ArgoCD | GitLab CI, Jenkins |
不要犯这些错
❌ 误区一:Spec 写得太细
有些团队把 spec 写成伪代码,每一行都规定死。Dev Agent 变成了打字机,没有发挥空间。好的 spec 描述”做什么”和”怎么算对”,不描述”怎么做”。
❌ 误区二:让 AI 自己写 Spec
PM Agent 的作用是补全和结构化,不是从零写 spec。业务背景、核心价值、优先级判断——这些需要人类。让 AI 独立写 spec,等于让 AI 替你做产品决策。
❌ 误区三:忽视 Guard Agent
团队最容易砍掉的环节。”测试都过了,架构检查一下意思意思就行。” 然后三个月后代码库里出现 12 个全局变量和 3 个 circular import。
❌ 误区四:Spec 归档了就不管了
归档不是终点。要持续追踪 spec 的健康度,定期 review 哪些 spec 的验收标准已经过时、哪些被频繁违反。spec 是活的文档。
总结:为什么这个模式能赢
AI 编码的竞争不是”谁的模型更强”,而是“谁的工程方法能让 AI 持续输出高质量代码”。
OpenSpec + 多 Agent 闭环的核心价值:
- 可预测:spec 定义了”对”的标准,Agent 不会天马行空地发挥
- 可验收:spec 即测试,没有”看起来对了”的灰色地带
- 可追踪:每个代码行都能追溯到 spec 的哪个 requirement
- 可复用:spec 知识库越用越聪明,新 feature 的启动成本越来越低
- 可扩展:加人不用加沟通成本,spec 就是新成员的 onboarding 手册
最后说一句:这套方法不是为了”管理 AI”而存在的。即使没有 AI,规范驱动开发也是软件工程的最佳实践。AI 只是把这个模式的杠杆效应放大了 10 倍。
规范驱动不是束缚,是让创造力在正确轨道上行驶的护栏。
Kate / CTO / 相信规范比信仰更重要
2026年5月