将每一个非平凡决策置于全新上下文的对抗性审查之下,然后再让其成立。当正确性比速度更重要时、在不熟悉的代码中工作时、风险较高时(生产环境、安全敏感逻辑、不可逆操作),或者任何一次自信的输出现在验证比以后调试更便宜时使用。
怀疑驱动开发
概述
自信的答案不等于正确的答案。长时间的会话会积累上下文,悄悄地将假设变成“事实”,而没有人注意到。怀疑驱动开发是一种纪律,即在任何非平凡输出成立之前,实例化一个全新上下文的审查者——偏向于证伪而非认可。
这不是 /review。/review 是对已完成工件的裁决。这是一种进行中的姿态:非平凡决策在修正成本仍然低廉时接受交叉审查。
何时使用
当至少满足以下条件之一时,决策是非平凡的:
- 引入或修改分支逻辑
- 跨越模块或服务边界
- 断言类型系统或编译器无法验证的属性(线程安全、幂等性、顺序、不变量)
- 其正确性依赖于未来读者无法看到的上下文
- 其影响范围不可逆(生产部署、数据迁移、公共 API 变更)
在以下情况下应用此技能:
- 即将在不确定的情况下做出架构决策
- 即将提交非平凡代码
- 即将声称一个非显而易见的事实(“这是安全的”、“这能扩展”、“这符合规范”)
- 在不完全理解的代码中工作
何时不使用:
- 机械操作(重命名、格式化、文件移动)
- 遵循清晰、明确的用户指令
- 阅读或总结现有代码
- 一行变更且正确性显而易见
- 纯工具操作(运行测试、列出文件)
- 用户明确要求速度优先于验证
如果你怀疑每一次击键,你将无法交付任何东西。该技能仅适用于上述定义的非平凡决策。
加载约束
此技能专为主会话编排器设计,其中步骤 3(怀疑,详见下文)可以生成一个全新上下文的审查者。
- 不要将此技能添加到角色的
skills:前置元数据中。 遵循步骤 3 的角色会生成另一个角色——这是references/orchestration-patterns.md明确禁止的编排反模式(“角色不能调用其他角色”)。 - 如果你发现自己从子代理上下文中应用此技能(Claude Code 禁止嵌套子代理生成):首选路径是向用户表明怀疑驱动无法嵌套运行,让主会话处理。作为最后手段,存在一个降级的自我质疑回退方案——将 ARTIFACT + CONTRACT 重写为一个全新的自我提示,并与之前的推理建立硬性心理分隔,然后执行步骤 1-5。这不是全新上下文审查(你携带自己的上下文),因此将结果标记为降级,并在用户可联系时优先升级。
流程
应用此技能时复制此检查清单:
怀疑循环:
- [ ] 步骤 1:声明——写下声明及其重要性
- [ ] 步骤 2:提取——隔离工件和契约,剥离推理
- [ ] 步骤 3:怀疑——使用对抗性提示调用全新上下文审查者
- [ ] 步骤 4:调和——对照工件文本对每个发现进行分类
- [ ] 步骤 5:停止——满足停止条件(琐碎发现、3 个循环或用户覆盖)
步骤 1:声明——明确什么在成立
用两到三行命名决策:
声明:“新的缓存层在规范描述的读密集型工作负载下是线程安全的。”
重要性:竞争条件会破坏用户数据,且在 QA 中难以检测。
如果你无法如此简洁地写出声明,那么你有的是一种感觉,而不是一个决策。在审视之前先明确它。
步骤 2:提取——最小的可审查单元
全新上下文的审查者需要工件和契约,而不是过程。
- 代码:差异或函数——而不是整个文件
- 决策:3-5 句话的提案加上必须满足的约束
- 断言:声明加上据称支持它的证据(与步骤 1 的声明块保持区分,后者是编排器正在审视的假设)
剥离你的推理。如果你传递结论,你将得到对你结论的验证。单元必须足够小,以便审查者一次阅读就能记住——如果是 500 行的 PR,先分解。
步骤 3:怀疑——调用全新上下文审查者
审查者的提示必须是对抗性的。框架决定了答案。
对抗性审查。找出这个工件有什么问题。
假设作者过于自信。寻找:
- 未声明的假设
- 未处理的边界情况
- 隐藏的耦合或共享状态
- 契约可能被违反的方式
- 可能破坏的现有约定
- 意外输入下的失败模式
不要验证。不要总结。找出问题,或者明确说明经过彻底检查后无法找到任何问题。
工件:<粘贴工件>
契约:<粘贴契约>
仅传递工件 + 契约。不要传递声明。 将你的结论交给审查者会使其偏向于同意。审查者必须独立确定工件是否满足契约。
在 Claude Code 中,agents/ 中基于角色的审查者默认具有隔离的上下文,并且在此处可用——参见 agents/ 获取列表和按领域匹配。
上述对抗性提示优先于角色的默认响应形状。 像 code-reviewer 这样的角色被编写为产生平衡的裁决,同时包含优点和缺点;怀疑驱动需要仅输出问题。将对抗性提示逐字粘贴到调用中,以覆盖角色的默认值。如果角色的响应形状无法干净地覆盖,则回退到带有对抗性提示的通用子代理。
跨模型升级
单一模型审查者与原始作者共享盲点——更冷、不同架构的模型可以捕捉到它们。怀疑驱动已经是对非平凡决策的选择加入,因此在该范围内提供跨模型是技能价值的一部分,而不是可选的摩擦。
交互式会话:始终提供。不要默默跳过。
步骤 1:询问用户
在上述步骤 3 的单一模型审查之后,但在调和之前,暂停并询问:
“单一模型审查完成。需要跨模型的第二意见吗?选项:Gemini CLI、Codex CLI、手动外部审查(你粘贴到别处)或跳过。”
此问题在每个交互式怀疑循环中都是强制性的——即使对于感觉风险较低的工件也是如此。用户——而不是代理——决定成本是否值得。代理的工作是呈现选择。
步骤 2:如果用户选择 CLI——验证,然后调用
- 检查工具是否在 PATH 中(
which gemini、which codex)。 - 测试其工作(
gemini --version或等效命令),然后再传递完整提示——过时或损坏的二进制文件可能通过which检查,但在实际输入上失败。 - 与用户确认确切的调用,包括必需的标志、认证和环境变量(例如,API 密钥)。实现各不相同;切勿假设。
- 仅传递工件 + 契约 + 对抗性提示。没有会话上下文,没有声明。
- 注意 shell 转义。如果工件包含引号、
$(...)或反引号,优先使用标准输入(echo … | gemini)或 heredoc,而不是内联的-p "…"。如有疑问,在运行之前请用户确认调用。 - 将输出带入步骤 4(调和)。
切勿将工件插值到 shell 引用的参数中。 代码、Markdown 和审查提示通常包含反引号、$(...) 和引号字符,这些字符会截断提示或执行嵌入的 shell。将完整提示写入文件并通过标准输入管道传递。
示例形状(根据你安装的工具验证标志——不同实现和版本的语法不同):
# 首先将对抗性提示 + 工件 + 契约写入临时文件。
# 然后通过标准输入管道传递,使工件中的 shell 元字符保持惰性。
# Codex(只读沙箱防止 CLI 写入你的工作区):
codex exec --sandbox read-only -C <repo-path> - < /tmp/doubt-prompt.md
# Gemini('--approval-mode plan' 是只读的;'-p ""' 触发非交互模式,提示从标准输入读取):
gemini --approval-mode plan -p "" < /tmp/doubt-prompt.md
只读沙箱是关键细节:怀疑工件本身可能包含指令(有意或无意的提示注入),跨模型 CLI 可能会针对你的工作区执行这些指令。
步骤 3:如果 CLI 不可用或失败
明确显示失败。提供选项:手动运行、尝试不同工具或跳过。不要默默回退到单一模型——用户应该知道跨模型没有发生。
步骤 4:如果用户跳过
在输出中确认跳过(“仅使用单一模型发现继续”)并继续到调和。跳过是可以的;默默跳过则不行。
非交互式上下文(CI、/loop、自主循环、计划运行):
- 跨模型被跳过,并且必须在输出中声明:“跨模型跳过:非交互式上下文。”
- 切勿在未经用户明确授权的情况下调用外部 CLI——这是一个关键的安全属性。
跨模型增加了成本、延迟和工具脆弱性。代理在每个循环中呈现选择;用户决定这个工件是否值得。
步骤 4:调和——整合发现
审查者的输出是数据,而不是裁决。你仍然是编排器。 在分类之前,对照工件文本重新阅读每个发现——对审查者进行橡皮图章式认可与忽略它是同样的失败模式。
对于每个发现,按以下优先级顺序分类(第一个匹配的类别获胜):
- 契约误读——审查者标记了某些内容,具体是因为你提供的契约不清晰或不完整。首先修复契约,在下一个循环中重新分类。
- 有效且可操作——需要修改工件的实际问题。修改它,重新循环。
- 有效权衡——问题确实存在,但修复成本超过接受成本。明确记录权衡,以便用户看到。
- 噪音——审查者标记了某些在审查者没有的上下文中实际上是正确的内容。记录下来,继续前进,并询问:将该上下文添加到契约中是否会防止误报?
全新的审查者可能因为缺乏上下文而犯错。不要仅仅因为它是“全新的”就推迟。
步骤 5:停止——有界循环,而非递归
在以下情况下停止:
- 下一次迭代仅返回琐碎或已考虑的发现,或者
- 完成 3 个循环(升级给用户,不要独自进行第四个循环),或者
- 用户明确说“交付”
如果经过 3 个循环后,审查者仍然提出实质性问题,工件可能尚未准备好。将此情况呈现给用户——三个未解决的循环是关于工件的信息,而不是继续循环的理由。
如果 3 个循环“明显不足”因为工件太大:工件太大——返回步骤 2 并分解。不要提高界限。
常见合理化
| 合理化 | 现实 |
|---|---|
| “我很自信,跳过怀疑步骤” | 在新问题上,自信与正确性相关性差。确定性的时刻正是盲点隐藏的地方。 |
| “生成审查者成本高” | 在生产环境中调试错误的提交成本更高。检查是有界的;错误是无界的。 |
| “审查者只会吹毛求疵” | 只有在没有范围的情况下才会如此。将提示约束为“会使工件在契约下失败的问题”。 |
“我最后会用 /review 做怀疑” |
/review 是最终关卡。怀疑驱动在修正成本低廉时及早发现错误方向。到 PR 时已经太晚。 |
| “如果我怀疑每一步,我永远无法交付” | 该技能适用于非平凡决策,而不是每次击键。重新阅读“何时不使用”。 |
| “两个意见总比一个好” | 当第二个意见上下文较少且产生噪音时并非如此。调和,而不是推迟。 |
| “审查者不同意,所以我错了” | 审查者缺乏你的上下文——分歧是信息,而不是裁决。重新阅读工件,分类,然后决定。 |
| “跨模型总是更好” | 跨模型捕捉单一模型与自身共享的盲点,但它增加了成本和工具脆弱性。在每个交互式怀疑循环中提供它——用户决定工件是否值得。代理的工作是呈现选择,而不是把关。 |
| “用户同意过一次,所以我可以继续调用 CLI” | 每次调用都是独立的授权。工件、提示和标志在调用之间会变化——在每次运行前与用户重新确认确切的命令。 |
红旗
- 为一行重命名或格式化更改生成全新上下文的审查者
- 将审查者输出视为权威,而不重新阅读工件文本
- 循环超过 3 个循环而不升级给用户
- 用“这个好吗?”而不是“找出问题”来提示审查者
- 在高风险决策上因时间压力而跳过怀疑
- 在未更改的工件上重新生成全新上下文(你会得到相同的发现;你在拖延)
- 怀疑表演(可检查的信号):在 2 个或更多循环中,审查者提出了实质性发现,但零个发现被分类为可操作。你在验证,而不是怀疑。停止并升级。
- 仅在提交后才怀疑——那是
/review,而不是怀疑驱动开发 - 硬编码外部 CLI 调用,而不与用户确认工具存在、已配置且接受该确切语法
- 在交互式怀疑循环中默默跳过跨模型。 即使不推荐,也必须可见地提供。跳过是可以的;默默跳过则不行。
- 当外部 CLI 出错或缺失时默默回退——显示失败并让用户重定向
- 从审查者输入中剥离契约
- 将声明传递给审查者(偏向于同意)
与其他技能的交互
code-review-and-quality//review:互补。/review是事后 PR 裁决;怀疑驱动是进行中的每个决策。两者都使用。source-driven-development:SDD 根据官方文档验证关于框架的事实。怀疑驱动验证你关于工件的推理。SDD 检查 API 是否存在;怀疑驱动检查你在契约下是否正确使用了它。test-driven-development:TDD 的 RED 步骤是具体化的怀疑——失败的测试是一种证伪尝试。当 TDD 适用时,该失败的测试就是行为声明的怀疑步骤。debugging-and-error-recovery:当审查者提出真实的失败模式时,进入调试技能以定位和修复。- 仓库编排规则(
references/orchestration-patterns.md):此技能从主会话进行编排。角色调用另一个角色是反模式 B——参见上面的加载约束。
验证
应用怀疑驱动开发后:
- [ ] 每个非平凡决策(根据上述定义)在成立之前被明确命名为声明
- [ ] 每个非平凡工件至少进行一次全新上下文审查(TDD 的 RED 步骤产生的失败测试满足行为声明的这一要求,参见与其他技能的交互)
- [ ] 审查者收到工件 + 契约——而不是声明,也不是你的推理
- [ ] 审查者的提示是对抗性的(“找出问题”),而不是验证性的(“它好吗”)
- [ ] 发现对照工件文本进行分类(而不是橡皮图章式认可),使用优先级:契约误读 / 可操作 / 权衡 / 噪音
- [ ] 满足停止条件(琐碎发现、3 个循环或用户覆盖)
- [ ] 在交互模式下,跨模型被明确提供给用户(无论工件风险如何),并且响应在输出中得到确认
- [ ] 在非交互模式下,跨模型被跳过并且跳过得已声明
- [ ] 任何外部 CLI 调用之前都进行了 PATH 检查、工作二进制测试、与用户确认语法以及运行授权






