浏览器端 Agent 的文件版本管理:不用 Git,基于 OPFS + SQLite 自己造了一个

做 CreatorWeave 的时候,有个问题绕不过去:Agent 改了文件,用户怎么知道改了什么?改坏了怎么回滚?

终端里的 Agent 好办------直接改磁盘文件,用户不满意就 git checkout -- .。但 CreatorWeave 跑在浏览器里,没有 git,没有命令行。用户授权了一个本地磁盘目录给 Agent,Agent 要是直接往里写文件,出了事没有后悔药。

我试过 isomorphic-git------浏览器端的 git 实现。测了一下,一个普通项目做 git status,Node.js 环境下 200ms,浏览器 OPFS 环境2 分钟。600 倍的差距。而且 isomorphic-git 对接 OPFS 的性能表现完全不能接受,慢到你怀疑它是不是卡死了。

更要命的是------普通用户的文件根本不是 git 仓库。你给他装一个完整的 git 实现,纯属杀鸡用牛刀。

所以我决定不装了。不用 git,自己造一个轻量的文件版本管理系统,专门给浏览器端的 Agent 用。

核心思路:OPFS 是沙盒,磁盘是真实世界

先说一个关键决策:Agent 不直接改磁盘文件。

Agent 的所有文件操作都在 OPFS(Origin Private File System)里完成------浏览器提供的私有文件系统,和用户磁盘完全隔离。用户审批了才同步到磁盘。

arduino 复制代码
本地磁盘目录(用户授权的)   ← 真实世界,用户说了算
       ↕ 审批同步
OPFS files/(Agent 草稿区) ← Agent 随便搞,改坏了不疼
OPFS .baseline/(原始快照)  ← "收据",记录改了什么

这三层存储各有各的职责:

存储位置 干什么 谁说了算
本地磁盘 File System Access API 用户的真实文件 用户
OPFS files/ 浏览器私有文件系统 Agent 的工作草稿 Agent
OPFS .baseline/ 浏览器私有文件系统 修改前的原始快照 系统

Agent 在 files/ 里随便搞------读、写、改、删,都没问题。改坏了?files/ 里有冲突标记,或者直接从 .baseline/ 回滚。用户看着没问题了,审批同步到磁盘。

这个设计从采用 OPFS 的第一天就确定了。因为我一开始就知道:浏览器端的 Agent 不能 YOLO,必须有"后悔药"。

.baseline------变更审批的"原始收据"

.baseline/ 这个设计解决一个核心问题:怎么知道 Agent 到底改了什么?

Agent 修改文件的流程是这样的:

markdown 复制代码
1. Agent 读取 src/main.ts(从磁盘或 OPFS 缓存)
2. 第一次修改时:
   - 把原始内容存到 .baseline/src/main.ts(快照)
   - 把新内容写到 files/src/main.ts(草稿)
3. 后续修改:
   - .baseline 保持不变(保留最原始的版本)
   - 只更新 files/
4. 用户审批时:
   - diff(.baseline, files) = Agent 的真实改动
   - 用户看到的 diff 就是"Agent 到底改了什么"
5. 同步到磁盘后:
   - 清理 .baseline 和 pending 记录

这个流程有几个实际踩坑后才加的细节。

Ghost Dedup:改了又改回去了

Agent 经常干一件事:读取文件 → 修改 → 写回。但有时候 LLM 犹豫了,改了又改回去了------新内容和原始文件一模一样。

这时候如果还标记为"有变更",用户审批时看到的 diff 就是空的,一脸懵。

所以在写入之前,我加了一层检测:

typescript 复制代码
// 写入前检测:新内容 == baseline 内容?
if (!isNewFile && baselineContent !== null) {
  const contentsMatch = await this.areFileContentsEqual(baselineContent, content)
  if (contentsMatch) {
    // 清理 pending 记录和 baseline 快照
    await this.pendingManager.removeByPath(normalizedPath)
    await this.deleteFromBaselineDirIfExists(normalizedPath)
    return // 跳过写入,不产生虚假变更
  }
}

