先讲三件真事,都发生在最近一周,同一台机器上。

事故一:构建号撞车。 两个会话并行处理同一个 iOS 应用,A 会话上传了 build 17,B 会话不知道,也构建了一个 build 17 往上传——上传工具还报了个”重复构建号”的错误火上浇油。最后排查发现两个 17 内容一致,虚惊一场,但如果内容不一致呢?

事故二:测试清了开发库。 一个会话跑测试套件,测试配置和开发数据库指向同一个文件,测试启动时重建数据库,把几百 MB 的开发数据清成了 0 字节。万幸那是弃用的测试数据,万幸不是。

事故三:署名被覆盖。 我这边批量更新 93 篇文章的署名,另一个会话同时在做站点的署名展示优化——两边都在动”署名”这个概念,靠文件不相交才没互相踩掉提交。

三个事故的共同点:多个 AI 会话共享同一份仓库、同一套服务、同一个构建号水位,而每个会话默认”世界里只有自己”。人类团队花了几十年解决多人协作问题,现在要把这些规矩提前搬给 AI——它们比人类同事快得多、便宜得多,也任性得多。

先说一个正面案例定调:就在写这篇的前一天,我的博客上同时活跃着两个会话——一个在发插件系列文章,一个在改站点的署名展示,外加我这边跑完了一轮 93 篇的批量翻新。三方零冲突,靠的不是运气,是下面这些规矩。

规则一:接手先读状态

所有治理规则里性价比最高的一条:动手前,先读这个领域当前的状态。

落到具体动作:接仓库先 git log,看有没有别的会话刚提交过;碰发布先查构建列表,看构建号水位到几了;接批量任务先读进度标记文件,看哪些条目已经完成。这条规则的成本是几分钟,收益是避免整轮返工。

进度标记值得单独表扬。批量翻新一轮里,试跑阶段先做完的两篇文章恰好也在某个批次清单里——批次 agent 开工前读标记文件,发现已完成就跳过,没有重做。标记文件是会话之间的交接物,同类的还有项目里的迭代日志和待办池:我曾靠一份迭代日志完整重建了一个项目的提审时间线,不用问任何”当事人”。会话会消失,交接物要留下。

事故一的完整教训就是这个:如果 B 会话上传前先拉一次构建列表,撞号根本不会发生。工具报 DUPLICATE 时也不该急着重传——报错先查列表再信工具输出,那次”错误”背后包其实已经在云端 VALID 状态躺着了。

规则二:文件集切分

并行的会话(或并行的子 agent)必须各自拥有不相交的文件集。我的博客翻新跑了 10 个批次,每个批次领一份互不重叠的文章清单,写正文、写配图、写标注数据——文件级无交集,天然无冲突。

但文件切分有个隐藏的边界:它只保证”写入不打架”,不保证”结果一致”。十个批次各自写各自的文章,谁来保证全站图片引用完整、失效旧图被清理、署名统一?答案是收尾必须单线程:一个收尾脚本在全批次结束后跑一遍,做集合差、做全量重渲、做统一替换。并发出生产力,串行收口才出一致性。

规则三:共享资源指定唯一写者

文件能切分,有些东西切不了:站点配置、全局署名、构建号水位、数据库 schema。这类全局态的规矩是同一时刻只允许一个写者——要么排期错开,要么明确归属。

事故三里两边没打架,纯粹是运气好(一个只动文章文件,一个只动主题配置)。但凡两个会话都需要改同一份配置,后提交的会无脑覆盖先提交的——git 不会替你做语义合并,它只看行。配置冲突的代价不是回滚,是静默丢失。

规则四:测试与开发数据物理隔离

事故二的修复方案值得抄:应用启动支持环境变量覆盖数据库路径,测试框架强制把测试库指到独立文件。从此测试怎么折腾,开发数据都活着。

这类问题的狡猾之处在于它平时不发作——共用一个文件几个月都没事,直到某天测试逻辑变成”重建数据库”。共享资源的风险不是常数,是随时会突变的;隔离的成本是一次配置,收益是一劳永逸,这买卖没有不做的道理。

规则五:走的时候打扫干净

并行会话还会留下第三种问题:垃圾。我审计博客时在仓库根目录发现两个某会话留下的包管理器占位文件——内容还是模板默认的”set this to true or false”,没人提交、没人删除,就躺在那儿等一个不明真相的后来者误提交。

批量会话的退出礼仪应该包括:临时文件删掉、实验容器清掉、进度标记留存(这个是给别人看的,不是垃圾)。**判断标准很简单:你的会话结束后,一个毫不知情的同事接手这个目录,需要花多少时间才能理解现状?**理解成本越低,打扫得越干净。

写在最后

这五条规则没有一条是新的——它们全是从人类团队协作里搬来的:先同步状态、划分职责边界、全局资源加锁、环境隔离、交接留文档。AI 会话没有改变协作问题的本质,只是把协作频率提高了一个数量级:人类同事一天提一次交,AI 会话一分钟就能撞三次。

频率越高,治理越要先行。这台 Mac 上的会话还会越来越多,规矩得先立好。

三次事故的完整复盘

开头那三件事值得展开,因为每个的处置里都藏着一条通用规程。

撞号那次,时间线是这样的:会话 A 上传 build 17 成功(App Store Connect 里状态已是 VALID),会话 B 不知情,本地也构建出一个 17 往上传——上传工具这时报”重复构建号”,火上浇油的是这个报错本身还是竞态误报。最后两边核对发现包内容一致,以云端为准、没有重传,收场。沉淀出两条规程:上传报错先查构建列表再信工具输出;接手发布类任务先查一次”水位”(最新构建号、最新提交)。

清库那次根因链条值得细看:测试框架的初始化配置与应用开发库指向了同一个数据库文件,测试一启动就按配置重建库——开发数据被清成 0 字节。修复是三层的:应用支持用环境变量覆盖数据库路径;测试配置强制指向独立的测试库;并要求任何”测试与真实数据共用资源”的写法在评审时按事故对待。万幸被清的是弃用的测试账号,但那次运气不能算进流程里。

署名共存那次则是正面案例:同一天里一个会话发插件文章、一个会话改站点配置、我这边跑完 93 篇批量翻新,三方零冲突——文件集不相交是前提,进度标记文件是交接物,最后由单线程的收尾脚本统一做全站一致性。

交接物的标准可以参考渐识项目的做法:一份”项目状态”文档,开头第一句就是”读完本文即可接手项目,无需重新考古”。判断一个会话的交接做得好不好,就看一个毫不知情的新会话要花多少时间才能上手。


本系列:(一)给七个 AI 助手各起了名字 | (二)本文 · 会话治理
相关:无人值守系统的故障复盘,见三条推送线集体静默:一次 cron 简报系统的排障。