Sunny Code

一个 Agent 改两个仓库,还是两个 Agent 分头做?

三轮跨仓库实验:从分工实现,到接口协商,再到消息何时进入上下文。

做 Sunny 的跨仓库协作时,我们需要决定一件事:一个功能同时涉及前端和后端,应该让一个 Agent 一起改,还是让两个 Agent 分头做、互相发消息?

两种方式都有吸引力。一个 Agent 能同时看到两边,修改接口时可以一起调整调用方。两个 Agent 可以并行工作;如果前后端已经各有一个长期使用的会话,也能继续沿用各自的上下文。

这里的 session,就是承载 Agent 对话历史和执行过程的会话。我们比较的两种安排是:

方式谁负责代码两边如何对齐
单会话、多仓库一个 Agent 访问并修改前后端两个仓库在同一个上下文里决定和修改
双会话、互发消息两个 Agent 各负责一个仓库通过消息交换接口方案和进度

实验分了三轮。前两轮比较这两种安排,先给明确接口,再加入需要协商的接口和已有历史。第二轮暴露出消息可能来得太晚,第三轮才改为比较两种消息投递方式。第三轮两组都是两个 Agent,不再有单 Agent 组。

同一项任务,两种工作方式

一个会话两个会话AgentAgentAgent前端仓库后端仓库前端仓库后端仓库共享上下文消息对齐↔ 消息
结构示意,不表示运行时间或 token 数量。两种方式共享任务预算。

三轮分别在比较什么?

每种方式 4 次。每格代表一次结果,按结果分组,不表示发生顺序。三轮不合并排名。

一个会话,还是两个会话?

单会话、多仓库

代码通过验收4/4
预算内完成且通过4/4

双会话、互发消息

代码通过验收4/4
预算内完成且通过4/4

28 项检查;更正两处显示文本断言后,对全部原产物复验。双会话均时少 12.6%,累计报告 tokens 为 2.17 倍。

第一轮:接口已经定好,分头做有没有收益?

题目是两个独立仓库里的 OAuth 集成,也就是产品里常见的“连接第三方账号”。连接状态要持久化,重启不能丢;要处理重复和并发回调、授权失败、超时、工作区隔离,兼容旧接口,还要在真实浏览器里完成跳转、重试和跨标签页同步。

仓库规模不大,但行为要求并不简单。

两种安排都用 GPT-6.1 Sol,medium 推理强度,并经过 Sunny 的真实执行链路。每一对实验从相同起点完成同一项任务。双会话共享整个任务的预算,不是每个 Agent 各领一份。

模型停止后,外部执行器再运行事先固定的 HTTP 和浏览器检查,不把评分结果发回去让模型补做。执行与评分流程分开,但文件访问仍主要靠指令约束,没有操作系统级权限隔离。

四对、八次运行,最终都通过了 28 项检查。双会话在四对里快了三对,平均实现时间少约 12.6%;累计报告 tokens 则是单会话的 2.17 倍。这个数包含缓存读取,不能直接说成费用翻倍。

这给了双会话一个不错的开局,但有一个前提:两边拿到的是完整、明确的接口约定,路径和字段都已规定好。它们主要需要分工实现,不必自己协商接口。

“全部通过”也经历过一次纠错。一个实现显示 Connected,评分器却要求小写 connected,把合法界面判错。我们保留原始成绩,只修正两处显示文本断言,然后对全部八份原始实现统一复验。模型没有补做,产物没有修改。[1]

因此,第一轮支持的判断很有限:接口明确时,两种安排都能完成这批任务;双会话有一些并行收益,也消耗了更多 tokens。接下来要看的是,接口还没定好时会发生什么。

第一轮:四对任务,分别看耗时

每对起点相同。全部通过 28 项检查。

新会话 1

单会话
230.7 s
双会话
282.9 s

新会话 2

单会话
245.9 s
双会话
201.7 s

交接历史 1

单会话
327.9 s
双会话
236.8 s

交接历史 2

单会话
280.9 s
双会话
226.9 s
所有条形使用相同的 0–360 秒刻度;不是端到端验收耗时。双会话在 4 对中快了 3 对,平均少约 12.6%,累计报告 tokens 为单会话的 2.17 倍。

