为人与智能体构建共享工作空间

Perplexity 项目如何让长期工作具备持久性、并发性和可追溯性。

作者Perplexity Engineering

我们最近推出了 项目功能,让人和智能体可以在 Perplexity Computer 中协作完成长期任务。每个项目都建立在一个共享工作空间之上,其文件系统会跨会话、跨智能体、跨用户不断积累上下文。跨越数月的多人协作需要具备持久性、并发性和可追溯性:

  • 持久性:工作成果应当比任何单个智能体会话或沙盒存在得更久,以便同一用户或智能体日后继续推进,也便于任何协作者在此基础上开展工作。
  • 并发性:同一项目中的多位用户可以各自拥有多个智能体并行编辑,共同推进共享状态,而不是相互覆盖。
  • 可追溯性:用户和智能体都应该能够查看历史记录,从而无需重新阅读整个项目,就能了解自上次会话以来谁更改了什么,并在需要时回滚编辑。

这些挑战对软件工程师来说并不陌生。Git 代码托管平台早在几十年前就为满足这些需求而诞生:这是一种托管服务,它在版本控制之上叠加了协作功能,让众多贡献者能够汇聚到唯一可信来源。为项目提供支撑的文件系统,正是这样一个为人与智能体、智能体与智能体之间的协作而打造的 Git 代码托管平台。

同一个项目:交互式智能体、定时智能体以及其他用户的智能体,都汇聚到同一个仓库。

架构

Git 本身已经能够处理持久性、并发性和可追溯性。它根据内容来标识每个文件,因此未更改的文件只存储一次,已保存的副本也永远不会过时。每次提交都会一次性保存所有更新的文件,因此协作者永远不会看到半完成的状态。当两个写入者发生冲突时,落败的一方可以在新版本上重新应用其更改并再次尝试。提交链展示了谁更改了什么,并让恢复早期版本变得轻而易举。智能体不需要的,是为人类打造的代码托管平台中的其余部分,例如审核界面、分支管理和通知。

我们的架构将整个流程放在云端运行,并支持编程之外的各种知识工作。每个 Computer 项目都由一个 Git 仓库提供支撑,该仓库由 5 个组件提供服务:

  • CLI 在每个沙盒中维护本地检出、同步更新,并提交智能体所做的更改。
  • 控制平面负责定位项目仓库,并签发用于访问该仓库的短期凭据。
  • Git 服务负责将仓库数据发送到沙盒、接收沙盒的提交,并决定哪个提交成为最新被接受的提交。
  • 基于 AWS S3 的存储负责持久化保存仓库数据。
  • 下游处理负责从已保存的文档中提取文本和其他有用信息,并在智能体加载或刷新项目时提供给它们。

每个智能体都有自己独立隔离的沙盒。在沙盒内部,它使用由一个小型 CLI 管理的仓库本地副本工作。CLI 请求控制平面定位仓库,然后通过 Git 服务获取和提交更改。智能体代理会在沙盒请求到达 Git 服务之前检查访问权限。Git 服务在共享文件系统上读写仓库,并决定每个提交是否成为最新被接受的提交。该文件系统由 AWS S3Files 提供,它让基于 S3 的存储可以像普通文件和目录一样访问。存储被划分为相互隔离的单元,每个单元都有自己的文件系统和 Git 服务副本。提交被接受后,后台工作进程会从发生更改的文档中提取文本和其他有用信息,将结果与仓库历史分开存储,并在智能体加载或刷新项目时提供给它们。

接下来,我们将逐一介绍这些组件。

CLI 联系控制平面以定位其仓库,然后经由智能体代理,通过 Git 服务进行获取和提交。Git 服务检查提交内容,并更新 DynamoDB 中被接受的 head 和发件箱。单元内的副本使用 S3Files 存储,而下游工作进程负责提取文本,供检出和同步使用。

CLI:在沙盒中处理本地文件

智能体运行在我们自研的沙盒平台 SPACE 中。每个沙盒都有一个小型 CLI,负责将仓库落地并保持同步。智能体每完成一个有意义的工作单元,CLI 就会将这些更改作为一次提交进行提交。

智能体通常只会接触项目文件中的一小部分,因此没有必要预先将整个仓库完整落地。为此,CLI 采用延迟落地的方式:通过 Git 大文件存储(LFS)保存的大文件和二进制文件,会先以小型指针文件的形式出现,其中记录了对象的真实大小,这样智能体就可以根据名称和大小来判断是否值得下载。对于 PDF 等文档,智能体会先读取解析后的文本,只在必要时才下载源文件。

另一个顺理成章的选择是使用虚拟文件系统。我们测试了一个基于 FUSE 的实现,但在文件系统密集型工作负载上,它比本地文件慢约 400 倍,因为每个请求都需要一次网络往返。此外,它通常需要主机授予特权能力,这意味着每个智能体都必须在特权沙盒中运行。

由指针大小和解析清单决定:读取解析后的文本、下载源文件,或者跳过。

