对抗性审查

Dev Tools

对每个非平凡决策进行对抗性审查,防止Agent过度自信导致的错误。 当正确性比速度更重要时、在陌生领域工作时、高风险操作时触发。 触发场景:架构决策、非平凡代码提交、声称"这是安全的/可扩展的/符合规格的"、 不确定领域的工作、生产环境部署、数据迁移、不可逆操作。

Install

openclaw skills install qqyougit-doubt-driven-development

Doubt-Driven Development(对抗性审查开发)

Overview

自信的答案不等于正确的答案。长时间会话积累上下文,悄悄把假设变成"事实"而无人察觉。

Doubt-Driven Development 是在任何非平凡输出成立之前,引入一个偏向证伪而非批准的新鲜上下文审查者的纪律。

这不是最终审查(/review)。最终审查是对成品的裁决。这是飞行中的姿态:非平凡决策在纠正成本还很低时被交叉质询。

When to Use

决策在以下任一条件成立时为非平凡

  • 引入或修改分支逻辑
  • 跨越模块或服务边界
  • 断言类型系统无法验证的属性(线程安全、幂等性、顺序、不变量)
  • 正确性依赖于未来读者看不到的上下文
  • 影响范围不可逆(生产部署、数据迁移、公开API变更)

适用场景:

  • 在不确定性下做架构决策
  • 提交非平凡代码/方案
  • 声称非显而易见的事实("这是安全的"、"这能扩展"、"这符合规格")
  • 在不完全理解的领域工作

不适用场景:

  • 机械操作(重命名、格式化、文件移动)
  • 遵循明确无歧义的用户指令
  • 阅读或总结现有代码
  • 显而易见的单行修改
  • 纯工具操作(运行测试、列文件)
  • 用户明确要求速度优先于验证

The Process

复制此清单应用本技能:

Doubt cycle:
- [ ] Step 1: CLAIM — 写出决策 + 重要性
- [ ] Step 2: EXTRACT — 隔离工件 + 契约,剥离推理
- [ ] Step 3: DOUBT — 用对抗性prompt调用新鲜上下文审查者
- [ ] Step 4: RECONCILE — 对每个发现按工件文本分类
- [ ] Step 5: STOP — 满足停止条件(微小发现/3轮循环/用户覆盖)

Step 1: CLAIM — 明确立场

用2-3行命名决策及其重要性:

CLAIM: "新的缓存层在规格描述的读密集负载下是线程安全的。"
WHY THIS MATTERS: 这里的竞态条件会损坏用户数据,且QA难以检测。

如果无法如此简洁地写出声明,你有的只是直觉,不是决策。在审查之前先把它明确出来。

Step 2: EXTRACT — 最小可审查单元

新鲜上下文审查者需要的是工件契约,不是推理过程。

  • 代码:diff 或函数 — 不是整个文件
  • 决策:3-5句提案 + 必须满足的约束
  • 断言:声明 + 支持证据(与 Step 1 的 CLAIM 块分开保留)

剥离你的推理。 如果你交出了结论,你得到的只会是对你结论的验证。审查单元必须小到审查者一次阅读就能把握 — 如果是500行的PR,先分解。

Step 3: DOUBT — 调用新鲜上下文审查者

审查者的 prompt 必须是对抗性的。框架决定答案。

对抗性审查。找出这个工件的问题。
假设作者过度自信。寻找:
- 未声明的假设
- 未处理的边界情况
- 隐藏的耦合或共享状态
- 契约可能被违反的方式
- 可能打破的现有约定
- 意外输入下的失败模式

不要验证。不要总结。找问题,或者在彻底审查后明确声明找不到任何问题。

ARTIFACT: <粘贴工件>
CONTRACT: <粘贴契约>

只传 ARTIFACT + CONTRACT。不传 CLAIM。 把你的结论交给审查者会偏向同意。审查者必须独立判断工件是否满足契约。

在扣子平台中的实现方式

使用 sessions_spawn 创建子Agent作为审查者:

  • 子Agent的指令中只包含 ARTIFACT + CONTRACT + 对抗性prompt
  • 不传递主Agent的会话历史或推理过程
  • 子Agent独立分析后返回发现

跨模型审查(可选)

单模型审查者与原始作者共享盲点。不同架构的模型能捕捉这些盲点。

在交互式会话中,始终提供跨模型审查选项,让用户决定是否值得:

"单模型审查完成。需要跨模型二次审查吗?"

注意:每次调用都需要用户明确授权。不要因为用户之前同意过一次就持续调用。