时间少了一点,用量多了多少?

第一轮 · 每种方式 4 次运行的平均报告 tokens

未缓存输入缓存读取缓存写入输出
单会话299,971
双会话650,318
每次均值单会话双会话
未缓存输入23,27643,162
缓存读取266,464592,128
缓存写入00
输出10,23115,029
两条使用相同刻度。每次用量是多次模型调用的累计值,含反复读取缓存;不是上下文窗口大小,也不是美元费用。均值四舍五入显示;缓存写入均为零。

第二轮:两边需要自己协商,还能顺利完成吗?

真实跨仓库工作里,前端往往已经依赖一套字段,后端也要保留既有行为。新功能没写,旧代码和历史决定已经占好了位置。

第二轮从能工作的 OAuth 产品出发,增加安全断开连接的功能。服务端要检查客户端操作的是否仍是当前版本,记录操作,并保证同一个请求重复发送不会重复执行。

一个具体场景是:断开连接已经成功,但浏览器没收到答复。用户后来又建立了新连接,再重试旧请求,不能把新连接也断开。

我们设计了两个场景,每个场景做两对比较:

  • 接口协商: 前后端的本地笔记里有互不兼容、尚未正式采用的 API 草案,需要自行统一路径和字段。评分器读取最终约定,再检查行为。
  • 已有历史: 先让真实 Sunny 会话完成维护工作,留下代码、测试和完整历史,再接着做新需求。双会话恢复各自原生历史;单会话可以读取两边完整历史文件。

第二个场景让双方都能获得相关信息,但上下文组织和检索成本不同。这也是两种工作方式的实际区别,不能把所有结果简单归因于 Agent 数量。

这一轮仍是四对、八次运行。每个任务共享十分钟、160 万累计报告 tokens 和 2.4 万输出 tokens 的上限。我们分别记录两个结果:停下来时,代码是否通过验收;以及是否在预算内完整完成任务。 代码过关但超出预算,仍算预算内交付失败。

第二轮单会话、多仓库双会话、排队发消息
预算内完成并通过全部验收4/40/4
停止时产物通过全部 48 项检查4/41/4

双会话四次都触及 token 上限。其中一次代码已经通过全部检查,但没有在预算内完整结束。

第二轮:失败落在哪一对?

同一任务单会话双会话
接口协商 148/48预算内完成48/48超预算
已有历史 148/48预算内完成21/48超预算
已有历史 248/48预算内完成37/48超预算
接口协商 248/48预算内完成21/48超预算
数字是检查通过数,不是完成比例。四次双会话均超预算;第一对的代码虽然 48/48,仍未在预算内交付。已有历史 1 的接口不一致;已有历史 2 的文件约定一致,前端实现却未对齐。

具体失败比排名更值得看。一次运行里,两边各自做好了一部分,却用了不同的路径和字段。另一次的接口约定文件已经一致,前端代码却仍在请求旧路径、读取旧字段。消息里同意了,文件里写对了,不代表代码已经跟着改对。

我们于是回头检查消息的时间线。38 条请求都被发送通道接受,但任务停止时,只有 20 条进入了接收方的持久化历史。一个历史场景发了 6 条,停止前一条都没有进入。

当时采用的是 followUp,也就是排队追加:对方忙着执行时,消息先等着,等这轮执行结束后再追加进去。双方都在长时间实现和测试时,影响接口的决定也可能一直在队列里等。

这让“两个 Agent 没做好”多了一种解释:对方发来的消息,可能根本还没有进入它当前的工作上下文。第二轮还不能证明这解释了多少失败,所以我们决定把消息时机单独拿出来测。[2]

第三轮:同样是两个 Agent,早点收到消息会怎样?

这一轮不再比较一个和两个 Agent。两组都是前后端各一个 Agent,复用第二轮的起始代码、历史、题目和预算,只改变消息的投递方式:

消息方式对方正在工作时,消息如何进入
排队追加(followUp)等这轮执行结束后追加
执行途中处理(steer)在执行过程中的工具边界进入上下文

途中处理也不会打断一个正在运行的工具。预实验还纠正了我们一个过于乐观的假设:由于异步转发,消息不保证赶在紧接着的下一步之前进入,可能再多走一步。

