尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用SDD+TDD约束AI编程:OpenSpec与Superpowers协作实践

用SDD+TDD约束AI编程:OpenSpec与Superpowers协作实践 最近有大半年时间我做 AI 结对编程基本处于“又爱又恨”的状态。爱的是它确实能把我从大量样板代码里解放出来恨的是只要需求稍微复杂一点“直接让 Agent 写代码”的模式就开始失控改完一个 bug 崩掉两个功能测试全绿但你根本不知道是它改对了还是测试早就被它顺手改成了“永远绿”。后来我把 OpenSpec 和 Superpowers 组合起来用 SDDSpec-Driven Development规格驱动开发管需求用 TDDTest-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. 搭建 SDDTDD 工作流从环境准备到命令级流程前面都是在说理论现在到了真正的搭建环节。我按自己实际项目里的落地方案把过程拆成几个部分。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 是 OpenSpecSuperpowers 是 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 的规格变更纳入代码评审的必要范围任何没有对应规格变更的“伪需求”代码在评审阶段就会被拦下来。这在以前是想都不敢想的。在我看来这套 SDDTDD 工作流最值得参考的一点是它不再把 AI 当作一个“会写代码的聊天对象”而是把它当作一个需要被流程约束的执行者。规格管住需求测试管住实现人在中间只做判断和决策。有了这两个约束AI 写代码才不会越写越大胆项目才会越写越清楚。
返回列表