跳到主要内容

你的编程智能体忘记检查的分支状态

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的编码智能体并不知道它在哪一个分支上。它以为它知道。十二轮对话前它看过 git status 的输出,它的上下文中有一份 CLAUDE.md 提到了会话开启时的分支名称,并且它观察到一个工具结果列出了五个当时正确的文件。从那时起,智能体就一直在基于那个快照进行静默推理。与此同时,你在另一个终端里运行了 git checkout main。智能体的 diff 顺利地写入了文件系统,因为操作系统并不关心这些字节属于哪个分支。这个 diff 在语义上是错误的,因为智能体对分支的心理模型已经落后了 300 个提交,且它所基于的父节点在你的工作树中已不复存在。

这就是分支状态漂移 (branch-state drift),它是数据库中“读-改-写”竞态 (read-modify-write race) 在编码智能体领域的翻版。智能体在第 N 轮读取世界状态,在第 N+1 到 N+k 轮之间修改计划,并在第 N+k+1 轮写回磁盘——而在这一时间窗口内的某个时刻,它脚下的世界已经发生了变化。没有异常抛出,没有工具返回错误。补丁被应用了。危害在下游显现:针对错误的基准分支开启了 PR,手动提交的代码被静默回滚,或者针对昨天刚迁移过的模式 (schema) 实现了新功能。

为什么文件系统对你们都在撒谎

Git 工作目录是一个投影。磁盘上任何时刻的字节都是最后一次检出的提交与工作树中任何未提交编辑的并集。智能体的工具——ReadEditBashGrep——都在这些字节上操作。它们的结果都不包含父提交信息。它们都不会注解“由于 HEAD 当前是 Y,所以该文件看起来像 X”。智能体在第 4 轮读取文件,并将其内容作为稳定的事实存储在上下文中。到了第 17 轮,那个提交和那个时刻都已不复存在。

同样的投影也会欺骗人类,但人类拥有外围信号——Shell 提示符中的分支、IDE 的状态栏、刚刚输入内容的肌肉记忆。除非有人专门接入这些信号,否则智能体一无所有。智能体观察分支状态的唯一窗口就是其最后一次工具运行的结果。如果该结果已是十二轮之前的,而你在此期间运行了 git pull --rebase,那么智能体就是在基于化石进行操作。

使其比人类犯错更糟糕的是恢复过程的不对称性。在编辑中途意识到自己在错误分支的人,通常能通过编译错误、缺失的导入或看起来不太对劲的名称发现问题。智能体发现不了,因为它对分支“应该长什么样”没有先验认知。智能体对“代码库包含什么”的模型仅限于它在本会话中读取过的内容,而这恰恰就是发生漂移的表面。

漂移的三种形式

分支状态漂移至少以三种结构截然不同的方式出现,且针对一种情况的修复方案并不适用于另一种。

第一种是单会话漂移 (single-session drift):用户在智能体运行期间于另一个终端切换了分支。智能体下一次读取文件时返回的是新分支的内容,但其之前的上下文仍描述着旧分支。智能体在这种矛盾中进行推理,并得出一个两者都不匹配的 diff。

第二种是共享检出下的多智能体漂移 (multi-agent drift on a shared checkout):两个智能体在同一个工作树上运行,其中一个智能体为了执行计划而切换了分支,导致另一个智能体看到的文件系统在其脚下发生了变化。第二个智能体读取到的“未提交更改”实际上是第一个智能体编辑到一半的状态,并试图通过扩展这些更改来提供帮助。由于每个智能体都基于不同的父节点进行推理,合并结果将变得语无论次。

第三种是长时间运行智能体针对移动远程分支的漂移 (long-running-agent drift against a moving remote):智能体分配了一个耗时数小时的任务——重构、迁移或跨数百个文件的扫描——到它完成时,main 分支已经推进了 12 个提交。智能体的 diff 相对其开始时的提交是正确的,但相对其必须合并进去的提交则不再正确。文本合并 (textual merge) 虽然成功,但语义合并 (semantic merge) 却失败了。测试在各自的分支上都能通过,合并后却失败了。

每种形式都有不同的根本原因。单会话漂移源于缺失轮次前奏 (turn-prelude)。多智能体漂移源于缺失隔离原语 (isolation primitive)。长跑漂移源于缺失变基并重新验证 (rebase-and-revalidate) 的步骤。混淆它们会导致修复了一个问题却遗漏了其他问题。

工作树是底线,而非上限

2026 年主流的修复方案与 Git 本身为解决人类并发问题而推出的方案一致:专用工作树 (dedicated worktrees)。每次智能体运行都会获得一个由共享 .git 对象库支持的独立物理目录。两个智能体不会在工作树上发生冲突,因为它们并不共享工作树。在工作树 A 中切换分支不会干扰工作树 B。磁盘成本微乎其微——仅重复工作文件;引用、对象和远程跟踪分支仅存储一份。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates