Agent 的文件冲突处理:用户和 Agent 同时编辑同一个文件怎么办

有一阵子,我的工作节奏是这样的:Agent 在左边的终端里改代码,我在右边编辑器里改同一个文件。两边都觉得自己拥有这个文件。直到一次我按下保存,屏幕弹出一句 File has been modified by another process,我才发现,我十分钟的改动被覆盖了。

AI technology illustration

这事让我困惑了很久。按理说,并发编辑是个老问题。数据库有事务,Git 有合并,Google Docs 有实时协同。为什么一个如此智能的 Agent,却像在裸奔?

后来我去翻各种编程 Agent 的设计思路,才慢慢想通:大多数 Agent 处理并发冲突的方式,比你以为的原始得多。

文件系统没有“锁”,也没有“事务”

先看一个基本事实:Agent 和你修改的是同一个文件系统的同一个文件。文件系统提供的基本操作是 读一个文件,写一个文件,但它不知道“编辑事务”是什么。它没有数据库里那种行级锁,也没有给文件加版本号。文件锁可以做到独占访问,但大多数编程 Agent 根本不用它,因为锁会挡住用户的正常编辑,而且容易死锁。

因此,当两个人(人或 AI)同时写同一个文件时,最终的输赢取决于写入顺序:后写的覆盖先写的。这里没有协商,没有合并,只有覆盖。

Agent 分两类,冲突风险完全不同

不是所有 Agent 都有同样的冲突问题。我在实践中观察到,编程 Agent 大体分三类:

类型 工作方式 冲突风险 典型代表
建议型 生成代码块或补丁,由用户手动接受 低 —— 用户有最终决定权 GitHub Copilot
直接写入型 自己读文件、改文件、写磁盘 高 —— 可能覆盖用户未保存的缓冲区 Cline、Claude Code、Codex CLI
IDE 协作型 Agent 的修改作为编辑器内变更展示,用户确认后应用 中 —— 借助编辑器的 diff 和 undo 机制降低风险 Cursor 的 Apply

最容易引起你问“怎么办”的就是第二类,直接写入型。它有能力在你不知情的时候改变磁盘上的文件。

直接写入型 Agent 做了哪些检查

我最早以为,Agent 会像 Git 一样先记录现状,写完以后做一次 merge。后来看了一些直接写入型 Agent 的输出日志和报错提示,才发现真实做法要朴素得多。它采用的是乐观并发控制:假设用户在 Agent 运行期间不会碰文件,只在最后写入前做一次校验。典型流程是这样的:

  1. Agent 读取文件内容,并记录下当前内容的哈希(或修改时间)。
  2. Agent 基于这份快照生成新内容。这个过程可能需要几秒到几分钟。
  3. 写入前,Agent 重新读取一次文件,并与之前记录的哈希做比较。
  4. 如果哈希一致,Agent 认为没人动过,直接写入新内容。
  5. 如果哈希不一致,Agent 报错、跳过,或提示你手动处理。

这个模式和数据库里的 乐观并发控制很像:不提前加锁,只在提交时检查版本。听上去还挺合理?但问题是,文件系统不像数据库那样维护版本号。Agent 只能用内容哈希或 mtime 来近似“版本”。而这两个“版本号”自己都不可靠。

为什么检查过了,你还是会丢代码

我遇到的情况就是典型例子。Agent 在文件系统级别写文件,而我改的内容还停在我的编辑器缓冲区里,没有落到磁盘。Agent 读取磁盘时看不到我的改动,校验哈希时也看不到,等它写完,我的缓冲区还停留在旧版本。然后我按下保存——我把自己那版内容写回磁盘,又把 Agent 的改动覆盖了。

换句话说,冲突不是同时发生,而是双方在完全不同的层级里工作:Agent 在磁盘上写,人在编辑器内存里改。Agent 并不知道你正在编辑一个未保存的文档,而你的编辑器也不知道磁盘在 Agent 出手的那一刻已经变了。于是两边互相覆盖,而且都不知道自己干了什么。

如果编辑器足够智能,你可能会看到“文件已在外部被修改”的提示,但大多数情况下你只会在保存之后发现不对。这已经晚了。

终极解法:让 Agent 和用户共享同一个“文档状态”

你一定想问,为什么不用协同编辑技术?Google Docs 里两个人同时打字也不会互相覆盖。这是因为它们用了 OTCRDT 这类算法:每个参与者编辑的不是磁盘上的文件,而是一个共享的文档模型,所有改动通过协议同步,并在合并时解决冲突。

如果 Agent 也接入编辑器的文档模型,而不是直接改文件系统,它就能像另一个协作者一样产生可合并的操作。这正是 IDE 协作型 Agent 做的事。但直接写入型 Agent 绕过了这一层。它面对的只是一个路径和一个 write_file 函数,它根本不知道自己写的是不是最新的。

所以,CRDT 和 OT 并没有过时,只是大多数 Agent 没有采用它们。它们把冲突处理的难题留给了 Git 和你的保存键。

给同时编辑场景的三条实用建议

理解原理之后,我现在的习惯是这样的:

  • 让 Agent 只负责你当前不碰的目录或模块。给它明确的任务范围,别让它大范围“重构”。
  • Agent 运行时,不要打开它正在改的文件。如果它跑得久,就切到别的分支,或者在旁边等它完成再检查 diff。
  • 真被覆盖了,不要慌。用 git reflog 找回之前的提交,或者用编辑器的本地历史(Local History)找回未提交的内容。你真正的保镖不是 Agent 的智能,而是 Git 的多分支和你的撤销缓存。

这三条只能降低概率,不能消灭冲突。如果你的工作流里“人机同时编辑同一个文件”是家常便饭,那就真的需要考虑换成 IDE 协作型 Agent,或者让 Agent 把修改写到独立文件,再通过人工 review 合并。

为什么 Git 不能实时帮你自动合并?

Git 只在你执行 commit、merge 时对比内容。Agent 直接写文件时,Git 根本不知道。你后来 commit 时,工作区里可能已经混入了两个人的改动,Git 只能基于行级 diff 做合并,而真实意图已经丢失。所以 Git 是事后的救援,不是事中的保险。

最后说句实话

Agent 的冲突处理能力,远没有你想象中那么智能。它不是面向并发设计的,它是面向“单写者”设计的。它把用户当成一个不存在的旁观者,把文件系统当成一块只能由它涂改的画布。这种设计让 Agent 可以脱离编辑器独立运行,但也让“用户和 Agent 同时编辑同一文件”成为一个不可能完全解决的问题。

未来,随着 Agent 深度集成进 IDE,并采用 CRDT 这类协同模型,冲突会减少。但在那之前,请把 Git 提交做勤一点,把重要改动尽早保存。因为等到两个“作者”同时按下最后一个键的时候,赢的是最后落盘的那个人,而不是改得对的那个人。

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/786.html

(0)
上一篇 3小时前
下一篇 3小时前

相关推荐