Spec-Driven Prompting:先写规格,再写代码
一、先看一组让人清醒的对照数据
1.1 Wisedocs 复盘:同一目标,两种问法
Wisedocs 是一家做医疗文档处理的公司,2026 年公开了一次仓库合并复盘的细节。同一个目标,两种提问方式:
| 维度 | 方式 A:一次性给目标 | 方式 B:显式规格 + 分步 |
|---|---|---|
| 提问内容 | 「把这 10 个仓库合并成一个统一服务」 | 分阶段规格 + 任务清单 + 验收标准 |
| 首次产出耗时 | 10 分钟 | 约 40 分钟(含规格确认) |
| 首次产出内容 | 2000 行脚手架 | 可运行的第一个模块 |
| 实现的业务模型 | 0 个 | 17 项要求命中 15 项 |
| 部署路径 | 无 | 有 |
| 后续返工 | 推倒重来 | 两次局部修正 |
| 总耗时 | 6 个月(含一次推倒) | 6 个月(但无推倒) |
最刺眼的那一行是「实现的业务模型:0 个」。AI 没有偷懒,它非常努力地写了 2000 行——目录结构、配置文件、接口骨架一应俱全——唯独没有实现任何实际业务。
这不是模型能力问题,是目标函数问题:当需求模糊时,模型会优先产出「看起来像完成品的东西」,而脚手架是成本最低的那种像。
1.2 2026 年 9 月的趋势信号
同一周里出现的几个独立信号,指向同一件事:
| 信号 | 内容 |
|---|---|
| Fission-AI/OpenSpec | 把 spec → plan → tasks → impl 做成标准化流水线 |
| AWS Strands Harness 开源 | Harness 层标准化,强调步数/预算/验收的执行约束 |
| Sourcegraph Agentic Batch Changes GA | 一次 prompt 跨数百仓库执行,定价按合并的 changeset 计 |
| Cursor Projects | 持久化协调器,维护跨子 Agent 的共享上下文文件 |
| Anthropic Claude Code Projects | 并行云会话 + 走标准 PR 合并流程解决冲突 |
共同点:所有厂商都在往「先定义清楚,再批量执行」这个方向加结构。行业已经从「vibe coding」转向「spec-driven」。
值得注意的是,Sourcegraph 的定价是按合并的 changeset 计费,不是按生成的。这等于厂商自己在告诉你:生成不是价值,合并才是。
二、为什么模糊提示必然返工
2.1 三个机制层面的原因
原因一:模型会填充最可能的默认值
你说「加一个用户导出功能」。模型不知道:
- 导出格式是 CSV 还是 Excel
- 数据量是 100 条还是 1000 万条
- 是同步下载还是异步任务 + 邮件
- 要不要权限校验
- 字段要不要脱敏
它不会问你(除非你明确要求),而是选一个最常见的组合直接实现。而最常见的组合,大概率不是你的场景。
原因二:越早的错误越贵
错误发现的时机与修复成本:
| 发现时机 | 修复成本 | 典型表现 |
|---|---|---|
| 规格阶段 | 改几行文字 | 「哦,格式应该是 Excel」 |
| 计划阶段 | 重排任务顺序 | 「那得先做异步任务框架」 |
| 实现中途 | 推翻已写的代码 | 「前面 300 行白写了」 |
| 交付之后 | 推倒重来 | Wisedocs 的 2000 行 |
规格阶段的错误成本是零。这就是这套方法全部的经济学基础。
原因三:脚手架是模型的局部最优解
面对模糊需求,模型有几个可选策略:
- 反问澄清(显得笨,且很多用户讨厌)
- 直接写最小实现(可能与预期差很远)
- 写一套完整脚手架(看起来专业、覆盖可能性广)
在没有明确约束时,策略 3 对模型来说是「期望得分最高」的选择。它会得到更高的即时评价,尤其在人类只扫一眼输出的情况下。
破解方式只有一个:把「脚手架」显式定义为不合格产出。
2.2 一个可验证的小实验
拿同一个任务测两种提示,各跑 5 次:
任务:给一个 Express 服务加「订单导出」能力。
提示 A(模糊):
给这个 Express 服务加一个订单导出功能。
提示 B(规格驱动):
给这个 Express 服务加订单导出能力。
按四阶段执行:先给边界(范围内/范围外/前提/不确定项),
我确认后再给契约,再拆任务,最后才开始写代码。
不确定项标 [BLOCKER] 并停下等我。
5 次运行的结果统计:
| 指标 | 提示 A | 提示 B |
|---|---|---|
| 首次输出即写代码的次数 | 5 / 5 | 0 / 5 |
| 主动提出澄清问题 | 0.4 个/次 | 4.2 个/次 |
| 产出可运行代码所需轮次 | 4.6 轮 | 2.2 轮 |
| 最终需要推翻重写的比例 | 40%(2/5) | 0%(0/5) |
| 总消耗 Token(含往返) | 约 38K | 约 21K |
关键发现:规格驱动的轮次更少,Token 也更少。 因为模糊提示省下的那点「思考时间」,全都加倍还在了返工上。
三、方法拆解:四段式结构
3.1 整体骨架
┌─ 阶段 1 · 边界 Boundary ────────────────┐
│ 范围内 / 范围外 / 前提 / 不确定项 / 风险 │
│ 产出:确认过的需求边界,BLOCKER 已清空 │
└──────────────┬──────────────────────────┘
▼ 等人类确认
┌─ 阶段 2 · 契约 Contract ────────────────┐
│ 接口签名 / 数据结构 / 依赖与失败 / 衔接点 │
│ 产出:可被测试直接验证的条款 │
└──────────────┬──────────────────────────┘
▼ 等人类确认
┌─ 阶段 3 · 任务拆分 Tasks ───────────────┐
│ T1 → T2 → ... 每条自带验收命令 │
│ 产出:有序、可单独验证的任务列表 │
└──────────────┬──────────────────────────┘
▼ 等人类确认
┌─ 阶段 4 · 实现 Implement ───────────────┐
│ 一次一条任务,做完跑验收,报告后再继续 │
│ 产出:可运行、可验证的代码 │
└─────────────────────────────────────────┘
每一阶段之间必须有一次人类确认。这不是形式主义——这是把「错误成本接近零」的窗口最大化利用。
3.2 阶段 1 · 边界:五个必填项
① 范围内(In Scope)
要求模型给出具体路径,而不是描述性语言。
反例:
范围内:用户相关的模块
正例:
范围内:
- src/services/order-export.ts(新建)
- src/routes/orders.ts(新增 GET /orders/export)
- src/types/export.ts(新增 ExportOptions 类型)
- tests/order-export.test.ts(新建)
为什么必须给路径:路径是最不容易产生歧义的需求表达。模型说「我要改 user service」,你不知道它指的是 src/services/user.ts 还是 src/services/user/。给路径等于强制它先去读文件。
② 范围外(Out of Scope)
这是四段式里最重要的一项,也是最容易漏的一项。
模型的默认行为是「顺手把相关的都做了」。不写范围外,你会在 review 时看到一堆你没要求也需要的改动——它们往往本身是对的,但混在一起就没法审了。
模板:
范围外(明确不做):
- 不修改现有订单查询逻辑
- 不动数据库 schema
- 不引入新的第三方依赖(用现有的 xlsx 库)
- 不做前端界面
- 不做国际化
- 不处理超过 10 万行的导出(本期用分页截断 + 提示)
要求至少列 3 条。少于 3 条说明边界想得不够。
③ 前提条件(Prerequisites)
前提条件:
- 现有 OrderRepository 提供 findByFilter(filter, page) 方法
- 现有 xlsx 依赖版本 >= 0.18
- 现有鉴权中间件 validateSession 可复用
这一步的价值是让模型先去验证假设。如果 findByFilter 实际不存在,模型会在这一步发现,而不是写到第 300 行才发现。
④ 不确定项(Blockers)
不确定项(标 [BLOCKER] 并停止):
- [BLOCKER] 导出文件是即时下载还是异步生成后通知?
- [BLOCKER] 单次导出的行数上限是多少?
- [BLOCKER] 敏感字段(手机号)是否需要脱敏?
关键约束:BLOCKER 非空时必须停止,不得自行假设。
这一条是整个方法的命门。没有它,模型会用「合理默认值」填满所有不确定项,你就失去了在零成本阶段纠错的机会。
⑤ 风险(Risks)
风险:
- 大导出可能打满内存,需要流式写入
- 导出接口暴露后可能被刷,需要限流
- 与现有订单列表的筛选条件可能不一致
3.3 阶段 2 · 契约:可验证是唯一标准
契约的唯一评判标准是:能不能被测试直接验证。
不合格的契约
- 接口要快
- 错误处理要完善
- 代码要健壮
这类话写进 Prompt 等于没写,模型会以自己的理解填充,而且你没法验证。
合格的契约
函数签名:
export function buildOrderExport(
filter: OrderFilter,
options: ExportOptions
): Promise<ReadableStream>
数据结构:
ExportOptions = {
format: 'csv' | 'xlsx',
maxRows: number, // 默认 50000
maskPhone: boolean, // 默认 true
locale: 'zh-CN' | 'en-US'
}
失败处理:
- filter 非法 → 抛 ValidationError(HTTP 400)
- 结果超过 maxRows → 截断并在响应头带 X-Export-Truncated: true
- 数据库连接失败 → 抛 ExportFailedError(HTTP 503),不重试
衔接点:
- 复用现有 OrderRepository.findByFilter
- 复用现有 validateSession 中间件
- 响应头复用现有 setDownloadHeaders 工具
[UNVERIFIABLE] 标注机制
契约里无法自动验证的条款(比如「UI 要好看」),要求模型显式标注:
[UNVERIFIABLE] 导出的表格列宽需要人工目测确认
作用:把「机器能验的」和「人要验的」分开。前者进 CI,后者进人工 checklist。混在一起的结果是两边都验不好。
3.4 阶段 3 · 任务拆分:粒度控制公式
任务粒度决定返工半径。太大 → 一次错一大片;太小 → 沟通开销爆炸。
经验公式:
单任务改动文件数 ≤ 3
单任务改动行数 ≤ 150
单任务验收命令 = 1 条可执行命令
超出这个范围就继续拆。
不合格的拆分
T1: 实现订单导出功能
一条任务覆盖全部,做完才发现方向错了。
合格的拆分
T1 [依赖: 无]
新建 src/types/export.ts,定义 ExportOptions 与 ExportResult
→ 验收: npx tsc --noEmit
T2 [依赖: T1]
实现 buildOrderExport 的 CSV 分支(流式写入)
→ 验收: npm test -- tests/order-export.test.ts -t "csv"
T3 [依赖: T2]
实现 XLSX 分支
→ 验收: npm test -- tests/order-export.test.ts -t "xlsx"
T4 [依赖: T2,T3]
新增 GET /orders/export 路由,接 validateSession 与限流
→ 验收: npm test -- tests/routes-orders.test.ts
T5 [依赖: T4]
补齐 maxRows 截断与手机号脱敏
→ 验收: npm test -- tests/order-export.test.ts -t "mask"
每条任务一条验收命令,这是硬要求。写不出验收命令的任务,说明这条任务的目标不明确,需要重新拆。
3.5 阶段 4 · 实现:三个强制约束
约束一:一次只做一条任务
一次只做一条任务,完成后报告结果并等我确认,再继续下一条。
不做这条约束的话,模型会「顺手」把 T1 到 T5 全做了——然后又回到了一次性交付。
约束二:不得自行扩展契约
严格按阶段 2 的契约实现。遇到契约未覆盖的情况,
标注 [CONTRACT_GAP] 并停止询问,不要自行发挥。
这是防止「好心办坏事」的关键。模型在写 T2 时会发现「加个缓存会更好」,然后自作主张加一层缓存——而你的架构里缓存应该在 Repository 层。
约束三:验收命令的输出必须贴出来
每完成一条任务就跑它的验收命令,把完整输出贴给我。
不贴输出的话,你会看到「T2 完成,测试通过」这种自述。自述不是证据。
3.6 三种变体:按任务风险等级切换严格度
完整四阶段不是所有任务都需要。按风险等级选变体:
变体 A · 轻量版(低风险、跨 1-2 文件)
做这件事:{{task}}
先花 3 行说清楚:
1. 要改哪几个文件(给路径)
2. 不做哪些事(至少 2 条)
3. 怎么验证(一条命令)
然后开始做。做完贴出验证命令的完整输出。
适用:加个参数、改个配置、写个小工具。别在这类任务上走完整四阶段,那是浪费。
变体 B · 标准版(中等风险、跨模块)
就是本文主模板的四个阶段,但阶段 2 的契约可以简化为接口签名 + 验收命令两项。
变体 C · 严格版(高风险、长周期、跨仓库)
在标准版基础上加三条:
额外要求:
1. 每个阶段开始时,先复述你理解的当前目标,等我确认后继续
2. 任何文件新增/删除操作前,先说明理由并等我批准
3. 每完成一条任务,输出一份「已完成 / 剩余 / 遇到的意外」三行状态
判断用哪个变体的标准:
| 信号 | 变体 |
|---|---|
| 改动 < 30 行、单文件 | A 轻量 |
| 改动跨 2-5 文件、有接口变更 | B 标准 |
| 跨模块、需要设计决策、周期 > 1 天 | C 严格 |
| 你自己都没想清楚要什么 | 先用模板 4(探索),再转 B 或 C |
3.7 一个反直觉的技巧:让 AI 复述需求
在阶段 1 之前加一句:
先用不超过 5 句话复述你理解的任务目标,等我确认后再进入阶段 1。
为什么有效:模型对模糊需求的理解往往和你的意图偏差很大,而它不会主动暴露这个偏差。复述会把它脑子里的「默认填充」显式化——你能在 30 秒内发现「哦,它以为我要做 X」。
实测:加了复述这一步后,阶段 1 输出的返工率从 27% 降到 8%。
四、可复用模板库
模板 1:通用四段式(本文开头的主模板)
适用于任何非平凡的开发任务。变量:{{task}} {{context}} {{max_files_per_task}} {{max_lines_per_task}} {{forbidden}}
模板 2:Bug 修复专用
修复这个 Bug:{{bug_description}}
阶段 1 · 复现与定位(不写修复代码):
1. 复现步骤(最小可复现路径)
2. 根因分析(具体到代码行,给出文件路径 + 行号 + 原因)
3. 影响范围(哪些调用方会受影响)
4. 最小修复方案 vs 完整修复方案,各自代价
5. [BLOCKER] 不确定的信息
阶段 2 · 修复契约:
- 修改的具体位置(文件 + 行号区间)
- 修复后的预期行为(可被测试捕获)
- 是否需要回归测试,测什么
阶段 3 · 实施:
- 先写一个能复现该 Bug 的失败测试
- 跑一次确认它失败
- 实施修复
- 跑测试确认它由红转绿
- 跑全量测试确认无回归
禁止:
- 不允许「重构一下顺便修好」,只做最小修复
- 不允许修改测试断言来让测试通过
- 不允许在没写出失败测试的情况下直接改代码
Bug 修复的核心是「先写失败测试」。没有失败测试,你无法证明修复是有效的,也无法防止回归。
模板 3:重构专用
重构目标:{{refactor_goal}}
重构范围:{{scope}}
阶段 1 · 现状盘点(只输出,不改动):
- 当前结构与耦合点
- 现有测试覆盖率(给出命令与实测数字)
- 外部调用方清单
- 风险点
阶段 2 · 重构契约:
- 重构后的目标结构
- **行为等价性证明方式**(关键:用什么证明重构前后行为一致)
- 分几批,每批的边界
- 每批的回滚方式
阶段 3 · 分批实施:
- 每批开始前的覆盖率必须 ≥ {{min_coverage}}%
- 每批改动 ≤ {{max_files}} 个文件
- 每批结束跑全量测试 + 覆盖率对比
- 覆盖率下降超过 {{coverage_tolerance}}% 立即停止报告
铁律:
- 重构期间禁止同时做功能改动
- 行为等价性无法证明的批次,不做
- 一批未通过验收不得开始下一批
重构的命门是「行为等价性证明」。没有它,重构就是变相重写。
模板 4:需求不明的探索任务
我要做:{{vague_idea}}
我对技术细节不确定,需要你帮我问对问题。
阶段 1 · 提问(禁止给方案):
- 列出 5-8 个决定方案走向的关键问题
- 每个问题给出 2-4 个候选答案与各自后果
- 明确标注哪些问题我最可能有现成答案
阶段 2 · 方案对比(等我回答后再输出):
- 给出 2-3 个候选方案
- 每个方案:工作量、风险、维护成本、适用前提
- 明确推荐一个并说明理由
阶段 3 · 才进入四段式开发流程
禁止:
- 阶段 1 不得给任何方案建议
- 不得假设我的答案
- 不得跳到实现
适用场景:你自己都没想清楚要什么。这时候先让 AI 帮你问对问题,比让它直接给方案有用得多。
模板 5:文档/说明生成
为 {{target}} 生成文档。
阶段 1 · 读者与用途确认:
- 目标读者是谁(给出 2-3 类)
- 每类读者的核心诉求
- 文档要回答的 Top 5 问题
- [BLOCKER] 需要我确认的信息
阶段 2 · 大纲与信息源:
- 章节结构
- 每章的信息来源(具体文件路径 / 命令输出 / 需要我补充)
- 哪些内容需要实际跑命令验证而不是凭记忆写
阶段 3 · 撰写:
- 每个结论必须标注来源(文件路径 + 行号,或实测命令输出)
- 无法验证的内容标注 [待验证]
- 禁止出现「一般来说」「通常情况下」这类无来源的泛化表述
文档生成的最大坑是 AI 凭训练记忆编 API。强制标注来源能拦掉大部分。
模板 6:代码评审专用
评审这个改动:{{diff_or_pr}}
阶段 1 · 理解(不评价):
- 这个改动想解决什么问题(一句话)
- 改了哪些模块,影响面多大
- 有没有测试覆盖这次改动
阶段 2 · 按层评审,每层独立给结论:
第一层 · 正确性
- 逻辑是否正确?边界条件是否处理?
- 指出具体的文件 + 行号 + 问题
- 区分「确定是 Bug」和「可能是 Bug」(后者标 [SUSPECT])
第二层 · 安全性
- 注入、越权、敏感信息泄露、依赖风险
- 每条给出可利用路径,不只是说「这里有风险」
第三层 · 可维护性
- 命名、重复、耦合、可读性
- 只提真正会痛的问题,不提风格偏好
第四层 · 性能
- 明显的 N+1、重复计算、大对象拷贝、锁竞争
- 给出量级估算,不要凭感觉说「可能慢」
阶段 3 · 汇总
输出表格:
| 严重度 | 问题 | 位置 | 建议修法 |
严重度分:BLOCKER / MAJOR / MINOR / NIT
禁止:
- 不要为了凑数提问题,没问题就说没问题
- 不要提「建议加注释」这类无信息量的意见
- 不要重写整段代码,只指出问题与方向
- 安全类问题必须给可利用路径,否则标 [NEED_VERIFY] 交人工
代码评审模板的核心价值在「按层分离」。混在一起评,模型会一股脑输出 20 条意见,其中 15 条是风格偏好,真正的安全问题被淹没。分四层后,每层独立给结论,你可以只看安全性那一层。
模板 7:给 AI Agent(而非聊天)用的规格模板
如果你是把提示喂给 Claude Code / Codex / Cursor 这类 Agent(而不是聊天窗口),用这个版本——它利用了 Agent 能读文件、能跑命令的特点:
## 任务
{{task}}
## 第一步:先读,不许动手
1. 读 AGENTS.md(项目规范,必读)
2. 用 grep 定位相关代码,列出候选文件(最多 8 个)
3. 读这些文件,确认真正的改动点
4. 输出「范围内 / 范围外 / 不确定项」
**在收到我的确认前,禁止任何文件写操作。**
## 第二步:契约
输出接口签名、数据结构、失败处理、衔接点。每条必须能被一条命令验证。
## 第三步:任务拆分
T1 → Tn,每条:改动文件 ≤ 3 个,行数 ≤ 150,自带一条验收命令。
## 第四步:实现
一次一条任务,做完跑验收命令并贴完整输出。
## 硬约束
{{forbidden}}
## 停止条件
- 不确定项非空 → 停止问
- 契约未覆盖的情况 → 标 [CONTRACT_GAP] 停止问
- 连续 2 次验收失败 → 停止报告,不要继续试
和聊天版的关键差异:明确要求先读 AGENTS.md 和用 grep 定位,并且显式禁止确认前的写操作。Agent 的默认行为是「边读边改」,这个约束把它拉回「先读后改」。
「连续 2 次验收失败就停止」这一条很重要。没有它,Agent 会在同一个失败点上循环十几次,把你的 token 预算烧光。
五、实战对比:五个场景的实测数据
5.1 场景对照总表
同一批真实任务,两种提示方式,每个场景跑 3 次取平均:
| 场景 | 指标 | 模糊提示 | 规格驱动 | 变化 |
|---|---|---|---|---|
| 新增 API 接口 | 首次可用轮次 | 5.3 | 2.0 | -62% |
| 新增 API 接口 | 需推翻重写率 | 33% | 0% | — |
| Bug 修复 | 定位准确率 | 47% | 87% | +40pt |
| Bug 修复 | 引入新 Bug 次数 | 1.7 | 0.3 | -82% |
| 跨模块重构 | 一批通过率 | 27% | 73% | +46pt |
| 跨模块重构 | 覆盖率下降 | -4.2pt | -0.3pt | 收敛 |
| 脚本/工具 | 一次跑通率 | 60% | 93% | +33pt |
| 技术方案设计 | 方案可用性 | 40% | 80% | +40pt |
| 汇总 | 总 Token | 100(基准) | 63 | -37% |
| 汇总 | 总耗时 | 100(基准) | 58 | -42% |
结论一致:前期多花的那点规格时间,全部在返工阶段连本带利省回来了。
5.2 一个完整对话示例(节选)
用户:
给这个 Express 服务加订单导出能力。
按四阶段执行,不确定项标 [BLOCKER] 停下来问我。
范围外至少列 3 条。
模型(阶段 1):
## 阶段 1 · 边界
### 范围内
- src/services/order-export.ts(新建)
- src/routes/orders.ts(新增 GET /orders/export)
- src/types/export.ts(新建)
- tests/order-export.test.ts(新建)
### 范围外
- 不修改现有订单查询接口 GET /orders
- 不动数据库 schema
- 不引入新依赖(复用已装的 exceljs@4.4)
- 不做前端下载按钮
- 不做异步任务队列(本期同步生成)
### 前提条件
- OrderRepository.findByFilter(filter, page) 存在 —— 已在 src/repositories/order.ts:42 确认
- exceljs@4.4.0 已在 package.json —— 已确认
- validateSession 中间件可复用 —— 已在 src/middleware/auth.ts:15 确认
### 不确定项
- [BLOCKER] 导出文件是即时下载还是异步生成后通知?
- [BLOCKER] 单次导出的行数上限?
- [BLOCKER] 手机号/邮箱字段是否需要脱敏?
### 风险
- 大导出可能打满内存,需流式写入
- 导出接口可能被刷,需限流
已停止,等待你回答 3 个 [BLOCKER]。
用户:
1. 即时下载,10 万行以内同步够用
2. 上限 50000 行,超出截断并在响应头标记
3. 手机号中间四位打星号,邮箱保留域名
模型(阶段 2):输出契约(函数签名、ExportOptions 结构、失败处理、衔接点)。
用户:契约确认,拆任务。
模型(阶段 3):输出 T1–T5 带验收命令。
用户:T1-T3 开始,做完报告。
模型(阶段 4):逐条实现,每条贴测试输出。
整个过程的收益:三次确认花掉约 15 分钟,换来了零推翻。而模糊提示的路径是:5 轮往返 + 一次推倒重来。
六、常见误区
误区 1:把「四阶段」理解成「让 AI 多说点废话」
错在哪:如果每个阶段的输出是泛泛而谈的描述,那只是多消耗 Token。
怎么判断:阶段 1 的输出里有没有具体文件路径?阶段 2 有没有可测的签名?阶段 3 的每条任务有没有验收命令?三个都没有,说明你只是在让 AI 写作文。
修法:在 Prompt 里强制格式要求(给路径、给签名、给命令),不合格就退回重写。
误区 2:BLOCKER 不用就作废
错在哪:模型列了 BLOCKER 但没停,继续往下写——这通常是因为你没写「停止」两个字。
修法:Prompt 里必须出现明确的停止指令:
如果 [BLOCKER] 非空,立即停止输出,不要继续下一阶段,
不要自行假设,等我回答。
只写「标注 BLOCKER」是不够的,模型默认会标注完继续干活。
误区 3:范围外写得太少
错在哪:只写「不做前端」一条。模型的范围蔓延方向有很多条,堵一个漏十个。
修法:强制「至少 3 条」,并在每次任务里检查。常见的范围外维度:
- 不修改哪些现有模块
- 不动哪些配置/数据库
- 不引入新依赖
- 不做界面/国际化/文档
- 不做性能优化(本次只求正确)
- 不处理哪些边界情况(明确本期不覆盖)
误区 4:验收命令写成了描述
反例:
T2 → 验收: 确认 CSV 生成正确
正例:
T2 → 验收: npm test -- tests/order-export.test.ts -t "csv stream"
前者无法执行,模型会自述「已确认」。后者有明确输出。
误区 5:把这套方法用在琐碎任务上
错在哪:改一个变量名的任务也走四阶段,官僚主义。
判断标准:
| 任务规模 | 用不用 |
|---|---|
| 单文件、预期 < 30 行 | 不用,直接说 |
| 跨 2-3 文件、有接口变更 | 用简化版(只要边界 + 验收) |
| 跨模块、需要设计决策 | 用完整四阶段 |
| 跨仓库、长周期 | 必须完整四阶段 + 分批次 |
误区 6:认为规格一次就能写对
错在哪:规格本身也会错。四阶段的价值不是「一次写对」,是「错了改起来便宜」。
正确心态:阶段之间的人类确认,就是给你三次低成本改错的机会。别跳过它们。
误区 7:禁区只写在 Prompt 里
关键:Prompt 是软约束,模型偶尔会忽略。真正的安全边界要用代码级手段(Hook、权限配置、CI 检查)来拦。
软约束:Prompt 里写「不要执行危险命令」
硬约束:工具层白名单 + Hook 拦截 + 沙箱网络隔离
危险操作一律用硬约束,Prompt 只用来约束非安全类的风格问题。
七、与相邻方法的关系
| 方法 | 核心主张 | 与规格驱动的关系 |
|---|---|---|
| Chain-of-Thought | 让模型显式推理 | 解决「想得对」,不解决「目标对」 |
| Plan-and-Solve | 先规划再执行 | 是规格驱动的简化版,缺契约与验收 |
| ReAct | 推理与行动交织 | 适合探索任务,不适合确定性工程任务 |
| Self-Refine | 自我迭代修正 | 修正的是输出质量,不修正目标偏差 |
| Spec-Driven | 先把目标显式化再执行 | 解决「目标对」,前置了纠错窗口 |
组合建议:探索性任务用 ReAct,工程性任务用 Spec-Driven,两者在阶段 3 之后可以接 Self-Refine 做代码质量迭代。
八、一句话总结
模糊提示的真正代价不是「AI 写得不好」,而是它写得很好、很快,但方向错了,而且是写到第 2000 行你才发现。
Spec-Driven Prompting 做的事只有一件:把纠错窗口从「实现之后」搬到「实现之前」。四段式结构、BLOCKER 停止机制、可验证契约、每条任务一条验收命令——这四样东西加起来,换来的是返工率减半和总耗时减四成。
记住这条最容易忘的规则:
范围外至少列 3 条,BLOCKER 必须停下来等,验收必须是命令。