我们用连续三个工具步骤检查链路:排队追加的消息在最后一个工具之后进入;途中处理的消息在第二和第三个工具之间进入。确认这项区别实际存在后,才按预定顺序跑完四对、八次正式任务。

同一条消息,在哪一步进入?

followUp · 排队追加

  1. 工具 1
  2. 工具 2
  3. 工具 3
  4. 消息进入

steer · 途中处理

  1. 工具 1
  2. 工具 2
  3. 消息进入
  4. 工具 3
三步骤预实验中观察到的顺序示意,不按耗时比例绘制。两种方式都不打断正在运行的工具;异步转发意味着 steer 也不保证紧接着下一步就收到。
第三轮排队追加执行途中处理
预算内完成并通过全部验收0/41/4
停止时产物通过全部 48 项检查1/44/4
停止前进入接收方历史的消息14/3245/47

发送被接受之后,多少消息真正进入?

第三轮 · 停止前进入接收方持久化历史

排队追加14 / 32
停止前仍未进入:18
途中处理45 / 47
停止前仍未进入:2
蓝色为已进入,灰色为停止前未进入;条形显示各组占比,消息总数不同。进入历史不代表理解。154 秒与 10.7 秒的中位延迟只统计已进入的消息,不能当作交付提速。

只看已进入历史的消息,中位延迟约为 154 秒和 10.7 秒。没有进入的消息不在这个中位数里,所以不能把这两个数包装成交付提速倍数。进入历史,也只说明模型有机会处理,不能直接当作已经理解。

这一轮出现了一段很能说明问题的过程:前端提出方案 A,后端提出方案 B。过了一会儿,前端同意改成 B,后端也同意改成 A。双方都很配合,代码却依然接不上。

途中处理组同样发生了交叉确认。但冲突信息能在工作尚未结束时进入,双方后来明确冻结最终版本,最后实现了相同接口。

那次产物通过全部 48 项检查,却因输出超预算仍算失败。另一次途中处理才在预算内完整完成。排队组也有一次做到 48/48,只是超预算。[3]

这批结果支持消息时机值得改进:途中处理组的最终代码更容易对齐。但四次里只有一次在预算内完成,协商和收尾的成本仍然很高。

第三轮:八次运行,一次次看

场景消息方式检查通过预算内交付
接口协商 1followUp21/48输出超预算
接口协商 1steer48/48输出超预算
已有历史 1steer48/48累计用量超预算
已有历史 1followUp21/48累计用量超预算
已有历史 2followUp28/48累计用量超预算
已有历史 2steer48/48累计用量超预算
接口协商 2steer48/48完成
接口协商 2followUp48/48输出超预算
保留正式运行顺序。48/48 的代码仍可能超出预算;这里不把它算作预算内完成。

先确认实验跑对,再谈模型做得怎样

三轮里,也有属于实验自身的失败。

历史恢复副本曾被放进错误目录,被生产路径检查拒绝。修正位置、验证真实恢复后,才继续使用同一份历史。第二轮最后一次单会话又遇到合盖休眠;系统日志确认环境中断,我们保留记录,只重跑这个无效尝试,没有重跑其他正常预算失败的样本。

第一轮还有测试服务未正常退出,以及在运行彻底结束前修改报告脚本、触发源码保护的问题。此后我们要求整个运行和清理结束前冻结 checkout,连报告脚本也不动。

这些记录需要与模型的失败分开。否则,环境中断可能被算成模型失败,人工修正实验的过程也可能从最终成绩里消失。

通过全部检查,能说明多少?

第一轮已经出现过评分器误判。到了第三轮,即使代码通过全部检查,我们也还需要回头看:已有检查漏掉了哪些行为?

我们又做了一次事后补充审计。没有让模型重跑题目,而是先审查需求与已有覆盖,写定 12 项边界组合。比如,无效请求会不会误伤待完成授权;不同工作区的操作回执在重启后是否仍然隔离;响应丢失后又建立了新连接,旧请求重试会不会把它断开。

检查先经过参考实现和四个故意破坏的版本校准:参考实现通过 12 项,四个错误版本都被对应检查抓住。然后对第二、三轮全部 16 份保存产物的副本统一复验,原文件与副本前后哈希都没有变化。

