Agent 方法论框架 superpowers:实现与评审为何隔离
本文基于 superpowers 仓库 commit 44c9b2d(2026-07-27)梳理,该项目仍在持续迭代,具体行为以仓库 https://github.com/obra/superpowers 最新代码与文档为准。
在 superpowers 的 subagent-driven-development 技能里,实现者和评审者是两份分开写死的提示词模板,评审者拿不到实现者的思考过程——它只拿到任务简报、实现报告和一份 diff 文件,并且被明确要求把那份报告当成「未经验证的说法」来读。 这不是排版上的分文件,是一条刻意设的信息边界:谁写的代码,谁就没资格给自己的取舍背书。
站内已经有几篇讲通用方法论的文章——Agent 角色分工讲为什么要拆角色,任务分解粒度讲一个任务该切多大,Claude Code 的 subagent讲工具层面怎么派活。这篇不重复那些结论,只做一件事:把一个真实开源项目怎么把「实现与评审不共享上下文」落成可运行的文件、脚本和提示词,逐条摊开给你看。仓库是 MIT 许可证的,你随时可以打开对照。
一、它想堵的漏洞:写代码的人给自己判卷
先看一个容易被忽略的事实:implementer-prompt.md 里本来就有一整段自查(Self-Review),分成 Completeness、Quality、Discipline、Testing 四组问题,包括「Did I avoid overbuilding (YAGNI)?」「Do tests actually verify behavior (not just mock behavior)?」。模板把它排在提交之后、汇报之前,自查里发现的问题要当场改掉再报。
按常理,自查做得这么细,独立评审是不是可以省?SKILL.md 给了一句很硬的话:
Implementer self-review never replaces the task review; both are needed.
为什么不能省,答案写在评审者那份模板里。task-reviewer-prompt.md 有一节标题就叫 Do Not Trust the Report:
Treat the implementer's report as unverified claims about the code. It
may be incomplete, inaccurate, or optimistic. Verify the claims against
the diff. Design rationales in the report are claims too: "left it per
YAGNI," "kept it simple deliberately," or any other justification is the
implementer grading their own work. Judge the code on its merits — a
stated rationale never downgrades a finding's severity.
这段话点破的是自查失效的机制。一个模型在实现过程中做出的每一个取舍,在它自己的上下文里都带着一条当时看起来成立的理由——「这里先不抽象,YAGNI」「这个分支暂时用不到」。等它回头自查,那些理由还在上下文里,于是自查变成了对既有理由的重新确认。它不是在说谎,它是没办法从自己的推理链里跳出来。
隔离要拿走的正是这条推理链。评审者从来没见过那些理由,它面前只有需求文本和实际改动,两者对不上就是缺陷,跟当初为什么这么写无关。
同一条逻辑还出现在评级规则里:如果计划文件本身要求做一件评审标准判定为缺陷的事(模板举的例子是「什么都不断言的测试」和「逻辑块的逐字复制」),评审者仍然要报为 Important,只是加一个 plan-mandated 标签。原文是 The plan's authorship does not grade its own work; the human decides.——计划的作者身份同样不构成免检理由,最终由人裁决。
二、隔离怎么落到实处:三份文件完成交接
隔离的说法容易,难的是评审者总得知道「原本要做什么」。superpowers 的做法是把交接物全部落成文件,而不是靠控制器在提示词里转述。
SKILL.md 的 Reviewer inputs 一条写得很死:评审者拿到三个路径——和实现者读的是同一份任务简报文件、实现者写的报告文件、以及一份评审包(diff 文件),外加绑定该任务的全局约束。
注意第一条。评审者和实现者共享的是需求的同一个源头,不是彼此的推理过程。这是这套设计最关键的一刀:需求必须同源,否则评审者会拿着自己脑补的需求去挑毛病;而过程必须隔离,否则就退化成自查。
三份文件各自由脚本产出:
scripts/task-brief PLAN_FILE N用一段 awk 从计划文件里抠出Task N那一节的全文,写进task-N-brief.md并打印路径。它匹配的是^#+[ \t]+Task[ \t]+[0-9]+这种标题,还专门处理了代码围栏,避免把 ``` 块里的假标题当成任务。scripts/review-package PLAN_FILE BASE HEAD生成评审包:提交列表(git log --oneline)、改动统计(git diff --stat)、以及带 10 行上下文的完整 diff(git diff -U10),三段写进一个按 range 命名的文件。scripts/sdd-workspace PLAN_FILE解析出这一份计划专属的目录<repo-root>/.superpowers/sdd/<plan-basename>/,并在上层写一个自我忽略的.gitignore。脚本注释里写明了为什么放在工作树而不是.git/下:Claude Code 把.git/当受保护路径、拒绝 agent 写入,实现者的报告文件就写不进去。
为什么非要走文件?SKILL.md 在 The Task Loop 开头给了理由:你粘进派发提示词里的一切、以及任何 agent 打印回来的一切,会在整个会话余下的时间里常驻你的上下文,并在之后每一轮被重读一遍。评审包因此从来不进控制器的上下文,评审者一次 Read 就拿到全部。这个「中间产物落盘而非回传」的思路,和中间产物落盘那篇讲的取向是同一路。
还有一条容易被当成客套话、实际很有杀伤力的约束——控制器不许替评审者预判:
If the prompt you are writing contains "do not flag," "don't treat X
as a defect," "at most Minor," or "the plan chose" — stop: you are
pre-judging, usually to spare yourself a review loop.
它连你会用哪几句话开脱都列出来了。如果你觉得某条会是误报,正确做法是让评审者照报,然后在循环里裁决并留痕,而不是提前把它的嘴堵上。
这套东西的组成部分如下,路径都在 skills/subagent-driven-development/ 下:
| 组成部分 | 它负责什么 | 对应仓库位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 主流程说明 | 定义整条循环:准备、任务循环、修复循环、最终评审、收尾 | SKILL.md | 上手第一件事,也是唯一讲清取舍原因的地方 |
| 实现者模板 | 实现者的角色、提问权、自查清单、四种状态的报告契约 | implementer-prompt.md | 每派一个实现任务都要按它组织提示词 |
| 任务评审模板 | 评审者的输入、不信任报告的立场、评级校准、输出格式 | task-reviewer-prompt.md | 每个任务实现完、进入门禁时 |
| 定向复审模板 | 只裁决既有 findings + 只看修复 diff 的窄口径复审 | re-review-prompt.md | 每一轮修复之后 |
| 任务简报脚本 | 从计划里抽出单个任务全文,避免让 agent 读整份计划 | scripts/task-brief | 派实现者之前 |
| 评审包脚本 | 把提交列表、stat、-U10 的 diff 打成一个文件 | scripts/review-package | 派评审者和复审之前 |
| 工作区脚本 | 解析每份计划专属的 git-ignored 产物目录 | scripts/sdd-workspace | 技能启动时,以及任何要落盘的时候 |
三、隔离之后,分歧怎么收场
角色一隔离,必然出现实现者和评审者各执一词的情况。这套流程没有靠「再聊聊」解决,而是设了一条有上限、有裁决、有留痕的路径。
修复循环的触发条件是明确的:spec ❌、任何 Critical 或 Important 级发现、或者控制器确认为真实缺口的 ⚠️ 项。Minor 不进循环,记进台账(ledger)交给最终的整分支评审去分诊——SKILL.md 直接点名:A roll-up nobody reads is a silent discard.(没人读的汇总等于悄悄丢弃。)
一轮修复 = 一次修复派发 + 一次定向复审,每个任务最多五轮。有意思的是这五轮的内部分层:
- 第 1–3 轮:唤回原来那个实现者。 理由写得很直白:它的上下文是完整的,它知道任务、代码和自己做过的选择。这里刻意在用上下文,而不是躲着它。
- 第 4–5 轮:换一个全新实现者,并且用高一档的模型。 理由是
A loop that survives three resumes usually means the implementer cannot see its own problem — fresh eyes and a capability bump in one move.(熬过三轮唤回的循环,通常意味着实现者看不见自己的问题。)
这两段其实是同一个判断的两面:上下文是资产,直到它变成盲区。superpowers 没有教条地一律隔离,而是给了一个切换点——三轮。至于什么时候用哪档模型,Model Selection 一节还有一条反直觉的提醒:Turn count beats token price.——最便宜的模型在多步任务上常常要多花 2–3 倍轮次,整体反而更贵,所以评审者和「从散文式描述开工」的实现者建议以中档为下限。这条和上下文管理里讨论的成本结构可以互相印证。
复审是窄口径的。re-review-prompt.md 要求逐条给出 ADDRESSED 或 NOT ADDRESSED,并且卡了一句很实用的话:"Attempted" is not addressed: the specific defect must no longer exist.(尝试过不算解决,那个具体缺陷必须不存在了。)复审者只看修复 diff,修复之外发现的问题进 Out-of-Scope Observations,不阻塞、也不延长循环。
第五轮之后还有未决项,断路器跳闸:停止派发,由控制器逐条裁决,只有三种归宿——评审者有误或该点可争议,记 parked 并写明为什么代码可以维持原样;确实是真问题但下游没有依赖,同样 parked 但注明真实且延后;真问题且承重(后续任务建立在它之上,或它暴露了计划缺陷),则 STOP,写 Task <N>: BLOCKED,带着发现、冲突的计划原文和修复历史交给人。
原文补了一句防作弊:Adjudicate only at the cap. Adjudicating earlier to end a loop is pre-judging with a different name.(只在到达上限时裁决,提前裁决就是换个名字的预判。)每次裁决都必须落成一条台账记录,悄悄丢弃是被禁止的。
四、边界与代价:它明确不管什么
这套流程不是白拿的,把代价摊开说清楚更有用。
它换来的东西有明码标价。 一个任务的常规成本是一次实现派发加一次评审派发;一旦进循环,每轮再加一次修复派发和一次复审派发,最多五轮;全部任务做完还有一次整分支评审(文档要求它跑在当前可用范围内能力最高的那档模型上,而不是会话默认档)、一次修复波次、一次定向复审。对于改一行常量、调一个文案这种变更,这套编排是彻底的过度设计——它自己的适用判定图里,第一个菱形就是「有没有实现计划」,没有计划的答案是「手工执行或先做头脑风暴」。
它要求一种特定形状的输入。 task-brief 靠正则去抓 Task N 标题,计划文件不长这样,脚本直接报「task N not found」退出。适用判定的第二个菱形是「任务之间大体独立吗」,紧耦合的答案同样是别用这条路。第三个菱形是「留在当前会话吗」,否则该用另一个技能 executing-plans。
它在几处主动放弃了覆盖面。 评审者被要求 Do not crawl the broader codebase.,只有在能指名一个具体风险时才允许看 diff 之外的代码,且要在报告里写明检查了什么。代价是:无法从 diff 判断的需求(活在未改动的代码里、或跨任务)只能报成 ⚠️ 项,然后由控制器自己解决——这部分负担并没有消失,只是转移了。
它不重跑测试。 两份评审模板都写着不要为了确认实现者的报告而重跑测试套件,只有读代码读出具体疑点时才跑一个聚焦测试。这意味着测试证据的信任边界落在实现者的报告上:评审者能做的是核对报告是否指名了覆盖测试、命令和输出,以及把输出里的警告和噪音当成 finding(原文要求 test output should be pristine),但它不会替你重新验证一遍。
它明确不管的事。 计划自相矛盾、或计划文本与评审标准直接冲突,交给人裁决而不是自动选边;承重的残留问题不自动绕过,而是停下来报 BLOCKED;最终评审之后没有第二轮修复波次。它也不承担产品决策——实现者模板里那句 It is always OK to stop and say "this is too hard for me." Bad work is worse than no work. 就是把「不会做」明确设计成合法出口。
还有一层隐性成本:读它。 SKILL.md 单文件近 28KB,里面大量篇幅是在解释某条规则为什么存在。想照搬,你得先把这些取舍读明白,否则很容易只抄形式、丢掉理由。
五、上手与避坑清单
下面每条都是仓库里被专门写下来防范的坑,说明为什么会踩、以及怎么避。
1. 评审包的 BASE 用了 HEAD~1。 为什么会踩:手边最顺手的写法就是它,而且单提交任务下结果完全正确,不会报错。后果:一个任务如果产生多个提交,diff 会静默地只剩最后一个提交的内容,评审者对前面的改动一无所知。怎么避:在派发实现者之前就 git rev-parse HEAD 记下 BASE,评审和修复轮次都用这个记下来的值。SKILL.md 在处理报告和派发评审两处各警告了一次,scripts/review-package 的脚本注释里还写了一遍,可见是高频事故。
2. 派发提示词里塞进前序任务的历史。 为什么会踩:控制器手里有全部上下文,顺手贴进去感觉是「给足信息」。后果:SKILL.md 记了一个真实数字——某次会话的派发提示词达到 42k 字符,其中 99% 是粘贴的历史。怎么避:一次派发只描述一个任务,按模板给五样东西——这个任务在项目里的位置一句话、简报路径、前序任务里简报无从知晓的接口与决定、你对简报里歧义的裁定、报告文件路径与报告契约。精确数值只出现在简报里,不在提示词里重复。
3. 派发时不写 model。 为什么会踩:省略它不报错,行为看上去也正常。后果:静默继承会话模型,通常就是最贵那档,整节 Model Selection 的省钱意图直接失效。怎么避:把「显式指定模型」当成派发的必填项,模板里 [MODEL] 是标了 REQUIRED 的。
4. 台账缺失或串台。 为什么会踩:觉得 todo 列表够用了。后果:会话记忆熬不过压缩,SKILL.md 称此为「观察到的最昂贵的失败」——丢失位置的控制器重新派发了整段已经完成的任务序列。怎么避:台账写在这份计划自己的工作区目录里,第一行必须写成 # SDD ledger — plan: <plan file path>;恢复时以台账和 git log 为准,而不是自己的记忆。另外,第一行指向别的计划文件的台账、以及旧的扁平路径 .superpowers/sdd/progress.md,都是别人的进度,别拿来当自己的。
5. git clean -fdx 一把梭。 为什么会踩:工作区是 git-ignored 的,清理命令看不出它有价值。后果:简报、报告、评审包、台账一起没了。怎么避:知道这件事会发生,真发生了就从 git log 里重建——提交是唯一活下来的记录。
6. 并行派多个实现者。 为什么会踩:任务本来就是按独立性切的,并行看起来是免费提速。后果:改动冲突。怎么避:SKILL.md 的规定是绝不并行派实现子代理;并行的位置在别处(该项目另有一个专门讲并行派发的技能),不在同一条实现流水线上。
7. 控制器自己动手把问题改了。 为什么会踩:发现只是个小毛病,派一轮的开销显得不值。后果:一是污染控制器上下文,二是这处改动绕过了评审。怎么避:原文的处理是唤回实现者;SKILL.md 末尾那张 Common Rationalizations 表把这类借口逐条列了出来,包括「差不多合规了」「再来一轮就收敛了」「修得很小,跳过复审吧」,右边一栏是对应的现实。
六、收束:先读哪三个文件
如果你只想拿走一个可复用的判断,是这句:需求同源,过程隔离。 让实现者和评审者读同一份需求文件,但别让评审者看见实现者是怎么想的;剩下的冲突用有上限、有裁决、有留痕的循环收场,而不是靠再解释一遍。
上手顺序建议是:skills/subagent-driven-development/SKILL.md 通读一遍,重点是 The Task Loop 和 The fix loop 两节的理由部分;然后对读 implementer-prompt.md 和 task-reviewer-prompt.md,注意同一件事(测试证据、YAGNI、报告内容)在两份模板里分别是怎么被表述的——差异就是隔离的设计意图;最后翻 scripts/ 下那三个几十行的 shell 脚本,它们是「怎么把交接物落盘」的最小实现。
照搬之前,先用三个问题量一下自己的场景:手上有没有一份带明确任务分节的实现计划?这些任务是不是大体独立?为这次改动多付一次评审派发的开销,划不划算?三个都是「是」,这套编排才开始有意义;只要有一个是「否」,SKILL.md 的判定图已经把你指向别的路径了。
本文属于 superpowers 方法论专题(共 30 篇,含三篇与其它开源 Agent 项目的对照)。想看把资产铺满的另一种取向,见 ECC 开源 Agent 套件专题。