这个检测用的是 Uint32Array 批量比较------把逐字节比较提升为 4 字节批量比较,大文件约 4 倍加速。

readFile 的多层决策树

读取文件时,三套存储的优先级不是固定的,而是根据上下文动态决策:

scss 复制代码
readFile(path)
│
├── preferNative? → 直接读磁盘文件
│
├── 有 pending 变更?
│   ├── OPFS 内容有冲突标记 → 返回 OPFS 内容(让 Agent 看到冲突)
│   ├── 磁盘 mtime > baseline mtime?
│   │   ├── 磁盘内容 == baseline → 迁移场景,保留 OPFS 草稿
│   │   └── 磁盘内容 != baseline → 真冲突,返回磁盘内容
│   └── 无冲突 → 返回 OPFS 草稿内容
│
├── 无 pending,非 preferOpfs → 读磁盘文件
│
└── 兜底 → 读 OPFS files/

最复杂的场景是:Agent 改了文件 A,但与此同时用户在编辑器里也改了文件 A。这时候怎么处理?

答案是 Git 风格的冲突标记------用行级 diff 生成冲突块:

markdown 复制代码
<<<<<<< OPFS
Agent 改的内容
=======
用户改的内容
>>>>>>> DISK

但不是整个文件标记为冲突------只在实际差异的行标记,公共行保持不变。这样 Agent 能看懂冲突,用户也能看懂。

冲突让 Agent 来解

两个工作区改了同一个文件怎么办?我不会让用户自己去解 <<<<<<< OPFS 这种东西。

标记冲突后,直接扔给 Agent。Agent 看得懂冲突标记,它会分析两边的内容,生成一个合并后的版本。用户只需要审批最终结果。

这比 git 的冲突解决体验好多了------git 要你自己开编辑器手动合并,我的方案是 Agent 帮你合并,你只管点头或摇头。

多工作区并行------灵感来自 vibe-kanban

有了沙盒和审批,下一个问题来了:能不能让多个 Agent 同时工作?

我之前用过 vibe-kanban------一个用 Kanban 看板编排 AI Agent 的工具。它的核心设计是 git worktree:每个 workspace 给 Agent 一个独立的 git 分支 + 工作目录,多个 Agent 并行互不冲突。

这和我想要的效果一样。但浏览器端没有 git worktree。怎么办?

用 OPFS workspace 替代 git worktree。

markdown 复制代码
一个本地磁盘目录(用户授权的)
    ├── workspace-1/     ← Agent A 在重构代码
    ├── workspace-2/     ← Agent B 在写测试
    └── workspace-3/     ← Agent C 在做 code review

每个 workspace 有独立的:
- files/     ← 自己的草稿区
- .baseline/ ← 自己的快照
- assets/    ← 自己的临时文件
- .subagents/ ← 自己的子 Agent

但共享同一个本地磁盘目录。

日常开发中这种场景太多了:一边让 Agent 重构组件,一边让它写单元测试,一边还让它 review 自己的代码。如果只有一个工作区,Agent 得排队,效率太低。

多工作区并行,各自审批各自的。如果改了同一个文件?冲突标记 + Agent 自动合并。

还能挂载多个本地磁盘目录

后来又加了一个能力:一个项目可以挂载多个本地磁盘目录。

典型的跨项目依赖场景------比如前端项目和后端项目在不同的磁盘目录,Agent 需要同时读写两边的代码才能正确理解依赖关系。一个 CreatorWeave 项目挂载多个根目录,Agent 在不同根目录下工作,路径解析自动路由。

在 SQLite 上构建 Git 语义

OPFS 只管文件存储。Git 语义------status、diff、log、restore------全部构建在 SQLite 之上。

bash 复制代码
fs_changesets 表    ← 快照(相当于 git commit)
fs_ops 表           ← 文件操作(相当于 git 的 index)
fs_snapshot_files 表 ← 快照内容(before/after,用于回滚)

每次审批生成一个 changeset(快照),包含所有被修改文件的 before/after 内容。