原先通过全部 48 项检查的 10 份产物,这次全部通过新增 12 项。其余六份也都保留在审计里:四份接口约定不一致,后端通过 10 项,浏览器两项无法执行;一份缺少约定,12 项均无法执行;还有一份前端接入未对齐,两个浏览器流程实际失败,与原报告的问题一致。

16 份代码,再过一遍 12 项边界检查

每格代表一份保存产物,按结果分组,不表示运行顺序。

全部通过12 通过 · 原先均为 48/48
10
接口不一致10 通过 · 2 阻塞
4
缺少接口约定12 阻塞
1
前端未对齐10 通过 · 2 失败
1
总计 192 个检查位置:170 通过、2 失败、20 阻塞。阻塞来自前置条件缺失,不等于 20 个独立缺陷。两项实际失败对应同一份前端未对齐产物;补测没有修改候选代码。

补测没有找到推翻原先全过结果的新反例,让我们对这些具体边界更有把握。但它不能证明检查已经完整。审查者已知之前结果,这也是事后审计,不是盲测或独立团队认证。[4]

我们没有人工修好候选代码再把它算作模型成功,但实验设计、评分器校准、故障处理和记录审阅都有人参与。完整的人工检查与返工时间没有统一计量,目前还不能报告生产意义上的端到端交付时间。

回到最初的问题,我们会怎么选?

如果一个新任务同时涉及两个仓库,我们目前更倾向于让一个 session 明确关联两边。尤其当接口还需要改动和协商时,同一个上下文可以省去一部分跨会话确认。这是结合实验作出的产品判断,不是普遍最优方案的证明。

如果前后端已经各有正在使用的会话,保留历史、继续分工仍有价值。但消息协作不能只做到“发送成功”:我们需要知道它还在排队、已经进入对方上下文,还是已经得到明确回复。第三轮也提醒我们,即使消息更早进入,双方仍可能反复确认、耗尽预算。

这些判断的范围很小。每轮只有四对,第一、二轮之间题目、预算和提示版本也有变化,不能合成一张总榜;所谓已有历史,来自先前维护任务,并非数月自然使用。第二、三轮各有一个双会话样本借用了宿主 Playwright 自测,偏离纯内置依赖前提,结果照样保留并披露。强制握手或指定协调者是否更有效,这几轮也没有测。

我们开始时想比较的是:一个 Agent 做,还是两个 Agent 分头做。真正跑下来,才发现“两个 Agent 能互相发消息”还不是一个足够完整的实验条件。

消息到了哪里、什么时候到、双方最后有没有据此改对代码,都得沿着记录看一遍。

方法与数据

[1] 第一轮:明确接口;包含新会话和预置交接历史。两处显示断言更正,八份原产物统一复验。

[2] 第二轮:接口协商与真实维护历史;四对、八次。预算失败保留,环境无效尝试另记。

[3] 第三轮:两组均为双会话,复用起始快照,只比较 followUp 与 steer。

[4] 补充审计:16 份保存产物、12 项新增检查;170 项通过、2 项失败、20 项受前置条件阻塞。

实验范围与可复核结果

24 次正式模型运行,每轮 8 次;补充审计复验其中 16 份保存产物,没有新增模型运行。GPT-6.1 Sol · medium · pi 1.4.0。消息桥经过生产 prompt、parser 和生命周期,但仍是实验适配器,不代表完整消息产品已经上线。

第一轮共用预算为 480 秒、120 万累计报告 tokens、2 万输出 tokens、24 条消息;后两轮为 600 秒、160 万累计报告 tokens、2.4 万输出 tokens、32 条消息。累计报告用量含缓存读取,不等于费用。第三轮复用第二轮的起始快照,不是接着第二轮失败代码修改。

第三轮按四对交替安排:followUp → steer,steer → followUp,followUp → steer,steer → followUp。前两轮的完整通过记录、第三轮的失败和未投递消息都保留。

下方数据为经过筛选的逐次结果与方法摘要,未公开原始会话、认证资料或本机路径。完整原始记录在本地保留;公开汇总不能替代原始 trace 审查。

下载三轮逐次结果与补充审计摘要(JSON)↓

AI 辅助起草。每轮只有四对;本文是工程实验记录,不是通用能力排名。