ZGent 设计之旅 #2
文件与权限模型
王子博
2026 年 4 月 21 日
在中关村人工智能研究院·软件智能研究所,我们在思考并设计下一代 AI agent 的框架、人机界面与生态系统,项目代号为 ZGent。这篇文章是一系列文章中的第二篇,我将会在每篇文章中介绍我们的一个设计理念,以及相应的技术实现。
OpenClaw 的普及让许多人意识到了,为 AI agent 配备运行环境并允许它在其中调用各种程序,比只能对话的 AI 方便、强大很多。这里所说的运行环境一般就是一台可以随意读写文件系统、运行程序代码、并且可以联网的电脑、虚拟机或容器。通过运行程序代码,agent 可以查询各种信息、生成网页、制作 PDF 文件、批量操作文件,甚至对软件开发工作完成从下载现有代码、开发新功能,到提交、部署新版本代码的整个流程。
为了让 agent 能够工作,需要提供很多种文件:
- 一台虚拟机或容器运行所需要的基本系统文件
- 关于用户是谁、做什么、有什么性格偏好的记忆
- 用户允许 agent 使用的各种密钥
- 和工作相关的各种参考资料,例如文档、聊天记录、数据库等
- 如果是软件开发项目,相关源代码、编译器、编译结果等
目前很多系统只能以简单粗放的方式管理这些文件,例如在每次对话时都全部暴露给 agent,并且没有版本记录和撤销改动的能力。我们希望探索出一套功能完善又易于使用的文件与权限模型。
底层:ZFS
上一篇文章中提到,ZGent 中的对话都在云端运行,所有文件与运行环境都在云端,不需要用户在自己的设备上安装与配置。这也让我们能够在云端使用更复杂的技术方案,而不增加用户上手使用 ZGent 的难度。我们选择让云端的服务器运行在 ZFS 文件系统上,并使用 ZFS 的这些功能:
- 可以将任意数量的存储设备合并视为存储池,方便灵活调整存储资源。
- 为每个用户分别在存储池中建立一个 ZFS dataset,实现按用户统计存储用量并设置上限。
- ZFS 具有写时复制(copy-on-write)和去重(dedup)功能,可以瞬间复制大文件,多个相同文件只占一份空间,直到某个副本真的被修改时才存储具体修改的部分。这一功能是等下会讲到的工作区和版本记录功能的基础。
工作区与存储
与其他产品相似,会话(session)是 ZGent 中的重要概念,每个会话有自己独立的对话记录和运行环境。我们将这些运行环境中的文件称为会话的工作区(workspace),每个会话可以随意读写自己的工作区。
总体上,我们认为会话与工作区应当是临时性的、可抛弃的,需要持久保留的数据应当放在存储(storage)中。每个用户有自己的存储空间,存储中的文件和文件夹可以被用户授权复制到各个会话的工作区中,工作区中的修改也可以在用户授权的情况下复制回存储中。
基础镜像与同步点
每个新会话的工作区都由基础镜像和若干同步点构成,基础镜像提供了容器运行所需要的基本系统文件,以及一些已经安装好的软件工具、编译器等,同步点则用来将存储中的若干文件和文件夹复制进工作区。在会话运行过程中,工作区的内容会被不断修改,不影响基础镜像和存储(除非用户授权将同步点的修改复制回存储)。
ZGent 自带一个比较简单的默认基础镜像,用户也可以在会话使用过程中随时将当前工作区保存成新的基础镜像,以便在新对话中重复利用。更成熟的软件开发项目常常会自带 dev container,ZGent 也兼容用它作为基础镜像。
每个同步点都表示用户的存储中的某个文件或文件夹对应到工作区中的某个位置,新对话创建时会复制这些文件到工作区,之后 agent 也可以请求再次从存储复制最新内容到工作区。能否从工作区复制回存储,则取决于这个同步点的权限设置:
- 只读:不能从工作区复制回存储。
- 允许请求写回:agent 可以请求从工作区复制回存储,需要用户确认。
- 允许写回:agent 可以随意从工作区复制回存储。
- 自动写回:每次内容变化后,自动触发从工作区复制回存储。
在会话使用过程中,用户也可以随时创建新的同步点和修改权限,agent 也可以请求将工作区中某个文件或文件夹变成一个新的同步点,复制进存储。
同步冲突
为了避免复制内容时意外覆盖了未保存的数据,尤其是多个会话并行读写存储时,后写入的数据可能覆盖了先写入的数据,我们不为 agent 提供两个单向的复制功能,而是只提供一个双向的“同步”功能(这也是为什么称为同步点)。当 agent 对一个同步点执行同步操作时,根据工作区和存储分别是否发生过变化,有 4 种可能性:
- 都没有发生过变化,什么也不做。
- 只有工作区变了,根据权限设置写回存储。
- 只有存储变了,重新复制到工作区。
- 工作区和存储都变了,这就是发生了冲突。
发生冲突时,不能简单丢弃任何一侧的数据。ZGent 采用“谁后写谁解决冲突”的模型,把两侧的修改都给 agent 看,让它决定实际要写入的内容。多数简单的冲突只需要 agent 自行将两边的修改合并起来即可,但有的修改是难以合并的,agent 可能会选择放弃工作区的进展,在最新版本上重做,或者停下来询问用户。
版本记录
利用 ZFS 的写时复制功能,ZGent 支持对工作区的所有文件保存快照,以及回退到之前的快照。这个快照功能是文件系统层面上的,因此:
- 不依赖 Git 等版本控制工具。
- 包含所有文件,包括 Git 忽略和未追踪的文件。
- 非常高效,并不需要真的复制任何文件内容。
- 有一致性,不同文件,以及每个文件的不同部分,都是在同一个瞬间快照的,不会损坏 SQLite 数据库等文件。
虚拟存储
在 ZGent 的设计中,agent 是看不到存储的,只能读写工作区,以及通过同步点间接读写存储。它甚至无法知道一个同步点的另一端是存储中的哪个路径,只知道自己的工作区这一端的路径。这意味着存储可以被设计得非常灵活,任何能“复制出来”“复制回去”的东西,都可以视为一种虚拟存储。
最基础的虚拟存储类别就是 Git repo,对于这样的同步点,每次同步就是在工作区中相应目录执行提交操作。从用户的视角看,如果配置了合适的新会话模板,其中包含一个 Git repo 虚拟存储的同步点,就可以不断创建新会话,让 agent 开发特定功能,开发完成后写回。
只要能定义“同步”这一操作,各种东西都可以包装成虚拟存储。例如:把钉钉的团队共享文档中的一份 PPT 文件包装成虚拟存储,ZSlide(ZGent 生态中的幻灯片制作工具)就可以在工作区内直接编辑,写回时自动上传回团队共享文档中。
这几天有朋友在问我 ZGent 会不会发布、会不会开源了,会的,会有官方部署的服务器,自己填写 API key 使用,也会开源。但最近仍然在非常快节奏地开发中,暂时不确定发布日期。
从 ZGent 开始能够用来开发 ZGent 的那一天起,团队每天平均从 20 次提交增加到了 130 次提交。我们正在同时开发多个平台的客户端、ZGent 生态中的多个工具,以及打磨各种界面和用户体验细节。我们在把自己重度使用过程中发现的各种问题都作为 issue 记录到 GitLab,并发现大部分 issue 只需要创建新会话、说一句话就能自动解决,非常希望大家也能有这样的开发体验。但是软件开发中确实仍然存在很多 AI 不能替人完成的复杂思考(就像这篇文章中的内容),需要时间来把各种设计做对。
下一篇文章再见!