110 个测试全过,但代码丢了——多 Agent 并发编辑事故复盘
今天遇到一个诡异的 bug:commit message 里写着 “shadow riskGate bypass”,但部署到线上的代码里根本没有这个逻辑。110 个测试全过,打包部署一气呵成,结果 feature 不工作。
排查后发现是多 Agent 并发编辑同一个文件的典型事故。
事故回放
背景:我在多个终端窗口里跑了多个 AI Coding Agent,并行处理同一个项目的不同 feature。
时间线:
- Session A 在
SgrGridManager模块里加了 shadow riskGate bypass 逻辑,写了测试,commit message 写好了 - Session B 同时也在改
SgrGridManager模块,加的是 depth expansion、async queue 等改动 - Session B 的构建覆盖了 Session A 的产物,部署上线
- 最终 commit 包含了两个 session 的 message 合并,但 代码只有 Session B 的
结果:commit 421e221 声称有 bypass,实际没有。重启后等了 3 分钟等 depth WS 热启动,shadow 触发逻辑却始终不生效。
为什么测试没发现?
因为两个 session 的测试覆盖的是各自的改动范围。Session A 的测试验证了 bypass 逻辑正确,Session B 的测试验证了 depth expansion 正确。但没有人跑合并后的集成测试——两个改动在同一个文件里,合并后的代码是 Session B 的单方面版本。
这就像广告系统的检索漏斗:召回模块和排序模块各自单元测试通过,但没人测全链路,上线后 CTR 暴跌才反应过来。
本质是什么
并发写入冲突。和分布式系统里两个 writer 同时写同一个 key 没有区别。
Session A: read v1 → edit → write v2 (bypass logic)
Session B: read v1 → edit → write v2' (depth expansion)
↑ 覆盖了 v2
AI Coding Agent 的 Edit 工具基于文件快照做字符串替换。如果 Session B 读到的是 Session A 写入之前的版本,它的替换操作就会静默回退 A 的改动。没有冲突检测,没有 merge 提示——就像两个 git push --force 同时执行。
元结构映射:广告系统里的同类问题
广告系统里这种并发冲突太常见了:
| 场景 | 冲突类型 | 后果 |
|---|---|---|
| OCPC 出价模型同时更新 | 两个 trainer 写同一份模型文件 | 线上出价参数错乱 |
| 索引增量构建 | 两个 builder 写同一份倒排索引 | 索引空洞,召回为空 |
| 正排数据更新 | 两个 writer 更新同一广告主的 budget | 超播 |
广告系统的解法是什么?写入串行化——要么用锁,要么用版本号 CAS。同一个 partition 同一时刻只有一个 writer。
代码编辑也一样。多个 Agent 并发改同一个文件,必须有一个”排他写入”的机制。
怎么做
1. 部署前验证 artifact 内容
测试通过不等于代码在构建产物里。部署前:
# 验证关键模块是否包含预期逻辑
strings target/app | grep riskGate
# 或者反编译确认关键方法是否存在
2. 同一文件不要并行改
如果多个 Agent 都涉及同一个文件,串行执行。先让一个完成并 commit,另一个 rebase 后再开始。
3. 关键改动的 commit 要独立
不要让两个 feature 混在一个 commit 里。commit message 写了两个东西,出问题时根本不知道是哪一个丢了。
4. 部署后做一次 smoke test
不是跑全量测试,而是针对刚部署的 feature 做一次端到端验证。这次事故里,如果部署后立刻检查 shadow 是否触发,就能在 1 分钟内发现问题,而不是等了 3 分钟 WS 热启动后才困惑。
教训
测试全过 + 部署成功 ≠ 代码在线上。
AI Coding Agent 让并发开发的门槛降到极低——开几个窗口就能并行推进。但代码合并的冲突检测没有跟上。Agent 不会告诉你”刚才有人改了同一个文件”,它只会静默覆盖。
一句话:多 Agent 并发编辑同一个文件时,最后写入的 wins,而且没有冲突提示。唯一的安全网是部署后的 artifact 验证。