AI 编码前的规格包:先给边界,再让它写代码
给 AI 编码工具使用的规格包模板:先写清任务边界、允许修改的文件、验收标准、测试证据和评审规则,再开始生成代码。
规格包不是长文档,而是开工边界
AI 编码工具生成 diff 很快,但它并不天然知道“应该生成哪个 diff”。规格包就是在生成代码前交给工具的一页工作单:这次要改变什么行为,允许改哪些文件,不允许做什么,验收标准是什么,最后要交哪些证据。它不是完整设计文档,也不是一句随手写的 prompt,而是一个可以被评审的边界。
目的不是让 AI 变慢,而是阻止最贵的那种快:看起来很快地补了一个功能,同时改了无关 helper、加了新依赖、写了只证明自己实现的测试,最后让 reviewer 猜测哪些改动被授权。
什么时候适合使用
规格包适合边界已经相对清楚的任务:一个 bug fix、一个 endpoint 变更、一个窄 UI 行为、一段迁移逻辑,或者一个可以在单个 PR 里完成的功能。如果这件事还需要产品发现、架构取舍或安全审批,就不要直接交给 AI 写代码。先写更高层的规格,或者用 规格生成器 和 功能规格模板 把问题拆清楚。
判断标准很简单:AI 生成完以后,reviewer 能不能根据这份包判断它是否做对?如果答案还是“看情况”,那就还没到生成代码的时候。
最少要有六个字段
- 任务摘要:只写一个要改变的行为,不把多个需求揉在一起。
- 允许修改范围:明确哪些文件可以改,哪些文件只能读,哪些文件不能碰。
- 非目标:写清不做重构、不加依赖、不改公开字段、不顺手优化。
- 验收标准:用 Given/When/Then 写出可观察行为。
- 证据要求:测试、日志、截图、手工检查或迁移 dry-run。
- 合并规则:哪种情况必须退回,不能靠一句“已完成”通过。
可复制规格包
AI 编码规格包 任务: - 要实现的行为: - 必须保持不变的用户或系统行为: 允许修改范围: - AI 可以编辑的文件/目录: - AI 可以读取但不能修改的文件: - AI 不能修改的文件: 非目标: - 不做无关重构。 - 未经批准,不添加依赖。 - 不重命名公开字段、路由、事件名或错误码。 - 除非本任务包含生成器,否则不修改 generated files。 验收标准: - Given: When: Then: - Given: When: Then: 失败路径: - 无效输入: - 权限或归属失败: - 超时、重试或部分失败: - 回滚或恢复期望: 评审前必须提供的证据: - 需要新增或更新的测试: - 需要运行的现有测试: - 手工检查、截图、日志或指标: - PR 描述必须把每条验收标准映射到证据。 合并阻断: - 任何验收标准没有证据,PR 就还没准备好。
例子:只加重试,不顺手重构
假设支付 webhook 在 ledger 服务超时时需要重试一次。糟糕的提示词是“给 webhook handler 加重试逻辑”。这会让模型自由发挥:改 client、换错误处理、引入 retry library,甚至改签名校验。规格包应该窄很多。
任务: - 当 LedgerClient.recordPayment 超时时,webhook handler 重试一次。 允许修改范围: - src/payments/webhook-handler.ts - src/payments/webhook-handler.test.ts 非目标: - 不修改 LedgerClient 公开 API。 - 不添加 retry library。 - 不修改 webhook 签名校验。 验收标准: - Given LedgerClient 第一次超时 When handler 处理有效支付事件 Then 它重试一次并记录支付。 - Given LedgerClient 连续两次超时 When handler 处理事件 Then 返回现有的可重试失败响应。 - Given 签名校验失败 When handler 收到事件 Then 不调用 ledger。
这样 reviewer 就不用猜。diff 要么只改两个文件,要么越界;测试要么证明每条标准,要么不完整。AI 仍然可以帮忙,但它不能自己决定工作的形状。
文件所有权要写清楚
很多 AI 代码漂移都从文件范围开始。模型看到一个 helper,觉得它可以更干净,于是顺手改了。一个小任务就变成了软重构。规格包要把“可以读”和“可以写”分开写。比如“可以查看 API client,但不能修改它”;如果看起来必须改别的文件,就停止并请求批准。
这对多人协作也重要。明确写范围可以减少冲突,也让 PR 更容易回滚。你不需要在评审时从整个仓库里判断意图,只需要看生成结果有没有遵守那一页边界。
要证据,不要信心
AI 最后的回复如果只是“实现完成”,价值很低。更有用的是:“AC-1 由 webhook-handler.test.ts 里的 retries once on timeout 覆盖”。规格包应该要求它每次都给出这种映射。
UI 任务的证据可能是截图和视口列表;API 任务的证据可能是契约测试和错误响应 fixture;数据库任务的证据可能是迁移 dry-run 和回滚说明。证据形式会变,但规则不变:每条验收标准都不能只停留在口头声明。
和 Spec Skills 搭配使用
Spec Skills 适合放在这里:先把粗糙工单整理成规格包,再让编码工具在这个包内实现。如果生成过程中暴露出缺失决策,先更新规格包,再进入下一次实现,而不是让 AI 自己猜。
这也让 AI 编码治理 变得具体。治理不一定是厚重流程。它可以是一条重复习惯:每个 AI PR 都从规格包开始,每条验收标准都映射到证据,越界改动必须在评审前移除。
先审规格包,再审代码
PR 到来后,先打开规格包。看 diff 是否留在允许文件内;看每条验收标准是否有证据;看是否新增依赖、重命名公开字段或未经允许修改 generated file。确认这些之后,再读实现细节。
如果规格包写错了,就修规格包再生成。如果实现忽略了规格包,就退回 PR。不要默默替 AI 修掉所有问题,否则团队会学到一个坏习惯:不清楚的规格也能靠 reviewer 兜底。Spec-First 的好处,恰恰是把这个兜底动作前移。
把评审记录写成表,而不是评论串
我更喜欢在 PR 描述里放一张小表,而不是让 reviewer 去几十条评论里拼结论。表格不需要复杂,只要能回答三个问题:这条标准在哪里实现,哪里证明,哪里还有风险。这样做还有一个好处:如果同一类 AI PR 反复出问题,团队能回头看记录,而不是靠记忆复盘。
AI PR 评审记录 验收标准 | 实现文件 | 证据 | 评审结论 AC-1 超时后重试一次 | webhook-handler.ts | timeout retry test | 通过 AC-2 连续超时返回可重试失败 | webhook-handler.ts | double-timeout test | 通过 AC-3 签名失败不调用 ledger | webhook-handler.test.ts | invalid signature test | 通过 越界检查: - 未新增依赖 - 未修改 LedgerClient API - 未修改签名校验逻辑 剩余风险: - 真实 ledger 超时延迟未在本地复现,只通过 mock 验证 - 发布后观察 payment_webhook_retry_total 指标 24 小时
这张表不是为了增加仪式感,而是为了让审核变短。Reviewer 不必重新理解整份 diff,只需要抽查映射是否真实存在。如果表里写了某个测试,测试不存在,或者测试并没有覆盖那条标准,PR 就退回。
团队可以从一条规则开始
不用一开始就要求所有 AI 生成代码都走完整流程。更现实的做法是先选高风险区域:支付、权限、数据迁移、API 契约、后台高危操作。只要这些区域的 AI PR 必须附规格包,收益就很明显。等团队熟悉后,再扩展到普通功能。
如果你担心阻力,可以把规则写得更具体:凡是 AI 生成代码修改了生产路径、数据库结构、公开 API 或权限判断,就必须附规格包;只改测试文案或内部注释,可以走轻量流程。规则越具体,团队越容易接受。
我会退回的三种规格包
规格包不等于把工单复制一遍。下面三种我会直接退回,因为它们看起来有格式,实际上没有约束住 AI。
- 范围太大。“优化 checkout flow”不够。要写到具体文件、具体行为、不能碰哪些边界。
- 验收标准只有成功路径。AI 很擅长写 happy path。失败、权限、重复请求、超时这些才决定代码能不能上线。
- 证据写成口号。“确保测试通过”没有用。要写哪一个测试、哪一个截图、哪一个日志或哪一个指标。
退回不是为了刁难。规格包越模糊,AI 越会自己补空白;它补得越多,reviewer 越难判断哪些改动是任务要求,哪些只是模型顺手做了。
一页检查清单
- 任务能否用一句话说明。
- 允许写入范围是否小于整个仓库。
- 非目标是否明确禁止重构和新增依赖。
- 验收标准是否覆盖成功路径、失败路径和权限边界。
- 证据要求是否写了具体测试或检查,而不是只写“跑 CI”。
- PR 描述是否把标准、文件和证据映射起来。
专题阅读路径
这篇文章归入 验收标准 主题。先读 Hub,再结合下面的清单、模板或工具落到具体项目里。
继续阅读
编辑说明与免责声明
发布于 2026-05-03。本文用于软件工程教学与实践参考,帮助团队把 AI 编码放进可评审的 Spec-First 工作流。
- 作者信息:Spec Coding 编辑部
- 编辑政策:编辑与事实核查政策
- 联系方式:联系页面