1. 当AI写代码开始“堆量”,问题出在哪
“氛围编程”这个词最近在圈子里被反复提起,说的是一种很典型的状态:打开AI编程助手,把需求用自然语言描述一遍,AI哗啦啦吐出一大段代码,看着像模像样,跑起来也能凑合,于是直接合并进主分支。第二天再加个功能,又让AI生成一坨,继续往上叠。两周之后,项目里出现了三套风格完全不同的工具函数、两套互相打架的状态管理逻辑、一堆没人敢删的“僵尸代码”,改一个字段名能引发五个模块报错——这就是所谓的代码雪崩。
我自己经历过一次最惨的:一个内部工具项目,前期靠AI快速堆出了大概六千行代码,功能确实跑通了。但当我试图给核心模块加一个字段校验时,发现同一个数据模型在四个文件里有四种不同的定义方式,有的用驼峰有的用下划线,有的做了空值判断有的直接裸奔。那一刻我才意识到,AI帮我省下的时间,正在以三倍的速度被还回去。
这个问题的根源不在于AI写得不好,而在于缺少工程纪律的约束。AI是一个极度高效的“代码生成器”,但它不是一个“架构师”,更不是一个“项目经理”。你给它什么上下文,它就产出什么结果;你不给它规则,它就会用最省事的方式完成任务——而“最省事”往往意味着“最不可维护”。
Superpowers 技能框架、TDD 契约驱动、AI 工程纪律闭环,这三个词组合在一起,本质上是在回答一个问题:怎么让AI在写代码的时候,像一个受过良好工程训练的人那样去思考?这不是一个工具层面的问题,而是一套工作流的重构。接下来我会把这套框架拆开,从它解决什么问题、核心机制是什么、怎么落地、踩过哪些坑,一层层讲清楚。
2. Superpowers 技能框架到底在管什么
2.1 它不是代码库,是行为约束层
第一次听到“Superpowers 技能框架”这个名字,很容易误以为它是一个类似插件市场的东西,装上去就能让AI变强。实际上它的定位完全不同——它是一层行为约束协议,作用在AI生成代码之前和之后,规定“什么能做、什么不能做、做完之后要检查什么”。
打个比方:AI编程助手像是一个能力很强但没什么规矩的实习生,你让他写个登录功能,他十分钟就能给你写出来,但他不会主动考虑密码加密、不会考虑错误提示的国际化、不会考虑接口的幂等性。Superpowers 技能框架做的事情,就是给这个实习生一本《员工手册》,里面写清楚了:写任何接口之前必须先定义输入输出契约,写任何业务逻辑之前必须先写测试用例,提交任何代码之前必须通过静态检查。
它的核心组成大概可以分成三块:
- 技能定义(Skill Definition):把一类任务的标准操作流程固化下来。比如“新增一个API接口”这个技能,会明确规定步骤:先写接口契约文档,再写失败测试,再实现最小可用代码,再重构,最后跑全量测试。
- 触发规则(Trigger Rules):定义什么情况下激活哪个技能。比如当AI检测到用户输入中包含“新增接口”“添加字段”“修改数据结构”等关键词时,自动加载对应的技能流程。
- 校验钩子(Validation Hooks):在关键节点插入检查点。比如代码生成完毕后,自动运行lint、类型检查、单元测试,任何一项不通过就阻断后续流程。
这三块合在一起,形成了一个“事前有规范、事中有引导、事后有校验”的闭环。我自己的体会是,技能框架最大的价值不是让AI写出更好的代码,而是让AI写出更一致的代码。一致性在多人协作和长期维护中的价值,远远超过单次生成的代码质量。
2.2 技能定义怎么写才有效
写技能定义这件事,很多人一开始会走偏。最常见的错误是把技能定义写成了一篇“最佳实践文档”,洋洋洒洒几千字,从设计模式讲到代码整洁之道,结果AI根本执行不了——因为太抽象了。
有效的技能定义必须满足三个条件:可执行、可验证、可组合。
可执行的意思是,每一步都是一个明确的动作,而不是一个原则。比如“遵循单一职责原则”就不是一个可执行的动作,但“每个函数不超过30行,超过则拆分为多个函数”就是可执行的。
可验证的意思是,每一步做完之后,能有一个明确的判断标准。比如“写一个测试用例”之后,验证标准是“该测试用例在当前代码下必须失败”——这就是TDD中经典的“红”阶段。
可组合的意思是,技能之间可以嵌套调用。比如“新增API接口”这个技能,内部会调用“写契约文档”“写测试”“实现代码”“重构”四个子技能。这种组合能力让技能框架可以覆盖从简单到复杂的各种任务。
我实际用下来,一个中等复杂度的技能定义大概在200到500字之间,包含5到8个步骤,每个步骤配一个验证条件。太短了覆盖不全,太长了AI执行时会丢失细节。
2.3 触发规则的设计陷阱
触发规则看起来简单,实际上是最容易出问题的地方。早期我用的规则很粗暴:只要用户输入里包含“写代码”“实现”“新增”就触发全套TDD流程。结果就是,有时候我只是想让AI帮我改个注释,它也给我走一遍完整的契约定义和测试流程,烦不胜烦。
后来我把触发规则做了分层:
| 触发级别 | 触发条件 | 执行动作 |
|---|---|---|
| 轻量级 | 修改注释、格式化、重命名变量 | 直接执行,仅跑lint |
| 中等级 | 修改函数内部逻辑、调整参数 | 跑相关单元测试 |
| 重量级 | 新增接口、修改数据结构、跨模块调用 | 全套TDD流程+契约校验 |
这个分层的关键在于识别“影响范围”。修改注释不影响任何逻辑,没必要走全套流程;但修改数据结构可能影响十几个调用方,就必须严格校验。识别影响范围这件事,AI目前做得还不够好,我的做法是在技能定义里显式列出“影响范围判断规则”,比如“如果修改涉及数据库模型文件,则自动升级为重量级”。
注意:触发规则不要追求一步到位。我一开始就想设计一套完美的规则,结果花了三天时间写出来的规则,实际用起来误触发率超过40%。后来改成先跑一周,记录所有误触发和漏触发的情况,再针对性调整,效果好了很多。
3. TDD 契约驱动:让AI先想清楚再动手
3.1 为什么契约要写在测试之前
TDD(测试驱动开发)本身不是一个新概念,但把它和AI编程结合起来之后,产生了一个很有意思的化学反应:AI在写测试的时候,比写实现的时候认真得多。
这个现象我观察了很久,后来想明白了。AI写实现代码时,倾向于“尽快让代码跑起来”,所以会走捷径、会忽略边界条件、会用最直接的方式解决问题。但写测试代码时,AI的思维模式切换成了“穷举所有可能的情况”,因为测试的本质就是覆盖各种输入和边界。
所以“契约驱动”的核心思路是:让AI先写测试,用测试来定义契约,然后再写实现。这里的“契约”不仅仅指接口的输入输出,还包括异常处理、边界条件、性能约束等。
举个例子。假设要让AI实现一个“用户积分计算”函数。如果不加约束,AI可能直接写:
def calculate_points(user): return user.order_count * 10 + user.login_days * 2这段代码能跑,但契约是模糊的:order_count为负数怎么办?login_days超过365怎么处理?积分有没有上限?这些都没定义。
如果走契约驱动流程,第一步是让AI写测试:
def test_calculate_points_normal_user(): user = User(order_count=5, login_days=30) assert calculate_points(user) == 110 def test_calculate_points_zero_orders(): user = User(order_count=0, login_days=30) assert calculate_points(user) == 60 def test_calculate_points_negative_orders_raises(): user = User(order_count=-1, login_days=30) with pytest.raises(ValueError): calculate_points(user) def test_calculate_points_max_limit(): user = User(order_count=1000, login_days=365) assert calculate_points(user) <= 10000写完这些测试之后,契约就清晰了:负数订单要抛异常,积分有上限。然后再让AI写实现,它就会按照测试定义的契约来写,而不是自己拍脑袋决定。
3.2 契约文档的格式与维护
测试是契约的一种形式,但不是唯一形式。对于跨模块、跨服务的交互,还需要一份显式的契约文档。我用的格式很简单,就是一个Markdown表格:
| 字段名 | 类型 | 必填 | 约束 | 示例 |
|---|---|---|---|---|
| user_id | string | 是 | 长度32,十六进制 | "a1b2c3d4..." |
| action | string | 是 | 枚举值:add/subtract | "add" |
| amount | integer | 是 | 1-1000 | 50 |
| reason | string | 否 | 最大长度200 | "每日签到" |
这份文档由AI根据测试用例自动生成,生成之后人工审核一遍,确认没有遗漏。之后每次修改接口,都必须先更新这份文档,再更新测试,最后更新实现。这个顺序不能乱,乱了就会出问题。
我踩过的一个坑是:有一次让AI直接改实现,改完之后再补测试。结果AI改实现的时候顺手改了一个字段的默认值,补测试的时候又按照新的默认值来写,导致测试和实现“完美匹配”,但和上游调用方的预期完全不符。上线之后直接报错。顺序就是纪律,纪律不能妥协。
3.3 契约变更的影响面分析
契约驱动最难的环节不是写契约,而是契约变更时的影响面分析。一个字段的类型从string改成integer,可能影响几十个调用方,哪些需要同步修改、哪些可以兼容、哪些必须发版,这些判断目前AI还做不好。
我的做法是建立一个“契约依赖图”。每次新增或修改契约时,让AI扫描代码库,找出所有引用了该契约的文件和函数,生成一张依赖关系表。然后根据依赖关系表,逐个判断影响级别:
- 直接依赖:直接调用该接口或使用该数据结构的代码,必须同步修改。
- 间接依赖:通过中间层间接使用的代码,需要评估是否受影响。
- 无依赖:确认不受影响的代码,标记为安全。
这张依赖图不需要很精确,但必须存在。没有它,契约变更就是盲人摸象。我现在的项目里,每次契约变更前都会让AI生成依赖图,然后人工确认一遍,再决定变更方案。这个流程大概会增加15%的前期时间,但能避免80%以上的线上事故。
4. AI 工程纪律闭环:从生成到合并的完整链路
4.1 闭环的五个关键节点
“工程纪律闭环”这个词听起来很宏大,拆开来看其实就是五个节点:定义、生成、校验、修复、合并。每个节点都有明确的输入和输出,节点之间通过自动化工具串联。
定义节点:确定要做什么,产出契约文档和测试用例。这个节点由人主导,AI辅助。
生成节点:根据契约和测试生成实现代码。这个节点由AI主导,人监督。
校验节点:运行测试、lint、类型检查、安全扫描。这个节点完全自动化。
修复节点:根据校验结果修复问题。这个节点AI主导,人确认修复方案。
合并节点:代码审查、合并到主分支。这个节点由人主导,AI提供辅助信息。
这五个节点形成一个循环,每次代码变更都要走一遍。听起来很重,但实际上大部分变更走的是轻量级路径,只有涉及核心模块的变更才走完整流程。
4.2 校验节点的工具链配置
校验节点是闭环中最关键的一环,因为它决定了“什么样的代码可以进入下一步”。我的工具链配置大概是这样:
# 校验流水线配置 stages: - name: lint tools: - ruff check . - mypy --strict . block_on_failure: true - name: test tools: - pytest --cov=src --cov-fail-under=80 block_on_failure: true - name: security tools: - bandit -r src/ block_on_failure: true - name: contract tools: - python scripts/validate_contract.py block_on_failure: true这个配置里有几个关键决策:
覆盖率阈值设为80%。这个数字不是拍脑袋定的。我试过90%,结果AI为了凑覆盖率写了一大堆无意义的测试,反而增加了维护成本。试过60%,又漏掉了太多边界情况。80%是一个平衡点,核心模块要求90%,工具类模块允许70%。
mypy用strict模式。类型检查在AI编程场景下特别重要,因为AI有时候会“忘记”类型约束,写出一些运行时才报错的代码。strict模式能在生成阶段就拦住大部分类型问题。
契约校验单独作为一个stage。这个脚本会检查代码中的接口定义是否和契约文档一致,不一致就阻断。这个检查帮我拦住了好几次AI“顺手改字段”的操作。
4.3 修复节点的自动化策略
修复节点是最容易失控的地方。AI发现测试失败后,会尝试自动修复,但它的修复方案有时候是“改测试”而不是“改实现”——这就完全违背了TDD的原则。
我的策略是:修复节点只允许修改实现代码,不允许修改测试代码和契约文档。如果AI认为测试有问题,它只能生成一份“测试异议报告”,由人工判断是否修改测试。
这个约束通过文件权限来实现:在修复阶段,测试文件和契约文档设为只读,AI只能修改src目录下的文件。如果AI尝试修改测试文件,流水线会直接报错并终止。
这个策略实施之后,修复节点的有效性提升了很多。以前AI经常“巧妙”地修改测试断言来让测试通过,现在它只能老老实实改实现。虽然有时候修复时间变长了,但代码质量明显提升。
4.4 合并节点的审查清单
合并节点是最后一道防线。我的审查清单大概有十几项,其中和AI编程直接相关的有几项特别重要:
- 生成代码的风格是否与项目一致:AI有时候会混用不同的代码风格,比如一会儿用驼峰一会儿用下划线,一会儿用函数式一会儿用面向对象。风格不一致的代码即使功能正确,也会增加后续维护成本。
- 是否有“孤儿代码”:AI在重构时有时候会留下一些不再被调用的函数或变量,这些代码不会导致测试失败,但会污染代码库。
- 注释是否准确:AI生成的注释有时候和实际代码逻辑不符,特别是当AI修改了实现但忘记更新注释时。
- 是否有硬编码的魔法数字:AI倾向于把配置值直接写在代码里,而不是提取成常量或配置文件。
这几项检查目前还需要人工完成,但我正在尝试让AI生成一份“自查报告”,把这些项目的检查结果列出来,人工只需要确认即可。
5. 落地过程中踩过的坑与应对方案
5.1 技能框架的“过度约束”问题
刚开始搭技能框架的时候,我恨不得把所有的工程规范都写进去,结果就是AI每做一步都要加载一大堆规则,生成速度慢得让人抓狂,而且经常因为规则之间冲突而卡住。
最典型的一次:我同时定义了“函数不超过30行”和“每个函数必须有完整的错误处理”两条规则。AI写一个包含五个错误分支的函数,光错误处理就写了20行,再加上正常逻辑,轻松超过30行。然后AI就开始纠结:是拆函数还是删错误处理?拆了函数又违反“相关逻辑放在一起”的规则,删了错误处理又违反“完整错误处理”的规则。最后AI直接卡住不输出了。
后来我学乖了,技能定义要分层,核心规则不超过10条,扩展规则按需加载。核心规则是那些绝对不能违反的,比如“先写测试再写实现”“契约变更必须同步更新文档”。扩展规则是那些视情况而定的,比如“函数长度限制”“注释覆盖率要求”。扩展规则只在特定场景下激活,不会同时全部生效。
5.2 契约驱动的“测试膨胀”问题
契约驱动的一个副作用是测试数量快速增长。每个契约变更都要新增测试用例,几个月下来测试文件比实现代码还长,跑一次全量测试要十几分钟。
这个问题不解决,TDD的反馈循环就会变得很慢,慢到开发者不愿意跑测试,TDD就名存实亡了。
我的应对方案是测试分层:
- 单元测试:针对单个函数的测试,运行时间在毫秒级,每次代码变更都跑。
- 集成测试:针对模块间交互的测试,运行时间在秒级,每次提交前跑。
- 契约测试:针对接口契约的测试,运行时间在十秒级,每天跑一次或契约变更时跑。
分层之后,日常开发只需要跑单元测试,反馈时间控制在5秒以内。集成测试和契约测试放到CI流水线里,不阻塞本地开发。
另外,我还让AI定期做“测试去重”:扫描所有测试用例,找出覆盖相同逻辑的冗余测试,合并或删除。这个操作大概能减少20%到30%的测试数量,同时保持覆盖率不变。
5.3 AI“遗忘”上下文的应对
AI编程助手有一个很烦人的特点:它会忘记之前定义的规则。特别是在长对话中,前面说了“所有接口必须做参数校验”,聊了十几轮之后,AI生成的新接口又忘了加校验。
这个问题目前没有完美的解决方案,我的做法是把关键规则嵌入到代码模板里。比如在项目里创建一个interface_template.py文件,里面预置了参数校验、错误处理、日志记录的框架代码。每次新增接口时,让AI基于这个模板来写,而不是从零开始。这样即使AI忘记了规则,模板本身也会强制它遵守。
另一个做法是定期“重述规则”。每隔几轮对话,我会主动把核心规则再发一遍给AI,提醒它遵守。虽然有点笨,但确实有效。
5.4 团队协作中的纪律对齐
这套框架在个人项目里跑得挺顺,但推广到团队时遇到了阻力。最大的问题是每个人对“纪律”的理解不一样。有人觉得覆盖率80%就够了,有人觉得必须90%;有人觉得lint警告可以忽略,有人觉得必须全部清零。
解决这个问题的关键是把纪律变成可量化的指标,并且把这些指标写进CI流水线,让机器来执行,而不是靠人的自觉。比如:
| 纪律项 | 量化指标 | 执行方式 |
|---|---|---|
| 测试覆盖率 | 核心模块≥90%,工具模块≥70% | CI阻断 |
| 类型检查 | mypy strict零错误 | CI阻断 |
| 契约一致性 | 接口定义与契约文档100%匹配 | CI阻断 |
| 代码风格 | ruff零警告 | CI阻断 |
| 注释覆盖率 | 公共函数100%有docstring | CI警告 |
这些指标一旦确定,就不再讨论,所有人按同一套标准执行。有异议可以提,但必须通过修改CI配置来变更,而不是在代码审查时扯皮。
6. 这套框架适合什么样的项目
6.1 最适合的场景:长期维护的核心项目
这套框架最大的价值在于长期可维护性,所以它最适合那些生命周期长、参与人数多、变更频率高的核心项目。比如一个持续迭代的业务系统,每周都有新需求,每个月都有新成员加入,代码库在持续膨胀。这种项目如果不加约束,半年之后就会变成没人敢动的“屎山”。
我目前在一个大概三万行代码的项目上跑这套框架,效果很明显:新成员上手时间从两周缩短到三天,因为契约文档和测试用例就是最好的文档;线上事故率下降了大概70%,因为大部分问题在CI阶段就被拦住了;代码审查时间减少了大概一半,因为格式和规范问题已经被机器检查过了。
6.2 不太适合的场景:一次性脚本和原型验证
反过来,如果你只是写一个一次性脚本,或者做一个快速原型验证想法,这套框架就太重了。定义契约、写测试、配流水线的时间可能比写代码本身还长。
我的判断标准是:如果这个代码的生命周期不超过一周,或者不会被第二个人修改,就不需要上这套框架。直接用AI快速生成,跑通就行,用完就扔。
但这里有一个灰色地带:很多项目一开始都说是“临时脚本”,结果用着用着就变成了核心工具。我的建议是,当你发现一个脚本被使用了超过三次,或者有第二个人开始依赖它,就应该考虑把它纳入框架管理了。
6.3 从轻量级开始逐步加码
如果你决定尝试这套框架,我的建议是不要一上来就全套铺开。先从最轻量的开始:只加一个lint检查和一个测试覆盖率检查,跑两周看看效果。如果觉得有用,再加契约文档和TDD流程。如果觉得太重,就退回到只保留lint和测试。
我自己的演进路径大概是这样:
第一阶段:只加lint和格式化,解决代码风格不一致的问题。 第二阶段:加单元测试和覆盖率检查,解决“改一处崩三处”的问题。 第三阶段:加契约文档和TDD流程,解决接口不一致和边界条件遗漏的问题。 第四阶段:加技能框架和自动化修复,解决重复性劳动和修复效率的问题。
每个阶段大概间隔一个月,让团队有时间适应。跳阶段推进的话,阻力会很大。
7. 一些实操中的小技巧
7.1 用AI生成技能定义初稿
技能定义不用从零开始写。我的做法是:先让AI分析项目里最近20次代码提交,找出重复出现的操作模式,然后基于这些模式生成技能定义初稿。比如AI发现“新增接口”这个操作出现了8次,每次的步骤都差不多,就可以自动生成一个“新增接口”的技能定义。人工只需要审核和调整即可。
这个做法能节省大概70%的技能定义编写时间,而且生成的技能定义更贴合项目实际情况,不是从网上抄来的通用模板。
7.2 契约文档的自动化同步
契约文档最容易出的问题是“文档和代码不同步”。我的解决方案是让契约文档从代码中自动生成,而不是手工维护。具体做法是在代码里用装饰器标注接口的契约信息,然后写一个脚本扫描所有装饰器,生成Markdown格式的契约文档。这样只要代码更新了,文档就自动更新,不存在不同步的问题。
@contract( input={"user_id": "string:32hex", "amount": "int:1-1000"}, output={"success": "bool", "new_balance": "int"}, errors=["INSUFFICIENT_BALANCE", "INVALID_AMOUNT"] ) def deduct_points(user_id, amount): ...这个装饰器不影响运行时逻辑,只是作为元数据存在。扫描脚本读取这些元数据,生成契约文档。CI流水线里加一步检查:如果代码中的契约和文档不一致,就阻断合并。
7.3 测试用例的“最小化”原则
AI写测试的时候倾向于“多多益善”,一个函数能写二十个测试用例。但测试用例不是越多越好,冗余的测试会增加维护成本,而且当契约变更时,需要修改的测试越多,出错的概率越大。
我的原则是每个契约点只保留一个“最小验证用例”。比如“参数校验”这个契约点,只需要一个测试用例验证非法参数会抛异常即可,不需要为每种非法参数都写一个测试。边界值测试保留两个(上界和下界),正常值测试保留一个即可。
按照这个原则,测试用例数量大概能减少一半,但覆盖率基本不变。因为很多测试用例实际上在验证相同的逻辑,只是输入不同而已。
7.4 定期做“纪律审计”
这套框架跑久了之后,容易出现“纪律松弛”的问题。比如有人为了赶进度,在CI配置里临时调低了覆盖率阈值,然后忘记调回来。或者AI在修复问题时,绕过了一些检查。
我的做法是每个月做一次“纪律审计”:检查CI配置是否被修改、检查是否有绕过检查的提交、检查契约文档和代码是否一致。审计结果在团队内公示,有问题就整改。
这个审计不需要很复杂,写一个脚本自动检查即可。关键是坚持做,一旦中断,纪律就会迅速瓦解。
8. 关于这套框架的一些个人体会
我在多个项目上跑过这套框架,有成功的也有失败的。最大的体会是:框架本身不难,难的是坚持。TDD、契约驱动、工程纪律,这些概念都不新鲜,难的是在项目进度紧张的时候依然坚持走完整流程,难的是在AI说“我已经写好了”的时候依然去跑一遍校验,难的是在所有人都说“这次特殊”的时候依然按规矩来。
但正是这种坚持,把“氛围编程”和“工程化AI编程”区分开了。前者是凭感觉写代码,写完就完;后者是有纪律地写代码,写完能维护。短期看前者快,长期看后者快。
另一个体会是:不要追求完美。我见过有人试图设计一套“完美”的技能框架,覆盖所有可能的场景,结果框架复杂到没人愿意用。好的框架应该是“够用就好”,先跑起来,再迭代。我现在的框架大概覆盖了80%的常见场景,剩下20%的特殊情况就人工处理,不强行纳入框架。
最后一个体会是:AI是工具,纪律是核心。Superpowers 技能框架、TDD 契约驱动、工程纪律闭环,这些手段的目的不是让AI变得更聪明,而是让AI的输出变得更可控。AI的能力在快速提升,但工程纪律的价值不会因为AI变强而降低——反而会因为AI生成代码的速度越来越快,而变得越来越重要。因为生成速度越快,没有纪律约束的代码雪崩来得也越快。