用途
Adverse 是多智能体对抗式代码审查技能,是 adverse 项目的 Claude Code 原生侧实现。相同逻辑也提供独立 CLI(adverse review …)。在 Claude Code 内运行时优先使用本技能:它使用 Claude Code 原生 Agent 工具启动审查者(无子进程认证问题、更快),仅在确定性的源码收集与合成步骤调用 Node 辅助脚本。
何时使用
用户明确要求对其代码进行对抗式/多视角/小组评审,或者即将合并/落地/发布非平凡改动并希望再做一次检查时使用。如果 diff 是纯机械性的(格式化、依赖升级、拼写修正),不要调用本技能——直接说明并停止。
前置条件
PATH 中需有 node(>= 20)。用 node --version 验证。技能脚本位于 ${SKILL_DIR}/scripts/ 下,仅依赖标准库(无需 npm install)。
如果缺少 node,告知用户:从 nodejs.org(或其包管理器)安装 Node 20+ 后重新调用。不要退回到其他实现;确定性合成器是契约。
阶段 0 — 决定审查范围
在消耗 token 前先选定范围:
- 如果用户指定了路径,那就是范围。
- 否则如果 git status --porcelain 报告有未提交改动,审查这些(相对 HEAD 的 diff 模式)。
- 否则如果当前分支领先于 main(或 master/配置的上游),审查自合并基点以来的 diff。
- 否则审查整个工作目录。
用一句话说明所选范围,以便用户纠正。
阶段 1 — 收集源码
运行 Node 辅助脚本,遍历目标并生成单个可直接嵌入提示词的文本块:
node ${SKILL_DIR}/scripts/collect.mjs --target <path> [--diff [base]] --out /tmp/adverse-source.txt --files-out /tmp/adverse-files.json
/tmp/adverse-source.txt 是要嵌入审查者提示词的内容。/tmp/adverse-files.json 是文件列表(用于向用户确认范围)。该辅助脚本执行与 CLI 相同的源码大小上限。
如果 collect 失败,向用户暴露错误——几乎总是“目标未找到”或“diff 为空”。
阶段 2 — 第 1 轮:独立审查
使用 Claude Code 的 Agent 工具并行启动三个审查者,每个对应一个角色。每个审查者获得:
- 系统提示词:从 ${SKILL_DIR}/scripts/prompts/<persona>.txt 读取(auditor / adversary / pragmatist)。
- 用户消息:/tmp/adverse-source.txt 的内容,前缀为 ${SKILL_DIR}/scripts/prompts/round1.txt。
- 模型:除非用户要求其他模型,否则用 opus。如果用户选择更小的模型,将其传给全部三个角色——不同角色使用不同模型会破坏单模型设计。
每个子智能体必须返回一个符合以下 schema 的 JSON 对象:
{ "persona": "<auditor|adversary|pragmatist>", "verdict": "approve|conditional|reject", "summary": "<one sentence>", "findings": [ { "severity": "critical|warning|info", "file": "<path or null>", "line": <int or null>, "title": "<short noun phrase>", "detail": "<2-6 sentences>", "fix": "<concrete remediation or null>" } ] }
将每个解析后的 JSON 对象保存到磁盘:
- /tmp/adverse-round1-auditor.json
- /tmp/adverse-round1-adversary.json
- /tmp/adverse-round1-pragmatist.json
如果某个子智能体返回格式错误的 JSON,将该角色重试一次,并在原提示词后附加解析器/校验器错误。如果重试仍失败,丢弃该角色。如果存活角色少于 2 个,中止运行并报告失败——合成至少需要 2 个声音。
三个都完成后,将它们合并为一个文件供下一阶段使用:
node ${SKILL_DIR}/scripts/combine.mjs
--round1 /tmp/adverse-round1-auditor.json /tmp/adverse-round1-adversary.json /tmp/adverse-round1-pragmatist.json
--out /tmp/adverse-round1.json
阶段 3 — 第 2 轮:交叉审查
对于每个产出了有效第 1 轮审查的角色,启动一个子智能体,它:
- 看到所有第 1 轮审查(合并后的 /tmp/adverse-round1.json)。
- 对其认同的发现进行验证(跨泳道验证是信号)。
- 对其认为错误/夸大的发现提出质疑(附具体理由)。
- 可选地补充其他角度浮现的新发现。
系统提示词:与第 1 轮相同的角色文件。用户提示词:${SKILL_DIR}/scripts/prompts/round2.txt 的内容,然后是第 1 轮合并 JSON,再然后是源码块。
输出 schema:
{ "persona": "<auditor|adversary|pragmatist>", "validate": [{ "from": "<reporter>", "title": "<title>", "reason": "<…>" }], "challenge": [{ "from": "<reporter>", "title": "<title>", "reason": "<…>" }], "added": [<finding object>] }
将每个保存到 /tmp/adverse-round2-<persona>.json 并合并:
node ${SKILL_DIR}/scripts/combine.mjs
--round2 /tmp/adverse-round2-*.json
--out /tmp/adverse-round2.json
如果用户要求更快的审查或 --single-round,完全跳过阶段 3。合成器将缺失的第 2 轮视为空交叉审查。
阶段 4 — 合成
运行确定性合成器。它生成规范报告——绝不要自己用 LLM 渲染发现,合成器的分组(交叉验证/共识/争议/单独)承载信号。
node ${SKILL_DIR}/scripts/synthesize.mjs
--round1 /tmp/adverse-round1.json
--round2 /tmp/adverse-round2.json
--out /tmp/adverse-report.md
--json-out /tmp/adverse-report.json
--html-out /tmp/adverse-report.html
读取 /tmp/adverse-report.md 并向用户呈现摘要,而非完整报告:
- 裁决行(例如 SHIP-WITH-CAVEATS (2/3 ship, 1/3 block))。
- 按严重程度和置信度的计数。
- 前 3 条发现(先交叉验证,再共识,再争议),附一行预览。
- 指向磁盘上完整报告和 HTML 仪表盘的指针。
然后询问用户希望如何继续:
- 仅对交叉验证的发现应用修复?(最高置信度)
- 对除争议外的所有内容应用修复?(最常见)
- 展示某条具体发现的完整推理?
- 只保存报告;我自己审查。
在用户选择之前,不要开始编辑文件。
阶段 5 — 清理
用户处理完报告后,删除 /tmp/adverse-* 并告知用户运行完成。不要提交这些文件。
失败处理
| 失败 | 处理方式 |
|---|---|
| collect.mjs 非零退出 | 暴露错误。常见原因:目标不存在、在非 git 目录使用 --diff、diff 为空。 |
| 某个审查者两次返回垃圾 | 用 2 个审查者继续,在摘要中标记运行为“降级”。 |
| ≥2 个审查者失败 | 中止。告知用户模型行为异常,建议用不同模型或 --single-round 重新运行。 |
| PATH 中没有 node | 告知用户安装 Node 20+。不要临时拼凑替代方案。 |
| 用户中断 | 停止启动新的子智能体。告知用户部分产物所在位置。 |
编排者注意事项
- 本技能约消耗 6 次模型调用(3 × 第 1 轮 + 3 × 第 2 轮)。按用户要求跳过第 2 轮可减半。
- 角色提示词刻意写成“守好本泳道”——不要用通用审查指令覆盖它们,这样做会瓦解设计所依赖的正交性。
- 合成器是确定性的、无 Python 的 Node 代码(synthesize.mjs)。统计 validate/challenge 边是共识信号;不要运行第四次 LLM“裁判”轮。
- 独立 CLI(adverse review)是另一条路径,它对任意编码智能体(claude -p、codex exec 等)进行子进程调用。仅在用户询问如何在没有 Claude Code 的情况下运行时提及它。