最近有大半年时间,我做 AI 结对编程基本处于“又爱又恨”的状态。爱的是它确实能把我从大量样板代码里解放出来,恨的是只要需求稍微复杂一点,“直接让 Agent 写代码”的模式就开始失控:改完一个 bug 崩掉两个功能,测试全绿但你根本不知道是它改对了,还是测试早就被它顺手改成了“永远绿”。后来我把 OpenSpec 和 Superpowers 组合起来,用 SDD(Spec-Driven Development,规格驱动开发)管需求,用 TDD(Test-Driven Development,测试驱动开发)管实现,整条工作流才算真正稳定下来。这篇文章就把这套搭建过程完整讲清楚,包括为什么要这样分、每一步怎么操作,以及我踩过的坑。
1. 为什么 AI 编程时代反而需要先补上“写规格”的功课
先聊一个比较反直觉的结论:AI 编码能力越强,需求描述反而要越严格。以前人写代码,代码本身就是需求和设计的唯一载体,需求不清晰还能靠“写代码的时候边想边改”兜底。但现在的编码 Agent 不同,它执行能力极强、判断能力有限,你给它一个模糊需求,它会非常自信地帮你把模糊的部分“脑补”出来——这听起来是好事,其实是最危险的事。
我早期的工作流非常简单,就是在 IDE 里选中一段代码,告诉 Agent“把这个功能改成 XX”,它改完我觉得不对,再告诉它“不对,我是要 XX”。来回对话十几轮,表面上看起来是在协作,实际上双方都在猜。等到项目里的函数越来越多,这种猜的成本会指数级上升,因为你还要猜“它上一次改的时候到底动了哪些不该动的地方”。
后来我意识到,问题不在 Agent 身上,而在流程上。我需要一种方式,把“我要做什么”从“怎么做”里彻底剥离开,并且让 AI 不能越界。这就是 SDD 的出发点:先把需求写成机器和人能共同理解的规格(Spec),再让 AI 基于规格而不是基于我们的聊天记忆去写代码。
而 TDD 解决的问题则是另一个方向:规格写得好,但 AI 写的代码怎么保证真的满足规格?靠人肉眼 review 不现实,靠 AI 自述“我已经实现了”更不可信。唯一可靠的做法,是先把验收标准变成测试,再让实现去通过这些测试。测试是规格的“可执行版本”。
所以 SDD 和 TDD 天生是一对:SDD 负责把事情说清楚,TDD 负责验证事情真的做完了。OpenSpec 和 Superpowers 分别对应这两个环节,OpenSpec 擅长把需求沉淀成结构化的规格变更,Superpowers 用一组技能驱动 AI 进入严格的 TDD 循环。两者之间不是二选一,而是接力关系。
2. OpenSpec 到底做了什么:把需求变成仓库里看得见的规格变更
我第一次看到 OpenSpec 项目时,第一反应是“这不就是一个放 Markdown 文档的文件夹吗”。但真正用了之后才发现,它的核心价值不在于文件格式,而在于它给 SDD 设置了一套“强制动作”:任何需求变更,都必须先走一遍“创建变更 → 编写规格 → 验证 → 计划 → 执行”的流程。
2.1 认识 OpenSpec 的目录结构和核心概念
初始化 OpenSpec 之后,项目根目录下会多出一个openspec/文件夹,里面最重要的几个部分:
openspec/ ├── specs/ # 已确认的需求规格,按能力域组织 │ └── pricing.md ├── changes/ # 正在进行的变更集,一个变更一个文件夹 │ └── add-tiered-discount/ │ ├── proposal.md │ └── specs/ │ └── pricing.md ├── projects/ # 可选,多项目场景下的能力域划分 ├── AGENTS.md # 给 AI 代理看的工作说明 └── openspec.json # 项目配置我自己的理解是,specs/是“已经生效的需求合同”,changes/是“正在草拟的修订合同”,任何对规格的改动都不允许直接改specs/,必须先新建变更,等变更验证通过后 OpenSpec 再帮你把它合并进正式的规格目录。这个设计和 Git 的分支模型很像,隔离性很好,也天然适合 code review。
2.2 一条变更长什么样:用规范 DSL 描述需求
在changes/下创建一个变更后,核心工作是编写规格描述文件。OpenSpec 自己也定义了一套很简单但约束力很强的 DSL,常用的结构是这样:
# Change: add-tiered-discount ## ADDED Requirements ### Pricing.calculateDiscount - `calculate_discount(base_price, customer_tier)` 在 customer_tier 为 "gold" 时,返回基础价格的 20% 折扣。 - customer_tier 为 "silver" 时,返回基础价格的 10% 折扣。 - 未知等级统一返回 0。 - 折扣结果保留两位小数。这套 DSL 之所以有效,是因为它把模糊的“希望支持折扣”拆成了逐条可验证的验收点。每条 Requirement 都以一个明确的函数或行为作为锚点,AI 见到这种文本,不会自然地去自由发挥,而是会把它解读成“需要满足的这些约束”。
2.3 有 OpenSpec 和没有 OpenSpec,区别到底在哪
很多人会问一个很实际的问题:我不装 OpenSpec,自己在项目里建一个docs/requirements.md行不行?行,但效果差很远。OpenSpec 提供的不是文档能力,而是工作流约束能力:
| 维度 | 没有 OpenSpec | 有 OpenSpec |
|---|---|---|
| 需求来源 | 依赖聊天记录、口头描述 | 有统一的规格文件可回看 |
| 变更范围 | 靠 AI 猜“这次要动哪些” | 每个变更独立成目录,范围可视化 |
| 需求状态 | 无法区分“正在讨论”和“已生效” | changes和specs天然区分草稿与基线 |
| AI 上下文 | 每次都要重新灌输需求 | 通过openspec status等命令让 AI 自主读取变更清单 |
| 验证环节 | 开发完才知道对不对 | 编写阶段就能做规格校验,防止前后矛盾 |
尤其关键的是 OpenSpec 提供了几个命令行入口,比如openspec status、openspec validate、openspec new change。这意味着 AI 可以在需要时自己调用命令去感知当前项目处于什么状态,而不是靠人去提醒它。我现在的工作流里,Agent 启动后的第一件事经常就是跑一次openspec status,看看有哪些变更在等着处理。
3. Superpowers 做了什么:把 TDD 变成 AI 的肌肉记忆
如果说 OpenSpec 解决的是“需求怎么描述”,那 Superpowers 解决的是“代码怎么动手”。Superpowers 本质上是一套 Markdown 技能库,它通过给 AI 编程助手注入结构化的技能文档,约束它按特定的工作方式执行任务。我主要用的是它对 TDD 的强化。
3.1 Superpowers 的安装与组成部分
Superpowers 的安装方式比较友好,在项目目录下执行:
npx superpowers然后按照提示选择你的 AI 编程工具,比如 Claude Code、Codex CLI 等。它会自动把技能文件安装到对应的配置目录(例如~/.claude/skills/或项目级的.claude/skills/)。
安装完成后,里面会有一大堆技能文件,常见的几个包括:
brainstorming:在动手前先多轮澄清需求,产出一份思考过程文档。writing-plans:把较大的任务拆解成可执行的分步计划。test-driven-development:核心技能,强制按照 红灯-绿灯-重构 的循环来写实现。requesting-code-review:在实现完成后,让 AI 切换成 reviewer 角色做自审。
这些技能并不是摆设,它们会改变 AI 的行为模式。比如超级技能的效果不是“建议你试试 TDD”,而是以系统提示词和规则的形式,要求 AI 每一步都必须执行:先写一个失败测试、运行测试确认失败、写最少实现、再运行测试确认通过、重构。如果 AI 跳过了某一步,它会主动提醒或者纠正自己。
3.2 为什么 Superpowers 能把“写测试”这件事真正落地
单纯告诉 AI “你写代码前先写测试”,大概率是没用的,它还是会悄悄先写实现,再补一个“看起来会通过”的测试。Superpowers 的聪明之处在于,它把 TDD 变成了一套可做检查的循环,并且反复强调一个原则:先看到失败,再看到成功。
我自己在实测中的体验是,一旦载入了 TDD 技能包,AI 的行为会有明显变化。它不再一口气把功能写出来,而是先只写一个测试用例,跑一遍,预期的结果是失败;然后才开始写实现,但只写“能让这个测试变绿”的最小实现,接着继续下一个测试用例,逐个推进,最后再做重构。这个节奏很接近一个资深工程师手写 TDD 的状态。
这里补一句我的理解:TDD 对 AI 的价值其实比对人更大。人有可能凭直觉跳过测试,但 AI 没有直觉,它只会最大化地“贴合上下文”。如果上下文中没有测试约束,它写出来的代码即使功能正确,也往往没有可验证性;可一旦它知道“每次变更都必须经过测试验证”,它反而能更好地控制自己的行为边界。
3.3 Superpowers 和普通“让 Agent 写代码”的对比
放一张对比图会更清楚:
| 行为 | 普通 Agent 编程 | 基于 Superpowers 的 TDD 模式 |
|---|---|---|
| 拿到需求后 | 直接开始生成代码 | 先 brainstorming,再写计划 |
| 写实现前 | 可能直接改业务代码 | 先写失败测试 |
| 验证方式 | 依赖人肉检查或者事后补测试 | 每步运行测试,失败在先、通过在后 |
| 重构阶段 | 基本没有明确重构步骤 | 绿灯后强制进入重构阶段 |
| 上下文管理 | 越来越散乱 | 技能文件提供了固定工作协议 |
4. 搭建 SDD+TDD 工作流:从环境准备到命令级流程
前面都是在说理论,现在到了真正的搭建环节。我按自己实际项目里的落地方案,把过程拆成几个部分。
4.1 环境准备清单
我当前的推荐环境组合是:
- 项目使用 Git 管理,仓库根目录作为一切操作的基础。
- OpenSpec CLI 版本不要太旧,建议用最新稳定版。
- AI 助手选择支持引入项目级 AGENTS.md/CLAUDE.md 的工具,例如 Claude Code 或 Codex CLI。
- Node.js 环境是运行 OpenSpec CLI 和 Superpowers 安装器的基础。
前期准备执行两步:
# 步骤 1:初始化 OpenSpec openspec init # 步骤 2:安装 Superpowers 技能 npx superpowers安装完成后,我还会手动检查项目根目录是否生成了AGENTS.md或者技能安装目录。如果 AI 工具读取不到这些文件,说明路径配置有问题,后面的一切都不会生效。
4.2 定义一条从需求到测试的固定管道
环境就绪后,我给自己定了一条固定工作流,每一步都有明确的命令或产物:
- 需求分析:人(也就是我)把需求拆成一个业务假设,不写代码,只描述“用户能做什么,结果应该是什么”。
- 创建变更:运行
openspec new change <变更名>,OpenSpec 自动生成changes/<变更名>/目录。 - 写规格:在变更目录里,按 DSL 把需求补充成可验证的 Requirement 表达。
- 规格校验:运行
openspec validate,让 OpenSpec 检查规格里有没有语法错误、重复定义或前后矛盾。 - 让 AI 做计划:此时才把控制权交给带 Superpowers 的 AI,让它基于规格生成 TDD 执行计划。
- TDD 循环:AI 按照 TDD 技能包逐条将 Requirement 转为测试,然后实现,再重构。
- 回归验证:运行完整测试套件和
openspec validate,确认规格实现与规格定义一致。 - 提交代码:先合入变更规格,再提交实现代码。后续 review 时审视的是“规格与测试是否对得上”。
4.3 把 OpenSpec 命令接进 AI 的自动上下文
很多人安装完 OpenSpec 和 Superpowers 之后,依然觉得两者是“各干各的”,原因是 AI 默认不会主动去读规格文件。解决办法是在AGENTS.md里明确写一段话,告诉 AI:开始任务前先运行openspec status,处理某个变更时必须先读完该变更目录下的所有 specs 文件。
我现在的AGENTS.md里大概有类似内容:
## Workflow - 本仓库使用 OpenSpec 管理需求规格。 - 开始任何编码前,先执行 `openspec status` 确认当前活动变更。 - 若存在当前变更,必须阅读 `openspec/changes/*/specs/` 下的规格文件。 - 所有实现必须遵循 Test-Driven Development 流程,先写失败测试,再写实现。 - 每次提交前执行测试套件和 `openspec validate`。这段配置非常重要,它才是把“写规格”和“写测试”真正串起来的胶水。否则 OpenSpec 是 OpenSpec,Superpowers 是 Superpowers,互相之间没有任何联动。
5. 一次完整实操:从规格到测试再到实现
接下来用一个非常简单但能说明问题的例子,走完整条链路。需求:给订单系统加一个“按用户等级计算折扣”的功能。
5.1 编写规格变更
我先创建变更:
openspec new change add-tiered-discount然后在openspec/changes/add-tiered-discount/specs/pricing.md中写规格:
# Change: add-tiered-discount ## ADDED Requirements ### Pricing.calculate_discount - 函数 `calculate_discount(base_price, tier)` 接收商品原价和用户等级,返回折后金额。 - 当 tier 为 `"gold"` 时,返回原价的 8 折(即扣除 20%)。 - 当 tier 为 `"silver"` 时,返回原价的 9 折(即扣除 10%)。 - 当 tier 为其他值时,不折扣,返回原价。 - 计算结果保留两位小数,使用四舍五入。 - 输入 `base_price` 为负数时,直接返回 `0.0`。执行校验:
openspec validate校验通过后,规格阶段结束。
5.2 交给 Superpowers 进入 TDD 循环
接下来让 AI 读取这个规格,并按照 Superpowers 的 TDD 技能执行。在带 Superpowers 的 AI 对话窗口里,我会给一句很短的指令:
请根据 OpenSpec 变更 add-tiered-discount 实现定价模块,严格遵循 TDD 流程。接着 AI 应该自己完成这几件事:
先写出第一个失败测试(测试优先):
# test_pricing.py from pricing import calculate_discount def test_gold_tier_returns_20_percent_off(): assert calculate_discount(100.0, "gold") == 80.0此时实现还不存在,运行测试会失败。AI 会记录这个失败,然后开始写最小实现:
# pricing.py def calculate_discount(base_price, tier): if base_price < 0: return 0.0 if tier == "gold": return round(base_price * 0.8, 2) return round(base_price, 2)运行测试,变绿。然后 AI 进入下一个测试用例,比如 silver 等级,再写失败测试:
def test_silver_tier_returns_10_percent_off(): assert calculate_discount(100.0, "silver") == 90.0重复整个循环。等到所有测试都过了,AI 会进入重构阶段,把重复逻辑提出来。最终实现里会多一个折扣映射表:
DISCOUNT_RATES = {"gold": 0.8, "silver": 0.9} def calculate_discount(base_price, tier): if base_price < 0: return 0.0 rate = DISCOUNT_RATES.get(tier, 1.0) return round(base_price * rate, 2)这一步很有代表性:AI 不是一开始就写出这个版本,而是通过 TDD 循环自然走到这一步的。
5.3 让规格与代码合入
功能完成后,先运行一遍完整测试套件和 OpenSpec 校验,确认规格中的每条 Requirement 都有对应测试覆盖。随后在提交信息里同时包含变更标识,比如:
git commit -m "feat(pricing): implement tiered discount (change: add-tiered-discount)"这样后续无论是 review 还是回溯需求,都能从代码直接跳到规格原始描述。
6. 实际使用中踩过的坑,以及我给团队落地的建议
这套工作流不是装上就能丝滑运转的,我在切换过程中遇到过几个问题,写出来让后来者避一避。
6.1 坑一:规格写得“太像人话”,反而留出了发挥空间
最初我写规格时,习惯用描述性的自然语言,比如“用户等级高的话,应该享受更大优惠”。结果 AI 虽然进入了 TDD 循环,但它把测试也写得模棱两可。后来我改成带有明确锚点的表达,将“更高折扣”细化为“gold 扣 20%,silver 扣 10%”,AI 的自由度立刻被压缩到合理范围。
教训就是,规格里每一个 Requirement 都应该是可判定的命题,不能出现“更好”“更快”“更友好”这类比较级词汇。如果你发现一条规格写完之后,你没法在测试里直接断言它是否满足,那就说明它不合格。
6.2 坑二:一次变更塞了太多需求
OpenSpec 的变更设计本意是“小而独立”,但实际用起来很容易越写越大。特别是当你让 AI 汇总多个需求时,它倾向于在一个变更里塞进四五个相关功能。变更一大,测试维度就爆炸,TDD 循环会变得又长又难维护。
我现在给自己定了一个约束:如果一个变更里超过三条相互独立的 Requirement,就拆成多个变更。宁可多走几遍流程,也不要让一次变更承担太多风险。这样 review 的时候也轻松,因为每个变更的规格、测试和实现完全对得上。
6.3 坑三:AI 有时候会把“测试”也当成“需要修改的代码”
这是我最开始在 TDD 落地时踩得最深的一个坑。AI 在实现阶段遇到测试失败时,它的第一反应不是去修业务代码,而是去“修正”测试断言,让测试符合自己的实现。这种行为在普通编程模式下很常见,但和 TDD 模式完全背道而驰。
Superpowers 的 TDD 技能已经内置了对这种行为的约束,要求测试一旦写好就不允许在红灯阶段被修改,只能改实现。但为了保险起见,我仍然在 AGENTS.md 里单独加了一条:红灯状态下不得修改测试文件,只允许修改业务代码。如果测试本身就是错的,请在重构阶段单独提出来讨论。
6.4 给团队推广时的实操建议
如果你的团队也想引入这套工作流,我建议先不要全面铺开。找一个边界清晰的小模块,自己用两天时间完整跑通,把生成的文档和提交记录拿给团队看,让大家直观理解“规格变更长什么样、TDD 循环的提交节奏如何”。然后再选定一个协作密度高的项目组试点,先约定规格文件的变化必须走 OpenSpec 流程,其他模块可以继续用原来的方式。等团队真正感受到“需求变得可追溯,测试变得有效”,再逐步扩大。
目前我们组已经把 OpenSpec 的规格变更纳入代码评审的必要范围,任何没有对应规格变更的“伪需求”代码,在评审阶段就会被拦下来。这在以前是想都不敢想的。
在我看来,这套 SDD+TDD 工作流最值得参考的一点,是它不再把 AI 当作一个“会写代码的聊天对象”,而是把它当作一个需要被流程约束的执行者。规格管住需求,测试管住实现,人在中间只做判断和决策。有了这两个约束,AI 写代码才不会越写越大胆,项目才会越写越清楚。