工作区可以是文件,账本必须是数据库
五月份给一个 Agent 系统做存储选型,团队里结结实实吵了两周。
一派的论据是「Agent 天然爱文件」:你看它操作目录、grep、diff 多顺手,全上 Markdown + Git,人能看、Agent 能改、还自带版本历史。另一派反驳:「都 2026 年了还拿文件系统当数据库使?并发怎么办?约束怎么办?」两边都有道理,谁也说服不了谁。
最后让我们达成一致的不是道理,是一次翻车。
那次翻车
我们有个后台任务编排的原型,图快,任务状态直接用 YAML 文件管理,Git 兜底。每个任务一个文件,字段大概是 status: pending/running/done,Agent 和人都可以改。
跑了三周出事了。一次并发场景下,两个 Agent 几乎同时更新同一个任务的不同字段,后写的把先写的覆盖了——文件写入没有行级的概念,整个文件替换。更阴的是第二个问题:某次 Agent 更新状态时打错了字,写成 status: runing。没有 schema 校验,这个错误值安安静静地躺了四天,下游一个按 running 过滤的统计脚本一直漏算它,直到有人对不上数才被翻出来。
Git 忠实地记录了这两次事故的完整历史。但 Git 只负责记录,不负责阻止——版本控制不是约束系统。这句话现在贴在我们的设计文档模板里。
想明白:Agent 到底「爱」文件的什么
翻车之后重新复盘那场争论,我发现双方其实都对,只是说的不是同一层东西。
「Agent 爱文件」这个观察是真的,但要说准确一点:coding agent 是 file-native 的。它的整个工作闭环——读(cat/grep)、改(编辑工具)、看变更(diff)、审阅(人读 Markdown 成本极低)、回滚(git revert)——每一环都建立在文件语义上,而且每一环对人和模型同时透明。这个闭环数据库给不了:给 Agent 一套 CRUD API,它要先学你的 schema,人要审阅只能看查询结果,回滚要靠你自己写补偿逻辑。
所以草稿、计划、中间产物、代码——这些工作区内容放文件系统,是真理。
但我们翻车翻在把这个结论外推到了它不该去的地方。任务状态是什么?是多方并发读写、有 schema、有一致性要求、错了有真实业务后果的东西——这是账本,不是草稿。账本要的四样东西,文件加 Git 一样都给不了:
- 约束。schema 校验、枚举值、非空、唯一性。
runing那种错,数据库在写入时就会拒绝。 - 并发控制。行级锁三十年前就是成熟技术,文件系统的答案是「整个文件覆盖,后写赢」。
- 业务审计。Git log 记录的是「文件怎么变的」,审计要的是「谁以什么权限做了什么业务动作」,粒度错位。
- 防误改。Markdown 对人和 Agent 都敞开着,这是工作区的优点、账本的死穴。
翻车后我们把任务状态迁进了 SQLite(就这个体量,够了),YAML 只留作 Agent 的工作草稿。迁移完那天我在群里发了那句总结,后来成了我们的选型口诀:工作区可以是文件,账本必须是数据库。
那条容易被忽视的「毕业线」
口诀好记,实操中真正难的是识别状态什么时候毕业——从「工作草稿」变成「业务事实」的那条线。
草稿的特征:可丢弃、可重建、错了能改、只有产生它的会话关心它。事实的特征:有下游依赖它做决定、错了有真实后果、多方并发访问。一条状态跨过这条线的时刻,就是它该搬进数据库的时刻。
我们的翻车本质上就是没看见毕业线:任务状态在原型期确实是草稿(就一个 Agent 玩,错了重跑),但接入编排、有了下游统计之后,它已经悄悄毕业成事实了,存储却没跟着升级。每个拿文件系统当数据库用的系统,都欠着这样一笔账,区别只是什么时候翻车、翻多大。
迁移之后:Agent 怎么访问账本
把任务状态迁进 SQLite 之后,马上遇到第二个设计题:Agent 怎么读写这个账本?直接给它 SQL 权限吗?
我们最后的方案是读写不对称:读,给 Agent 一个只读的查询工具,随便查(只读连接 + 超时限制,查坏了也就是慢);写,不给 SQL,只给一组语义化的操作接口——claim_task、complete_task、report_failure,每个接口内部带着状态机校验(pending 才能 claim、running 才能 complete)。
为什么写路径要包一层?还是那个原则:机制能拦的错误不要靠约定。让 Agent 直接 UPDATE,哪怕提示词里把状态机写得再清楚,它总有一定概率写出非法状态迁移——概率乘以调用次数就是必然。包成接口后,非法迁移在接口层直接被拒,Agent 收到明确报错还能自我纠正。上线三个月,接口层拦截日志里有十几次非法迁移尝试——每一次都是没包接口时的一次静默数据损坏。
这个「读宽写窄」的不对称,现在成了我们所有 Agent 访问账本类数据的默认姿势。它其实就是老掉牙的最小权限原则,只是执行主体从人变成了模型——而模型比人更需要这层保护,因为它犯错不知疲倦。
夹在中间的:记忆和知识库
工作区和账本之间还有一块灰色地带——Agent 记忆、个人知识库这类东西。它们的需求很拧巴:要人类可编辑(Markdown 的强项),又要机器可检索(数据库的强项),还要长期演化。
我自己的知识库现在是纯 Markdown 三层结构:原始材料层(网页剪藏、导入的文档)、编译层(提炼出的概念页和实体页,靠双链互引)、索引层(一份 catalog 文件)。没有数据库。这不是懒,是评估过的决策:它至今没出现过需要事务的场景——单人写入、错了能改、没有下游系统依赖它的「正确性」。不引入 ledger 也是一个需要主动做的决策,条件变了(多人并发、引用完整性要求、权限)再升级不迟。
一点展望
往后看,我不觉得结局是文件和数据库谁吃掉谁。更可能的形态是数据库把自己伪装成文件树:对 Agent 暴露路径、diff、类 commit 操作——它的母语;内部保留事务、索引、约束、审计——数据库的本分;对人保留可读文本。
这个思路其实很老了,Plan 9 的「一切皆文件」、FUSE 把对象存储挂成目录,都是同一个方向。新鲜的是需求侧:世界上第一次出现了数以亿计的、以文件操作为母语的「用户」,而且他们的数量还在暴涨。有足够大的需求,接口层的进化会很快。已经能看到一些数据库产品在往这个方向做实验了,值得保持关注。
在那个理想接口出现之前,先记住口诀,以及口诀背后那次 runing 的教训。