让AI写代码不翻车,只需要三个卡口

你大概率遇到过这些场景:让AI帮你加个功能,它上来就改代码,改完一跑测试全是红的;或者改着改着偏离了你最初的需求,你花在纠偏上的时间比自己写还长;或者过了两周想查某个功能为什么这么设计,翻遍聊天记录也找不到。
这些问题的根源是同一个——AI在三个环节最容易翻车:需求理解、测试纪律、代码和文档的一致性。它不理解上下文就动手、跳过测试直接交差、改完代码不更新文档。人类程序员也会犯这些错,但AI犯起来毫无心理负担,而且速度更快、规模更大。
之前我写过一篇文章“都说Superpowers和OpenSpec好用,一个管代码质量,一个管变更记录——到底行不行?”,介绍了两个专门解决这些问题的开源框架:OpenSpec管「做什么和为什么」(规格管理和变更记录),Superpowers管「怎么做和做得对不对」(TDD、子代理审查、验证)。当时我提了一个理论上的「三段式分工」方案,但没实际验证过。
这篇就是填坑的——我把两个框架串成了一套完整的工作流,写了一套叫spec-dev的Claude Code扩展指令(你可以理解为一段可复用的自动化脚本)来协调它们,然后用两个真实的开源Java项目做了试跑。先看它怎么工作的。
三个卡口:防住AI最容易翻车的环节
spec-dev做的事情不复杂——在AI动手写代码之前、写代码的过程中、写完代码之后,各设一个卡口:
卡口一:动手之前,先把需求想清楚(Spec阶段)。AI不能直接写代码,必须先产出一份完整的规格文档——你要改什么、为什么改、涉及哪些文件、技术方案是什么、怎么验证。全部写完、用户确认之后,才允许进入下一步。OpenSpec负责驱动这个阶段,产出proposal、design、specs、tasks这些文档。
卡口二:写代码的时候,必须先写失败测试(Build阶段)。Superpowers的TDD铁律——没有失败测试就不能写生产代码。AI不能跳过测试直接交差,每个功能必须走「写失败测试→验证失败→写最小实现→验证通过」的循环。小功能(≤5个任务)在当前会话直接做,大功能则派子代理(各自是独立的AI会话,只负责一个任务)分头执行。
卡口三:写完之后,代码和规格必须对得上(Close阶段)。逐项检查:规格里说SearchCommand要改,代码里是不是真的改了?规格里说Handler不需要动,代码里是不是真的没动?发现不一致就必须修——改代码或者改规格,直到两边完全一致。然后归档变更,把规格增量合并到主规格库。
两个框架的对接逻辑并不复杂:OpenSpec产出的规格文档是Superpowers计划生成的输入,Superpowers的审查结果又在Close阶段反馈回OpenSpec的规格文档。信息在三个阶段之间自动流转。
不管你用不用spec-dev,这个三卡口思路可以直接拿走:动手之前写清楚做什么→写代码的时候先写测试→写完之后验证代码和文档一致。卡口本身不复杂,但确实能防错。下面看两次真实试跑中它们是怎么发挥作用的。
第一次试跑:给任务管理CLI加优先级——流程通了,skill本身踩了6个坑
第一个验证项目是taskmanager-cli(GitHub: ailsonguedes/taskmanager-cli),一个Java写的命令行任务管理工具,用SQLite做存储。要加的功能是「任务优先级」——新增Priority枚举、改数据库schema、CLI支持--priority参数、列表按优先级排序。
Spec阶段很顺利,拆出5个任务。按当时的阈值触发了完整模式——每个任务需要3个子代理轮着执行和审查,5个任务下来就是15+次调用。对一个小项目来说,这个开销偏重了。阈值后来调整为≤5轻量、≥6完整。
真正有意思的是Build阶段暴露的问题。6个问题,每个都是在实际跑的时候才发现的设计盲点。
最典型的一个:框架间的规则冲突。Superpowers要求所有开发在Git worktree(隔离的Git分支,不影响主分支)里做,标记为强制要求。但实际场景中,改三四个文件的小功能根本不需要隔离——直接在当前分支做反而更快。两套框架在这个点上撞车了。组合多个框架时,要预设它们的规则可能冲突,提前明确谁优先。
另一个有代表性的:集成测试的数据污染陷阱。项目用SQLite做存储,测试写入的数据跨运行累积——第一次跑测试通过,第二次就失败了,因为数据库里已经有上次的数据。如果你让AI写集成测试,一定要检查它有没有处理数据清理。这种问题人类凭经验能避开,但AI不会主动考虑。
另外4个问题本质上都是同一类——指令不够具体。阈值设错是因为没实际跑过、归档步骤模糊是因为没考虑目录已存在的情况、框架间context不一致是因为各自假设了不同的执行环境。这些问题的共同特征是:光看文档设计想不出来,必须实际跑了才知道哪里有缺口。
每发现一个问题,当场改完继续跑。第一次试跑的核心结论:流程方向对了,但实现细节的打磨只能靠真实项目暴露。6个问题全是「文档上看着没问题,跑起来才知道」的情况——这也说明为什么验证工作流不能只用demo项目。
第二次试跑:给Maven Central搜索工具加排序——卡口三真的抓到了问题
第二次换了个更有挑战的项目——mcs(maven-central-search),用Java写的搜索Maven Central仓库的CLI工具。这个项目用了Java新语法(sealed interface、record)、依赖注入(Dagger)、自动格式化(spotless)——跟真实的生产项目没区别。
要加的功能是--sort排序选项——改7个源文件加5个测试文件,4个任务,轻量模式。
Spec和Build阶段都按预期走完了——先写失败测试、验证失败、写实现、验证通过,TDD循环没出意外。
真正的亮点在Close阶段。逐项对比代码和规格文档的时候,发现了一个不一致:spec文档里标记SearchCommandHandler为MODIFIED(需要修改),但实际代码里Handler根本没动——sort参数是通过Builder传递的,不需要改Handler。
怎么会这样?因为Spec阶段AI在不动代码的情况下预判「哪些文件需要改」,本质上是一种猜测。它看到sort参数需要从CLI传递到查询层,就判断Handler需要改。但实际实现时发现Builder模式可以承载这个参数,Handler不需要动。如果没做一致性验证,这个错误就会沉淀在文档里,后面再有人看这份spec就会被误导。
这次试跑还暴露了另一个问题:构造函数签名变更的连锁反应。一个类的参数从3个变成4个,计划里漏掉了某个测试文件的调用点,编译时才报错发现。写计划时对「影响面」的分析很难做到100%完整——这也印证了TDD的价值,编译器和测试是最好的兜底。
规格先行不是一次性投资,是持续维护的资产。代码改了但规格没更新,这份规格就从「文档」变成了「误导」。一致性验证的核心价值就是防止这种脱节——而且两次试跑证明,脱节确实会发生。
两次试跑后的真实结论:能用,但要知道它的边界
直接说结论:工作流是可行的,但不是「启动就不管」的自动化工具,而是有纪律的工作流引导。它能防错,但也会带来开销。
Token消耗:轻量模式(≤5个任务)是单代理执行的,Spec阶段大概消耗一个会话的1/4,Build阶段消耗1/2到2/3。比纯Superpowers的子代理模式省,但比裸写代码肯定贵。值不值取决于场景:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Spec预判精度有上限。两次试跑都出现了规格和实际代码不一致的情况——因为在不动代码的情况下预判「哪些文件需要改、改成什么样」,本质上是一种猜测。好在close阶段的一致性验证能捕获这些偏差,但你需要接受一个「预判→实现→修正」的循环。这不是bug,是这种工作方式的固有特征。
对简单功能来说,文档产出量可能比代码变更量大。第二次试跑改了12个文件,但spec阶段产出了5个文档。这个比例在大型功能里合理(文档是投资),在小功能上会感觉重。如果要继续改进,最大的杠杆点是合并小功能的spec文档——proposal + design + specs合成一个精简文档,减少仪式感。
AI编程规范化的第一步不是规范,是找到AI在哪些环节最容易翻车,然后在这些环节加约束。两次试跑验证了:需求理解、测试纪律、代码-文档一致性这三个卡口确实能防错,而且实现成本不高。
如果你在用AI做项目开发,不管用不用spec-dev,至少做到这三步:AI动手之前把需求、方案、验证标准写清楚;写代码的时候先写测试;写完之后检查代码和文档是否一致。前期多花的10分钟,省的是后面反复纠偏的半小时。
本文是《都说Superpowers和OpenSpec好用,一个管代码质量,一个管变更记录——到底行不行?》的后续。上篇讲两个框架各自的能力和结合思路,这篇用两次真实试跑验证了结合方案。spec-dev的核心设计(三个卡口 + 轻量/完整模式 + 一致性验证)不依赖特定工具,你可以用任何工具链实现类似流程。
夜雨聆风