实现者和评审员必须是两个 Agent
只出一件东西的时候,这个问题不存在
你手上只有一篇要写的东西,那就一轮一轮地改:让它写,你看,你说哪不行,它再改。你自己就是评审员,中间的每一步你都在。这时候讨论「实现者和评审员要不要分开」是没有意义的,因为分开的那一半本来就长在你身上。
问题是从「一次要出一批」开始的。
我这条内容产线上,人的位置和单篇的时候完全不一样:开工前定规格、定事实源,收工后验收,中间那一段人不在。这不是偷懒,是批量之后人工逐篇确认必然退出中间环节——留下来也看不完。于是原来靠人补上的那道判断,得换成别的东西来补。我换的是三层:规格写死、独立评审、脚本客观体检。这篇只讲中间那一层。
最省事的做法当然是让写的那个 Agent 自己再检查一遍:省一个阶段,也省一份提示词。它写完,你在同一段提示词后面追一句「现在请你自检以下十项」,它会认认真真地给你逐条打钩。
这个自检的问题不在于它不认真,而在于它检查的对象不对。它手上有自己刚才那一整段推理:为什么这么写、哪句是它掂量过觉得可以的、哪个地方它心里其实没底但说服了自己。它拿着这份推理去看自己的产物,看到的是「我当时是这么想的,所以这么写合理」。而真正要检查的是「离开你的推理,这段文字本身能不能成立」。
更麻烦的是另外一类失败。批量跑的时候有两种事故:一种是它压根没写,一种是它写了但写坏了。这两种在返回结果里长得一模一样——都是「返回了一个看起来正常的结果」。一个知道自己刚才干了什么的 Agent,是没法诚实地报告「我其实没干」的。
四个当场能问自己的问题
我判断一条流水线上「写」和「审」有没有真的分开,就问这四句。不用看代码,看编排的形状就能答。
第一句:评审的那一方,能看到实现的那一方的推理过程吗?
如果评审是在同一段对话里追加的一轮,那答案是能,整段上下文都在。这种情况下它不是评审,是同一个人的第二次自我确认。真正的分离是:评审员启动的时候手上只有两样东西——落盘的文件,和这一篇的规格。实现者想了什么、纠结过什么、哪句话是权衡之后留下的,它一概不知道。它只能拿规格去量文件。
第二句:两边的输出是结构化字段,还是一段自然语言?
「我已经完成了这篇文章,字数符合要求,质量良好」——这种话没法用。它既不能被机器统计,也没法在事后回查是谁在哪一步松了口。我这条线上两个阶段都强制返回固定字段:写的那一边要交 slug、是否已写入、字数;审的那一边要交 slug、字数、改了什么、还剩什么没解决、结论。字段是硬的,没填就是没填,不靠解析自然语言去猜它到底过没过。
第三句:结论是两值还是三值?
这一句是我这四个问题里最有信息量的。
如果结论只有「通过 / 不通过」,那么「一次就写对」和「写歪了但被评审改回来了」会被记成同一件事。批量跑完你看到一片通过,感觉良好,实际上可能有相当一部分是被抢救回来的。我把结论定成三值:一次就过、改过之后能过、不通过。第二类单独统计。
为什么值得单独统计:一次就过和改过才过,反映的是两个完全不同的东西。前者说明规格说清楚了,后者说明规格没说清楚——评审员之所以能改,是因为它看着同一份规格能看出偏差,那就意味着实现者本来也该看出来却没看出来。改过才过的那些集中出现在同一处,要改的就不是提示词,是规格里那一段的写法。
第四句:评审员的权限,比实现者大还是小?
这个我认为是最容易搞反的。直觉上「审查者管着执行者,所以权限应该更大」,实际上恰恰相反。评审员能改文件,但不能提交、不能构建、不能碰别的文件。它的活动范围比实现者还窄。
理由很简单:评审员是这条链上最后一个动手的角色,它出错没有人再兜。权限给大了,它一旦跑偏,破坏范围也跟着变大。我这条线上的原话就写在提示词里:不许 commit,不许跑 build,不许动其它文件。
落到编排上是什么样
具体到编排脚本末尾,就是一个两阶段的管线:对每一篇,先起一个实现者,拿到它的结果之后再起一个评审员,两个阶段各带自己的结构化 schema。
const results = await pipeline(
ITEMS,
(it) => agent(writePrompt(it), { label: `write:${it.slug}`, phase: 'Write', schema: WRITE_SCHEMA }),
(w, it) => agent(reviewPrompt(it), { label: `review:${it.slug}`, phase: 'Review', schema: REVIEW_SCHEMA })
.then((r) => ({ slug: it.slug, write: w, review: r })),
)
这几行里我要拎出来的不是 API,是三个决定。
第一,篇与篇之间不设同步点。 每一篇独立走完写和审两个阶段,A 篇进评审的时候 B 篇可能还在写,谁也不等谁。没有「等全批写完再统一评审」这道屏障。这一条是我后来觉得最值的:屏障一旦存在,最慢的那一篇会把整批卡住,而且中间任何一次中断,都会把「已经写完但还没审」的一批悬在半空。
第二,评审员的输入是重新构造的,不是继承来的。 reviewPrompt 和 writePrompt 是两段分别拼出来的提示词,评审员从规格出发去读文件,不接手上一段对话。
第三,评审员的第一件事不是评质量,是验存在。 提示词里第一条约束的原话是:如果这个文件不存在或为空,直接返回 verdict=FAIL 并在 remaining 里写明「文件未落盘」。
先验存在再验质量,顺序不能反。前面说过,「没做」和「做坏了」在返回结果里表现一致,如果评审员一上来就进入质量判断,遇到空文件时它很可能顺着规格生成一段像样的评语,而不是报告文件不在。把这两类检查拆成先后两步,是我在这条线上花过代价换来的。
评审员提示词里另外几条也都是有来历的:
- 「发现问题就用 Edit 直接改掉,不要重写全文。」 这句拦的是「整篇重写」这条捷径。重写出来的东西通常也能读,但它已经不是被审查的那一篇了——你失去了「改了什么」这个信息,也失去了判断规格好不好的依据。
- 「用 Python 数正文字数,别用 grep。」 中日韩字符按字节计会虚高,字数这道门直接形同虚设。所以返回的字数字段必须注明是「你自己数出来的最终值」,而不是照抄实现者报的那个。
- 「你绝对不许联网。」 我这条线上遇到过子代理调联网检索之后既不报错也不返回、一直挂在运行中的情况,而且挂住的那个还会覆盖已经写好的成品。核实这件事我留在开工前自己做,做成事实卡发下去,子代理只准写文件。
收工的时候我只统计三件事:完成数对不对得上应完成数、不通过的都有哪些、字数不够的都有哪些。这三件事都是回磁盘数出来的。汇总层报什么我不看——我这条线上,汇总两个方向都骗过我:报零完成但磁盘上已经躺着若干篇,报已完成但实际没落盘。汇总层的状态不是事实,产物才是事实。
什么时候这条不适用
你自己在中间那一环里,就不用拆。 单篇创作、每一轮你都过目、方向和提纲都由你敲定——这时候你就是评审员,再插一个 Agent 评审员进去只会多一层噪音。这条判据的适用前提是「人已经退到两端」,前提不成立,结论也不成立。
评审员没有独立依据的时候,拆了也没用。 评审员之所以能审,是因为它手上有一份和实现者同源、但独立于实现者推理过程的东西——规格和事实卡。如果这一批的判断标准还没写下来、还在你脑子里,那评审员拿到的就只有「你觉得写得好不好」,它给不出比实现者更可靠的答案。这种情况下该做的是先把规格写出来,而不是先加一个评审岗。
注意评审员可能连事实卡一起质疑。 我这条线上出现过评审阶段反查出事实卡本身有错、导致已经写完的几篇同时出错的情况,得回头实测修正再全批检查一遍。所以我给评审员的定位不是「核对文章符不符合卡」,而是允许它对卡本身提出疑问、写进「还剩什么没解决」里交回来,而不是让它自行修正后写进文章。
还有一种情况是拆了不解决问题:失败集中在同一处。 如果改过才过的那些篇,改的都是同一段,那说明缺口在规格,不在评审。这时候加评审员只是每次都把同一个洞补一遍,代价是每一篇都多跑一个阶段。该做的是回去改规格里那一段的写法。评审是用来兜住分散的、偶发的偏差的;系统性的偏差要在开工前解决。
最后说一句我为什么坚持这个拆分。多 Agent 编排的可靠性设计,前提是承认单个 Agent 的成功率明显低于一。独立评审、回查、结构化输出,都是在这个前提下的补偿措施。不承认这个前提,就会把「偶尔失败」当成「基本不会失败」来设计——然后在批量跑完之后,对着一片绿色的汇总,去猜里面到底有几篇是真的。
这篇的判据来自我自己带 Agent 干活的实践,样本有限。它更像一份可以拿去验证的假设,而不是一份可以照抄的规范。
延伸阅读
- 下一篇(协作与移交):Agent 批量生产:把核实从生产里拆出来单独做
- 这个专题的主论点:Agent 调用没报错,不等于这件事办成了
- 专题导读与七个阶段的地图:把 Agent 当成一个要上岗的员工来带:这个专题讲什么