Step 4: RECONCILE — 折叠发现

审查者的输出是数据,不是裁决。你仍然是编排者。 在分类之前重新阅读工件文本 — 橡皮图章式同意审查者和忽略审查者是同样的失败模式。

对每个发现,按此优先级顺序分类(首个匹配的分类生效):

  1. 契约误读 — 审查者标记的问题是因为你提供的 CONTRACT 不清晰或不完整。先修复契约,下一轮重新分类。
  2. 有效且可操作 — 需要修改工件的真实问题。修改它,重新循环。
  3. 有效权衡 — 问题是真实的,但修复成本超过接受成本。明确记录权衡让用户看到。
  4. 噪音 — 审查者标记了实际上在审查者没有的上下文下正确的东西。记录下来,继续,并思考:如果在契约中加入该上下文,能否防止这个误报?

新鲜审查者可能因为缺少上下文而犯错。不要因为它是"新鲜的"就盲目服从。

Step 5: STOP — 有界循环,不是递归

满足以下条件时停止:

  • 下一轮只返回微小或已考虑过的发现,
  • 完成3轮循环(向用户上报,不要独自磨第4轮),
  • 用户明确说"就这样"

如果3轮后审查者仍然提出实质性问题,工件可能还没准备好。向用户展示这个情况 — 三轮未解决是关于工件质量的信息,不是继续循环的理由。

如果3轮"明显不够"因为工件太大:工件太大了 — 回到 Step 2 分解。不要提升上限。

Common Rationalizations

借口现实
"我很自信,跳过审查"自信与正确性在新问题上相关性很低。确定感最强的时刻正是盲点隐藏的时刻。
"生成审查者成本太高"在生产中调试一个错误的提交成本更高。检查是有界的;bug不是。
"审查者只会挑刺"只有在不设范围时才会。把prompt约束在"会导致契约失败的问题"。
"最后用 /review 做审查"/review 是最终门控。Doubt-driven 在方向错误但纠正成本低时抓住它。到PR时就太晚了。
"每步都审查就永远发不了"本技能只适用于非平凡决策,不是每次按键。重读"不适用场景"。
"两个意见总比一个好"当第二个意见上下文更少且产生噪音时则不然。调和,不要盲从。
"审查者不同意所以我是错的"审查者缺少你的上下文 — 不同意是信息,不是裁决。重读工件,分类,然后决定。
"用户说过一次yes就可以一直调用"每次调用都是独立的授权。工件、prompt和标志在调用之间变化 — 每次运行前都要和用户确认。

Red Flags

  • 为单行重命名或格式化变更生成审查者
  • 不重读工件文本就把审查者输出当作权威
  • 循环>3轮不向用户上报
  • 用"这样好吗?"而不是"找问题"来提示审查者
  • 在时间压力下对高风险决策跳过审查
  • 对未变更的工件重新生成审查(你会得到相同发现;你在拖延)
  • 审查剧场(可检查信号):跨2轮以上,审查者提出实质发现,但没有一个被分类为可操作。你在验证,不是在质疑。停止并上报。
  • 只在提交后才质疑 — 那是 /review,不是 doubt-driven
  • 不确认外部CLI工具是否存在/已配置/接受该语法就硬编码调用
  • 在交互式循环中静默跳过跨模型审查选项
  • 剥离审查者输入中的契约
  • 把 CLAIM 传给审查者(偏向同意)

Interaction with Other Skills

  • code-review-and-quality:互补。code-review 是事后PR裁决;doubt-driven 是飞行中逐决策。两者都用。
  • test-driven-development:TDD 的 RED 步骤是具体化的质疑 — 失败测试就是证伪尝试。
  • subagent-driven-development:子Agent完成每个任务后,用 doubt-driven 审查非平凡决策。
  • self-improvement-loop:当 doubt cycle 发现的错误模式重复出现时,触发 self-improvement-loop。

Verification

完成 doubt-driven development 后:

  • 每个非平凡决策在成立前都被明确命名为 CLAIM
  • 每个非平凡工件至少经过一次新鲜上下文审查
  • 审查者收到 ARTIFACT + CONTRACT — 不是 CLAIM,不是推理
  • 审查者的prompt是对抗性的("找问题"),不是验证性的("好吗")
  • 发现按工件文本分类(不是橡皮图章),使用优先级:契约误读/可操作/权衡/噪音
  • 满足停止条件(微小发现、3轮、或用户覆盖)
  • 交互式模式下,跨模型审查选项已明确提供给用户
  • 任何外部调用都经过了路径检查、工作测试、语法确认和用户授权