跳到主要内容
两个代理互审,我以为这就独立了

两个代理互审,我以为这就独立了

林默
林默

· 阅读约 6 分钟

上周五晚上我干了一件当时觉得挺稳的事:让两个 agent 一起弄一个 OAuth 令牌刷新的集成,一个写连接器,另一个写连接器的测试。文件不同,任务不同,测试全绿。我甚至有点乐,觉得这怎么说也算最朴素的对抗了吧。

乐了没几分钟,我把两份产物并排一看,心里咯噔一下。说不上来。

后来讲给一个朋友听,他问了一句:这俩代理,是不是从同一个 prompt 里推出“正确行为”的?

先停一下,问个问题:产物彼此独立,就真独立了吗?

我一开始也以为答案是肯定的。文件不同,角色不同,怎么不算独立?后来发现不是。Ken Alger 那篇《The Contract Discovery Bottleneck》把这事拆得比我亲身踩的还透。他那个密码重置流程,代理写出来的实现有个缺陷——重置链接可以反复用。没人发现,因为没有任何地方写过“重置链接应当只能用一次”。实现是坏的,但它符合所有写出来的规格。

剥到芯是这么一层:验证只对已经声明的不变量负责。最该声明的点没人写出来,验证器越认真,就越是认真地确认一个错误的想法。

再往下呢?有人给那个密码重置功能补了独立写的规格。代理照做,验证器终于拒绝重复令牌,代理改对。听起来这事结了?

评论区一个叫 Vinh 的人做了个并发实验。他没改规格,只把“链接只能用一次”这条不变量换了个形状表述——successes <= 1。在这个计数形式下,第一次并发测试就失败:200 次试验,每一次两个重置请求都成功,零拒绝。再换成原子版本,200 次全过,每次恰好拒绝一个。

我挺喜欢这个。不是因为他找到了 bug,而是他证明了一件事:同一条不变量,两种措辞。一种措辞下验证器证明没问题,另一种措辞下同一批代码稀里哗啦。验证器没变,代码没变,只是“什么叫单次”多了一个维度——顺序的重用和并发的重用不是一回事。验证器再正确,它也只会验证你告诉它的那一个形状。

停一下。真正让我后背发凉的不是这里。是我让两个代理互审,为什么没觉得有什么不对?

因为我自动把角色的独立性当成了证据的独立性。实现代理和测试代理,听起来是两个系统。但如果它俩都从同一段不完整的 prompt 里推导正确行为,它们共享的是同一个盲点。盲点不会因为复制到两个文件里就变成共识,它只会变成“两个东西一致地错”——最危险的一种错,因为所有机器检查都会通过。

Ken 的回应也落在这层。对抗性代理如果共享相同的上下文、模型假设、还有对需求的同一份不完整理解,独立性一样存疑。不是角色不够“对抗”,是原料没换。让一个模型扮演攻击者,用的还是那个不懂并发的上下文,它攻击出来的还是那个不懂并发的世界。

所以 JO Do 那条评论我几乎想整段抄下来。他说他们现在在接触 prompt 之前就写验收标准,从工单开始,而不是从同一个对话开始。这个做法对,不在流程更严谨,在知识来源变了。独立不是谁生成的产物,是哪个源头喂了它。

你可以引入第三个代理、第五个代理,甚至一个“验证者代理”。但验证器验证的是你写下来的合同,发现不了你没写下来的合同。而“一个重置链接应该只能用一次”这种事,对任何人都不是显而易见的需求——它是安全属性,藏在产品直觉背后,根本不会出现在功能请求里。除非你从外部把“单次使用的约束”这个知识投进去,否则代理永远不知道它该验证这个。

所以真正值钱的活,从验证挪到了发现。发现这个合同够不够:单次使用、两个并发尝试不能都成功、令牌不能授权不同账户、成功重置后旧令牌要失效、重置要使现有会话失效……这些哪些算产品决策,哪些是安全属性,哪些该是不变量,哪些根本不用管——这不是代理现在能自动列出来的。它列得出来,也不意味着列得对。

这就是那篇文章说的“合同发现瓶颈”。我觉得它比验证更深一层。验证器能完美验证“链接只能用一次”,但不会有人去修那个并发漏洞——除非先有个人发现“我以为的只能一次,其实被绕过”。

扯远一句,还有个更让人后怕的。代理不光写代码,还调用 API。HTTP 200 只说明请求被处理了,不说明你依赖的那个约束被执行了。一个写入可能成功后超时,然后重试一次,产生两次效果。等你发现,坏工具调用已经发生了。发出去的邮件回不来,扣过的款没法取消,消息可能已经跑到几百个人的屏幕上。坏实现还有个 diff 可看,坏调用连 diff 都没有。

证据如果能在执行之外单独产生,它就不能证明动作发生了;执行发生但证据路径脱离,动作就不可见。两者没有因果绑定,一切验证都是演给自己看的戏。

说回那个让我心里咯噔的集成。连接器和测试都写好了,绿色一片。它俩从同一段不完整的需求里推出来,完全一致,错得端端正正。真正的问题不是它们没对抗,是我没给它们任何外部来源的约束。我没有把“令牌刷新必须幂等”这条从安全政策里硬塞进去,也没从以前的线上事故里提炼。我给了两个角色,却只给了一个原料袋。

那晚的事,根子就在这儿。多代理互审不是不行,是它救不了共享的盲点。想真独立,要么换原料,要么别信角色。

至于这些合同到底该怎么被谁拥有、怎么把安全政策和既往事故这类“明显但没人说”的东西固化下来,是另一个更难的问题。这个我还没想透,先放这儿。

林默
林默

从具体轶事入口,一问一答把默认对的判断剥到设计权衡,主动暴露走过的弯路。

查看主页 →