在此基础上实现了完整的 Git 命令映射:

Git 命令 CreatorWeave 对应 实现
git status git_status() fs_opsreview_status + status
git diff git_diff() diff .baseline vs files/,支持 stat/numstat/patch
git log git_log() fs_changesets 按时间倒序
git restore --staged git_restore(staged=true) review_status 从 approved 改回 pending
git restore git_restore() discard pending 或从 snapshot 恢复
git commit 用户点击"审批" pending → approved → synced

速度快是因为不走文件系统扫描------所有变更记录都在 SQLite 索引里,毫秒级返回。isomorphic-git 要遍历整个文件树做 diff,而我的方案只需要查几张表。

快照切换的事务性

快照切换(从一个 commit 跳到另一个)实现了类似 Saga 模式的补偿机制:

markdown 复制代码
切换到目标快照:
1. 前进:apply 目标快照
2. 后退:rollback 目标快照
3. 如果中间某个快照回滚失败 → 自动反向补偿之前已成功的快照

这保证了切换的一致性------要么全部成功,要么全部回退到切换前的状态。

和 git 的对比

Git CreatorWeave 的方案
依赖 需要安装 git 纯浏览器,零依赖
仓库初始化 git init 用户授权文件夹就有了
暂存 git add 所有修改自动进入 pending
提交 git commit 用户审批 = commit
回滚 git checkout -- . .baseline/ 恢复
分支 git branch 多 workspace 并行
冲突解决 手动或 merge tool Agent 自动解决
status 速度 isomorphic-git in OPFS 2 分钟 毫秒级(SQLite 索引)
适用场景 开发者、代码仓库 普通用户、任意文件

学到了什么

  1. 不要在浏览器里硬塞终端的设计 --- git 是给开发者用的,普通用户不懂什么叫 commit、branch、merge。浏览器端 Agent 面向的是普通用户,需要一套更直观的方案:改了什么、看不看、同不同意
  2. OPFS 天然是沙盒 --- Agent 改 OPFS 不改磁盘,这个"隔离"是浏览器免费给你的,不用白不用
  3. 冲突不应该让人来解 --- Agent 看得懂冲突标记,让它来合并,用户只管审批结果
  4. 多工作区并行的价值被低估了 --- vibe-kanban 用 git worktree 实现并行,浏览器端用 OPFS workspace 同样能做到,而且更轻
  5. 这套系统的本质是让 Agent 有"外部感知" --- Agent 改了什么、文件从哪里来、到哪里去,全程可追溯。不是版本管理工具,是 Agent 和真实世界之间的缓冲层

🔗 链接:


如果觉得有意思,点个赞让更多人看到。你觉得浏览器端 Agent 还需要哪些 git 特性?rebase?cherry-pick?欢迎评论区聊聊。

相关推荐
梦想的颜色1 小时前
TypeScript 完全指南(下):从类型体操到生产级配置
前端·javascript·typescript
Ricky05532 小时前
CTRL-WORLD:一种用于机器人操控的可控生成世界模型(中美2025年联合研究)
人工智能·机器人·世界模型
jeffer_liu2 小时前
Spring AI 生产级实战:工具调用
java·人工智能·后端·spring·ai编程
阿乔外贸日记2 小时前
2026尼日利亚五项清关政策更新,拉高能源装备进口综合成本
大数据·人工智能·搜索引擎·智能手机·云计算·能源
民乐团扒谱机2 小时前
【AI笔记】短时纯音时长对音高感知偏移效应研究综述
人工智能·笔记
侃谈科技圈2 小时前
破除数据中台落地困境:2026数据治理平台差异化能力与选型决策指南
大数据·人工智能
大象说2 小时前
Python多进程共享队列无报错僵死 120G Nginx访问日志清洗踩坑全记录
人工智能·自然语言处理
Cosolar3 小时前
AutoGen 精通教程:从零到企业级多 Agent 系统架构师
人工智能·后端·面试
甲维斯3 小时前
Claude Code 省钱小妙招!200K和自动压缩
人工智能