控制平面:定位仓库并授予访问权限

控制平面负责定位每个仓库的存储位置并授予访问权限。创建仓库时,控制平面会将其分配到一个存储单元——这是一个隔离的存储单位,拥有自己的 Git 服务副本。当智能体开始处理某个项目时,CLI 会向控制平面询问仓库的存储位置。控制平面返回该位置以及访问 Git 服务的临时凭据。控制平面从不读取或写入仓库数据,由 Git 服务决定哪些内容可以进入仓库。

Git 服务:传输数据并协调更新

Git 服务负责执行 Git 操作,并在沙盒与存储之间传输仓库内容和版本历史。它是对 Git 自身服务命令的一层轻量封装。它支持用于告知客户端仓库 head 位置的引用发现、用于克隆和获取的 upload-pack,以及用于接收新对象和推进仓库 head 请求的 receive-pack。它还提供 Git LFS API:对于大文件和二进制文件,它会签发短期链接,让沙盒可以直接从 S3 上传和下载这些文件。

我们一开始使用的是现成的代码托管服务器,这样就可以在构建我们自己的轻量级代码托管平台的同时,并行开发智能体和用户界面的原型;我们自己的平台只专注于我们需要的 Git 操作。

该服务由一组副本组成,每个副本都是同一服务器的一个运行实例。一个单元将共享存储与一组副本及其配套服务组合在一起。这样,同一单元内的副本就可以访问同一个共享文件系统,而无需为每个仓库各自维护独立副本。

这种共享存储让副本可以保持无状态。单元中的任何副本都能为该单元的任何仓库提供服务,因此替换或新增副本时,无需在服务器之间迁移仓库数据。针对某个仓库的请求通常会发往同一个副本,以充分利用其缓存,但仓库并不依赖于该特定实例始终可用。

验收步骤会检查仓库不变量,确保被接受的提交对应一个有效的仓库状态。我们为每个仓库定义了明确的约定,因此可以通过机械化检查防止仓库过度膨胀。PDF、图片等大文件和二进制文件通过 Git LFS 存储,而不是以原始 Git blob 的形式存储。压缩机制在限制仓库大小的同时,保留近期历史可供访问。每次成功提交后,CLI 会询问 Git 服务该仓库是否需要压缩。如果保留的提交数量超过阈值,Git 服务只会保留较少的一组最新提交。较早的历史会被替换为单个起始快照,近期提交则会被重放,以更新提交标识符。

压缩会保留最新的提交,将较早的历史折叠为单个快照根,并以新的标识符重放近期提交;新的 head 保留相同的文件。

一个简单的同步步骤负责处理并发写入者。它跟踪最新被接受的提交,即 head。收到提交请求时,只有当该提交基于这个 head 构建时,它才会接受。否则,它会返回一个结构化的“基线已过期”响应;落败的一方随后需要在新的 head 之上重放其更改并重新提交。这一设计确保并发的智能体不会在不知情的情况下相互覆盖,同时也不会造成明显的性能下降。在我们的生产工作流中,因基线过期而被拒绝的提交不到 2%。总体而言,提交延迟的中位数低于 1.7 秒,p99 延迟低于 8 秒。

两个智能体同时提交;其中一个被接受,另一个收到“基线已过期”响应,在新的 head 上重放后重新提交。

Git 服务还会记录每一项被接受的更改。每接受一次写入,它就会向发件箱添加一条记录。发件箱是一份日志,列出了仓库、新的提交、做出更改的用户以及发生更改的路径。解析器等下游服务会读取这份日志来发现新的工作。

这些组件共同构成了一个仓库管理层,同时去掉了智能体不需要的部分。拉取请求并无必要,因为没有人会逐一审核每项更改。取而代之的是:Git 服务在提交时检查约定,在竞争中落败的智能体重新应用自己的更改,而提交历史则记录了谁更改了什么。确定性检查让每个仓库始终符合其约定,而无需依赖人工运维人员来管理仓库的增长。

存储:确保工作成果持久保存

存储层独立于沙盒和 Git 服务副本,持久化保存仓库数据。仓库存放在 AWS S3Files 上,它通过文件系统接口提供基于 S3 的存储,原生 Git 无需任何修改即可使用。每个仓库都是共享文件系统上的一个裸 Git 仓库,其中包含仓库的对象和引用,而不是工作副本。

存储与计算解耦,让沙盒可以保持临时性。每个智能体都有自己的沙盒,许多智能体可以在同一个项目上工作,而无需共享一台机器。如果某个沙盒崩溃,只有尚未提交的工作会面临风险。

我们也可以为每个项目提供一个长期存在、由所有智能体共享的沙盒,让项目文件存放在这台机器的磁盘上。然而,这会把智能体的计算资源绑定到这台机器的运行位置,并要求按其最繁忙时段的需求来配置规格。此外,这台机器还需要连续数月保持健康运行,并将备份和恢复作为项目存储系统的一部分进行管理。

