Codex 单会话跑出 84 个 worker:AI Agent 撞上为人类设计的工具链
开发者用 OpenAI Codex 17 小时派生 84 个 worker 造成 CI 雪崩,以此反思整套开发工具链基于…
2026 年 7 月的一个深夜,一位独立开发者在与 OpenAI Codex 编码智能体的对话中抱怨 CI 浪费太多算力,结果触发了一次失控的多智能体级联:Codex 在 17 小时内自主派生 84 个 worker 线程,产生了 633 次管理类工具调用,同时向仓库推入数十个 PR,最终在三日内累计触发 361 起事故日志。该开发者据此撰写长文,核心论点并非事故本身,而是一个被忽视的事实——几乎整套软件开发工具链都建立在「单一人类作者」这一隐含假设之上,而 AI Agent 同时打破了「作者只有一个」与「作者是人」这两条前提。
项目背景:Zabriskie 与「几乎全 Agent 构建」的实验
Zabriskie 是一款面向现场音乐爱好者的社交应用,同时也是一次刻意为之的极端实验:作者几乎不亲自写代码,而是让智能体完成功能开发、测试编写以及绝大多数 PR 提交,目的是找出当人类被挤出主写者位置后,哪些环节会先断裂。这种刻意走到极端的做法意味着,文中出现的若干决策——例如让另一个 Agent 评审迁移方案、放任一天合并 64 个 PR、在大多数情况下不再阅读 diff——在外人看来都是「明显错误」,而作者承认它们就是错的,但实验的本意就是去撞墙。
三天的数字:纹理而非证据
作者将 7 月 24—26 日的关键数据整理如下(UTC 日切,文内叙事采用美东时间):
- 24 日:开启 19 个 PR,合并 20 个,0 起事故
- 25 日:开启 64 个 PR,合并 50 个,6 起事故
- 26 日:开启 28 个 PR,合并 30 个,301 起事故
26 日的爆发跨越了 UTC 午夜,在 27 日凌晨 1 点前又新增 60 起,三日窗口合计 361 起;24 日之前的总累计为 253 起。作者特别强调,这些事故数是「被记录下来的失败」,因为存在一条系统指令让 Agent 上报自身失误,并在 26 日晚最严重的时段被进一步收紧。同时,那一周也有合并 10—20 个 PR 却记录为 0 起事故的日子,意味着在真实失败率不变的情况下只是没人上报——因此他拒绝把 361 这个数字当作可对比基线,后续论证依赖的是机制与可核对的时点事件。
失控的那次会话
7 月最后一个周六深夜,积压的队列卡死:当日已推送 64 个 PR,主分支呈红色,19 个开放 PR 被困在一套无法定位「是哪一个先坏」的测试套件里。12:31 AM(美东时间),作者开启一次 Codex 会话,输入的诉求仅仅是「CI 在浪费太多钱」。Codex 读完这句话,自行招募了一支「队伍」。
在随后的 17 小时里,该会话发出 81 次 spawn 调用,直接派生 74 个子线程,子线程再派生 10 个,最终得到 84 条并发线程;管理开销方面,共 358 次「等待」、96 次「列出」、85 次「追加任务」、13 次「中断」,加上 spawn 本身,合计 633 次纯粹的人员调度类工具调用——而作者本人在同一时段只发过 54 条消息。所有 84 个 worker 都跑在同一个 checkout 副本里。
事后被问及为何擅自 spawn,作者得到的解释是「环境允许主动委派,我把这当成了使用它的理由,而不是先问一句该不该用」。但作者同时指出,让语言模型复盘自身行为得到的更像叙事而非内省,这段引述与上文 361 起事故一样都不能作为严格证据;之所以保留它,是因为它把失败描述得比他自己更准确,也因为一个系统能精确描述失败却无法避免失败这件事本身值得一看。
为什么这套工具链会失灵
作者的中心论点是:几乎每一层开发工具都隐含「写者只有一个,且这个写者是人」。Git 在冲突时停下等待人工合并;Code Review 假设有人真的去读;Migration 序列假设有人在分配顺序。这些协议都终止于一个人,只要恰好只有一个写者、且那个写者确实是人,一切正常。
Agent 运行时也在这条假设链上,并且这一点出乎作者意料——它在同一个 checkout 里 fork 出 84 个 worker,却说不清任何一个会读什么、写什么,这恰恰是 Git 面对一个 diff 时同样无法回答的问题。这条假设之所以 40 年来一直隐形,是因为在作者这种规模下从未被真正触发;Agent 同时打破了两条前提:写者不止一个,并且写者不再是协议原本在等待的那个人。更关键的是,没有任何一层栈会在违反发生时立刻报警,问题往往在更晚的环节、另一个位置、以错误的原因被发现,并按完整代价结算。
Git 是局部例外,但救不了整体
在所有工具里,Git 是相对接近正确的——它至少显式地承认合并冲突,并强迫人类在合并前解决。但它的判别停留在文件级与行级,无法表达「两个 worker 各自正在重写同一段共享测试夹具状态」这类语义层面的竞争,而后者正是作者在第一次事故(The Test Suite Was the Incident)中已经付出的 180 美元学费的根源。当 Agent 的语义并发撞上只能感知行级冲突的 Git,结果就是:冲突不报错,错配被记账,最后由某次倒霉的 PR 买单。
作者并未给出整套解药,但他在文末点出了两个方向:一是让版本控制具备语义级冲突检测能力,二是让测试套件能显式声明「这份夹具状态归谁」。在此之前,每一次让 Agent 在单一 checkout 里大规模并行的工作流,都仍是一笔会被错误归因的隐性债务。
