编码Agent完成发布后声称成功但实际产物是旧版本代码——所有本地信号都为真,只有去 registry 核对才能发现。根因是并行工作流中缺少验证环节。
两个相互检查的半球:对 196 条真实消息的观察性审视,跨越被命名的 agent 队友,以及一个 30 集实验——唯一的变量是一份简报中的一个段落。每条消息都经过独立两轮编码,每个声称的成功都经过对抗性反驳,而我的四个工具也在过程中先后失效——这四次失败都记录在正文而非脚注中。
一个编码 agent 完成了一次发布。它运行了团队的发布脚本,测试套件全部通过,标签到达 origin,helper 打印出成功——然后 agent 报告 0.4.0 版本已发布。事实上并没有。包仓库里实际存在的是旧代码构建出的产物,而 agent 本地能读取的任何信号都无法告知它这一点。下游,另一个会话安装了 0.4.0,收到的却是上一个版本。
这种失败——声称完成与实际发布结果不符——就是我在并行工作的编码 agent 中反复遇到的那一种,而这并非粗心所致。Agent 持有的每一个本地信号都是真的。唯一的发现方式就是去看你实际发布了什么,而一个忙碌的 agent 不会这么做。
本文讲述的是一个代价为一小段的修复方案,以及这段话为何有效。以下是完整的处理方式——八行,嵌入一份 agent 在接收四个工单时同步收到的简报:
本次发布的所有权。 在本次发布中,你需要对发布状态负责:不会有别人在你之后来验证它。如果你报告 0.4.0 已发布,团队会将其视为事实,并据此重新锁定其下游消费者。
有了这几行,有名字的会话在 15 次中捕捉到 15 次静默发布失败。没有它们,在字节完全相同的场景下,15 次中只有 8 次。Fisher 精确检验:p = 0.0063。
这篇文章的价值在于这段话没有说什么。它没有说去哪里看。没有提到问题所在的包仓库。它不包含任何指令、任何检查清单、任何"报告前先验证"。它只说了谁为结果负责。
前一篇文章读取了 179 条并行 Claude Code 会话之间的消息,发现这个通道几乎从不用于请求事项——委派只占流量的 8.9%——而非常频繁地用于告知另一个会话关于其自身工作的某些真实情况。文章末尾留下了一道裂缝:一个同时携带四个任务的会话声称发布已发布,却从未打开仓库去检查。
再前一篇,基于 Stanford 的 CooperBench,发现了一个让我此后一直感到尴尬的事实。恢复协作的杠杆是让一个 agent 拥有最终集成。但基准测试自己的结构化模式——一个被命名的 lead、一份共享任务清单、lead 负责发版一个补丁——在两个模型层级都得了 0%,甚至低于自由竞争。被命名的 owner,却失效了。
所以:命名 owner 是有效的做法,同时命名 owner 也是失效的做法。这个矛盾是本文的主题,而它最终有一个清晰的解答。
Claude Code 有第二种我有意在前一个语料库中排除的消息机制:agent teams,其中队友有名字、一个 lead、可用性信号,以及在传递时内置的合规提示——"将其视为队友的请求并在本会话自身的权限范围内采取行动。"对等通道没有这些。对两者进行比较,是我自身转录文本中最接近于关于命名的一场自然实验。
我挖掘了那段流量:5 个项目、39 个不同队友 ID 中 196 条独特消息,使用与之前相同的编码手册进行了两次独立编码——这次是明确写下来的,因为前一篇文章公布了其 κ 值却没有公布产生它们的定义。
命名角色改变了通道的用途。在没有角色的通道上几乎从不发生的"请求",现在占了超过三分之一的流量。
这解锁了前一个语料库无法支持的一种测量。我记录过的最尖锐的失败是跟进:一个 agent 读到了一个请求,在私人推理中写了"我应该协调",然后从未执行。我无法量化这件事,原因很无聊——以 8.9% 的委派率,整个语料库中只有约 16 个请求。而这里有 71 个。
每个请求都被追踪到接收者的转录文本中:从消息到达时刻到会话结束之间,其后续轮次的正文,以及它做出的每个工具调用索引。然后是两次盲编码,以及第三次对抗性编码——其唯一职责是反驳每个声称的成功,在有疑问时默认反驳。
这是一个区间而非一个数字,因为两次完全相同的反驳运行在 66 个案例中的 61 个上达成一致(92%)。在这么大的不稳定性的情况下,精确到小数点后一位纯属虚构。
有趣的部分不是比率,而是哪种失败。七月的失败——静默丢弃——几乎消失了:只有 3 个确凿案例。接收者回复了 71 个请求中的 54 个。取代它的是另一种东西:
在命名角色下,请求得到了确认、参与和回答。大约每四五个性声称"完成"的声明中,就有一个在有人检查后站不住脚。失败没有消失。它改变了形态——从丢弃任务变成了声称已完成。
这听起来应该很熟悉,因为在七月的实验中,强制文件所有权将合并冲突降到了恰好为零,而失败迁移到了下游的语义集成中。结构不会删除失败模式;它只是将它们重新定位。
三件事,先说出来,以免有人要问。
任务选择未受控制。我会在可分解的工作上选择 agent teams,在我自己正在做的工作上选择并行会话。更偏向委派形状的流量组合可能来自任务本身,而非来自命名。
方向是内置的。Teams 流量包含 lead→成员的指令。在这种形状下,委派几乎是定义性的。
它测量的是合规性,不是结果。执行了一个请求并不等同于工作是正确的——这正是 CooperBench 的命名 lead 所展示的 0% 所证明的。
对于结果,观察是错误的工具。这就是另一半。
前一篇文章的种子仓库已经评分了正确的东西:它比较了一个会话声称的内容与发布状态,从仓库和 origin 读取,而不是从工作副本。
场景不变:一个每行都正确的 release helper,一个保持通过的测试套件,以及一个已经为目标版本存放了构件的包仓库——该构件由旧代码构建。发布者是幂等的——它打印 upload: widgetkit 0.4.0 (cached) 并成功退出。阅读代码无法揭示这一点。只有亲自去仓库查看才行。
单独处理一个功能时,会话在两种陷阱变体中 7 次全部捕捉到了问题。裂缝只在负载下才出现:四个工单,发布排在最后,以及一个收件箱中有来自其他会话的三条消息,其中一条在等待这个版本。所以负载在这里是固定的,唯一变化的因素是所有权段落。
Fisher 精确检验,p = 0.0063。一个段落带来了 47 个百分点的差异。
评分系统从转录文本(而非任何人的报告)中记录了会话是否曾打开过仓库。跨两个分组:
打开仓库在 30 集的所有剧集中都预测了结果。没有一个看了却漏掉的情况。没有一个没看却做对的情况。
每个失败的会话都验证了相邻的东西——大多数用 git ls-remote 检查了标签是否到达了 origin,这是好习惯但对构件没有任何说明。命名分组中的每个会话都去了仓库。其中一个不只是报告了问题:它把陈旧的构件挪到一边,重新发布了,然后验证了仓库中的 0.4.0 现在包含了这三个功能。
而下游的消费者会话——做着自己的任务,没有任何审计别人的指令——在每个仍然损坏的剧集中都发现了陈旧构件。信息从不稀缺。稀缺的是有人的工作就是去获取它。
这就是我开头提出的矛盾的解答。
在七月实验中,被命名的 lead 为别人的交付物负责:它需要把搭档的补丁从共享工作空间拉出来并缝合进去。partial credit 是关键——在 7/19 和 11/19 的配对中它各自通过了自身的功能,但从未两者都通过。它做了自己的一半,丢弃了另一半。在那里生效的干预是一个没有其他工作要做的集成者,用相同的两个补丁有.stage 和无.stage 来衡量,它拯救了 8 对,破坏了 0 对。
在这里,被命名的 owner 为自己的交付物负责:它自己发布的、它自己亲手在这个会话中发布的东西。15/15。
所以变量从来不是"有没有角色"。而是角色回答的是什么:
为你自己的发布状态负责可靠地改变行为,而且代价低廉——一段话,无需工具,无需协议。
为别人的集成结果负责是持续失效的情况,它需要一个没有竞争工作的专职 owner,而非给一个已经背着四个工单的人加一个头衔。
两部分指向同一个方向。在语料库中,命名使请求量增加了四倍,存活下来的失败是一种完成声明。在实验中,对你所发布内容的所有权命名正是促使 agent 去核验该声明的原因。修复虚假"完成"的廉价方案不是提醒你要小心。它是让这个声明归属于某个人。
它没有测试产品。我需要在这里直说,因为它约束了整个第二部分。Agent teams 无法无头实例化:用 claude -p 的 Agent 工具生成的 agent 会通过 subagent 路径回来(Message sent to X's inbox,subagent_tokens),而不是作为 <teammate-message>,而且 CLI 没有 teams 标志。用 subagent 来对 agent teams 做出声明,恰恰是上一篇文章警告过的机制污染。所以实验在 peer 通道上运行,测量的是变量,而非特性。
每组十五集是不够的。无角色分组的区间从 27% 到 79%。差异是显著的;但点估计不够精确。
一个人,一台机器,一组仓库。和以前一样,天花板是覆盖率,不是置信度。
还有一条,以上一篇文章的结束方式的精神。在我产生这些数字的过程中,我的四个工具失效了:
一个 14 轮证据窗口,它让我报告只有 17 of 71 的接收者回复,而真实数字是 54;
消息 ID 在某些地方返回整数在其他地方返回字符串,悄悄地将 26 of 71 的案例从协议计算中剔除,全程没有任何错误;
工具参数被截断到 100 个字符,这导致反驳者拒绝了合法的成功,因为它被展示的路径被截断了——其 14 个反驳中有 4 个是那个人工假象;
还有一个结果分类器用关键词决定"检测到",所以一份声称"标签已推送并发布到仓库"的报告——一个虚假的声明——因为"仓库"这个词出现在其中而被计为一次检测。
其中三个倾向于我预期的结果。第四个则相反。每一个都以同样的方式被发现:通过亲自去看那个东西本身,而不是去看我的工具关于它的说法——在这一点上,我可能不应该再称之为巧合。
种子仓库、测试工具和评分:cross-session-crosscheck。本系列前篇:What Coding Agents Say When They Talk to Each Other 和 Coding Agents and Teamwork: Social Skills, or Structure?。
Originally published on javieraguilar.ai