由项目内所有智能体共享的一个长期沙盒,与通过无状态 Git 服务副本访问持久化共享存储的短期、每个智能体专属的沙盒之间的对比。

下游处理:让文档便于智能体读取

主要的下游组件是一条解析器流水线,它从新增或更新的文档中提取文本和元数据,让智能体能够轻松读取项目文件。解析器工作进程从变更流队列中消费更新,根据提交元数据推导出发生更改的文件,并通过内容哈希获取原始字节。随后,它会解析文档,生成提取出的文本和文档元数据。

解析输出存放在仓库之外,保存在一个以源文档哈希为键的存储中。这些工件永远不会提交回仓库,因为那样会不必要地污染历史记录,并增加并发写入的次数。相反,解析输出会通过检出和同步到达沙盒。每个检出都带有一个预留目录,供 CLI 用于落地这些工件。解析是异步进行的,因此后台解析器工作进程不会阻塞检出。

智能体如何使用工作空间

智能体使用标准文件系统工具,探索已加载到沙盒中的仓库版本。它们不遵循固定的“先搜索、后读取”流程,而是列出文件夹、用 grep 搜索文件内容、阅读相关段落,并根据发现的内容决定下一步该看哪里。

与 Computer 类似,Search 也通过其标准文件工具采用相同的方法。一个独立的探索智能体在更有限的预算内搜索项目,并返回相关的文件路径和佐证摘录。随后,回答模型读取选中的文件并生成答案。复用现有的项目沙盒可以缩短准备时间:接入一个已预热的检出大约需要 2.9 秒,而冷启动则需要约 8 秒。

Computer 沙盒、Ask 的探索子智能体、后台知识写入者以及解析器工作进程,都在读写同一个仓库和发件箱。

解析后的文本让 PDF 和 Office 文件等较大的文档也能用同样的工具进行搜索。智能体会先查看清单(一份列出哪些文档已有提取文本的列表),然后像对待普通文件一样搜索和阅读这些文本。只有当文本不足以满足需求时(例如需要查看图表、版式或嵌入的数据),它才会下载原始文档。

我们在同一语料库上,用 150 个单文档金融问题,将这种方法与基于索引的语义搜索进行了基准对比。探索智能体在检索正确文档方面得分为 0.867,在正确回答方面得分为 0.627,而语义搜索分别约为 0.39 和 0.37。也就是说,探索智能体的回答得分约为语义搜索的 1.7 倍,检索得分约为其 2 倍,同时延迟降低了约 25%。探索智能体主要使用提取出的文本,很少下载原始 PDF。

在内部基准测试中,探索方式与语义文件搜索的对比。

一套共享基础设施

Computer 的持久记忆使用同一套文件系统基础设施。Perplexity Brain 是 Computer 的知识 wiki,它将记忆存储在知识仓库中,这些仓库拥有各自的历史和策略。将知识与项目文件分开存放,意味着后台的记忆更新不会与对这些文件的编辑相互竞争。需要两者的会话会同时加载两个仓库。

后台智能体读取现有知识、整合新信息,并将更新后的页面保存为提交。其他智能体使用普通文件工具读取这些页面,人也可以编辑它们。写入者通过仓库共享成果,而不是与每个读取者直接协调。提交历史记录了谁更改了什么,而发件箱则将被接受的更新报告给下游服务。人和智能体可以在彼此成果的基础上继续推进,并留有知识演变过程的记录。

结语

当人和智能体共享一个项目时,工作空间的作用不只是存储文件。它提供了一个汇集各方贡献、跟踪更改内容的公共场所。Git 已经提供了其中的大部分基础。我们在此之上增加了协调机制和自动化检查,让智能体无需有人逐一批准每项更新就能提交工作。最终得到的是一个精简到只剩核心要素的代码托管平台,因为从本质上看,跨智能体协作和多人参与的软件开发是同一个问题。

智能体在彼此独立的临时沙盒中工作,同时为持久化的仓库贡献成果。仓库限制让增长保持可控,显式下载让智能体自行选择要获取的内容,而普通文件则让它们能够用熟悉的工具进行探索。后台更新、交互式编辑和定时任务可以在彼此成果的基础上继续推进,而无需直接协调。

随着越来越多的智能体在项目中自主工作,它们共享的工作空间必须保持可靠,并且对人来说易于理解。目标很简单:每项更改都应比产生它的智能体更持久,能够供下一位协作者使用,并清晰记录更改了什么。

引用

BibTeX
@online{perplexity2026BuildingSharedWorkspacesForPeopleAndAgents,
  author = {{Perplexity} and Se Won Jang and Peter Hart and Kartik Kapur and Kevin Lau and Sidhant Rajadnya and Ishbir Singh and Thomas Wang and Jarrod Bryan and Claire Beamer and Jeremy Yang},
  title = {{Building shared workspaces for people and agents}},
  date = {2026-10-12},
  url = {https://www.perplexity.ai/hub/blog/building-shared-workspaces-for-people-and-agents}
}