人とエージェントのための共有ワークスペースの構築

Perplexityのプロジェクトが、長期的な作業を永続的、並行的、かつ追跡可能に保つ仕組み。

著者Perplexity Engineering

先日、 プロジェクトをリリースしました。プロジェクトでは、Perplexity Computer上で人とエージェントが協力して長期的なタスクに取り組めます。各プロジェクトは、セッション、エージェント、ユーザーをまたいでコンテキストを蓄積するファイルシステムを備えた共有ワークスペース上に構築されています。何か月にもわたるマルチプレイヤーでの共同作業には、永続性、並行性、追跡可能性が求められます。

  • 永続性:作業が個々のエージェントのセッションやサンドボックスを超えて永続的に保持され、同じユーザーやエージェントが後から作業を継続したり、他の共同作業者がそれを土台にしたりできること。
  • 並行性:同じプロジェクト内の複数のユーザーがそれぞれ複数のエージェントを使って並行して編集を行い、互いの変更を上書きするのではなく、共有された状態を前に進められること。
  • 追跡可能性:ユーザーとエージェントの双方が履歴を確認でき、プロジェクト全体を読み直すことなく前回のセッション以降に誰が何を変更したかを確認したり、必要に応じて編集をロールバックしたりできること。

こうした課題は、ソフトウェアエンジニアにとって見覚えのあるものです。Gitフォージは、これらの要件を満たすために数十年前に登場しました。バージョン管理を共同作業機能で包み込み、多くのコントリビューターが単一の信頼できる情報源に集約できるようにするホスト型サービスです。プロジェクトを支えるファイルシステムは、人とエージェント、そしてエージェント同士の共同作業のために構築された、そうしたGitフォージの一つです。

1つのプロジェクト:対話型エージェント、スケジュール実行エージェント、他のユーザーのエージェントがすべて同じリポジトリに集約されます。

アーキテクチャ

Gitはすでに、永続性、並行性、追跡可能性を備えています。Gitは各ファイルをその内容によって識別するため、変更されていないファイルは一度だけ保存され、保存されたコピーが古くなることはありません。各コミットは更新されたすべてのファイルを一度に保存するため、共同作業者が中途半端な状態を目にすることはありません。2つの書き込みが衝突した場合、負けた側は新しいバージョンの上に自身の変更を再適用して再試行できます。コミットの連なりは誰が何を変更したかを示し、以前のバージョンへの復元も容易にします。エージェントに不要なのは、レビュー画面、ブランチ管理、通知など、人間向けに作られたフォージのそれ以外の部分です。

当社のアーキテクチャは、プロセス全体をクラウド上で実行し、コーディングにとどまらないあらゆる形態のナレッジワークをサポートします。すべてのComputerプロジェクトは、次の5つのコンポーネントによって提供されるGitリポジトリに支えられています。

  • CLIは、各サンドボックス内でローカルのチェックアウトを管理し、更新を同期し、エージェントの変更を送信します。
  • コントロールプレーンは、プロジェクトのリポジトリの場所を特定し、そこにアクセスするための短期間有効な認証情報を発行します。
  • Gitサービスは、リポジトリデータをサンドボックスに配信し、サンドボックスからの送信を受け取り、どの送信を最新の受け入れ済みコミットにするかを決定します。
  • AWS S3ベースのストレージは、リポジトリデータを永続的に保存します。
  • 下流処理は、保存されたドキュメントからテキストやその他の有用な情報を抽出し、エージェントがプロジェクトを読み込んだり更新したりする際に利用できるようにします。

各エージェントには、それぞれ独立したサンドボックスがあります。そのサンドボックス内で、エージェントは小さなCLIによって管理されるリポジトリのローカルコピーを使って作業します。CLIはコントロールプレーンにリポジトリの場所を問い合わせ、Gitサービスを通じて変更のフェッチと送信を行います。エージェントプロキシは、サンドボックスからのリクエストがGitサービスに到達する前にアクセス権をチェックします。Gitサービスは共有ファイルシステム上のリポジトリを読み書きし、各送信を最新の受け入れ済みコミットにするかどうかを決定します。このファイルシステムはAWS S3Filesによって提供されており、S3ベースのストレージに通常のファイルやディレクトリとしてアクセスできるようにしています。ストレージは分離されたセルに分割されており、各セルは独自のファイルシステムとGitサービスのレプリカを持ちます。コミットが受け入れられると、バックグラウンドワーカーが変更されたドキュメントからテキストやその他の有用な情報を抽出し、その結果をリポジトリの履歴とは別に保存して、エージェントがプロジェクトを読み込んだり更新したりする際に利用できるようにします。

次に、各コンポーネントを1つずつ見ていきます。

CLIはコントロールプレーンに問い合わせてリポジトリの場所を特定し、エージェントプロキシ経由でGitサービスを通じてフェッチと送信を行います。Gitサービスは送信内容をチェックし、DynamoDB内の受け入れ済みヘッドとアウトボックスを更新します。セルローカルのレプリカはS3Filesストレージを使用し、下流のワーカーはチェックアウトと同期のためにテキストを抽出します。

CLI:サンドボックス内でローカルファイルを扱う

エージェントは、当社独自のサンドボックスプラットフォームであるSPACE上で動作します。各サンドボックスには、リポジトリを実体化して同期を保つ小さなCLIがあります。エージェントが意味のある作業単位を完了するごとに、CLIはその変更をコミットとして送信します。

エージェントが触れるのは通常、プロジェクトファイルのごく一部にすぎないため、リポジトリ全体を最初に実体化する必要はありません。そこでCLIは遅延的に実体化を行います。Git Large File Storage(LFS)を通じて保存された大容量ファイルやバイナリファイルは、オブジェクトの実際のサイズを記録した小さなポインターファイルとして届くため、エージェントは名前とサイズからダウンロードする価値があるかどうかを判断できます。PDFなどのドキュメントの場合、エージェントはまずパース済みテキストを読み、必要な場合にのみソースファイルをダウンロードします。

もう一つの自然な選択肢は、仮想ファイルシステムを使うことです。FUSEベースの実装を試したところ、すべてのリクエストでネットワークの往復が必要になるため、ファイルシステムへの負荷が高いワークロードではローカルファイルに比べて約400倍遅くなりました。また、通常はホストからの特権機能が必要となるため、すべてのエージェントを特権サンドボックスで実行しなければなりません。

ポインターのサイズとパースマニフェストによって、パース済みテキストを読む、ソースをダウンロードする、スキップする、のいずれかが決まります。

コントロールプレーン:リポジトリの特定とアクセス権の発行

コントロールプレーンは、各リポジトリの保存場所を特定し、アクセス権を発行します。リポジトリが作成されると、コントロールプレーンはそれをストレージセルに割り当てます。ストレージセルとは、独自のGitサービスレプリカを持つ分離されたストレージ単位です。エージェントがプロジェクトの作業を開始すると、CLIはコントロールプレーンにリポジトリの保存場所を問い合わせます。コントロールプレーンは、その場所とGitサービス用の一時的な認証情報を返します。コントロールプレーンがリポジトリのデータを読み書きすることはありません。リポジトリに何を取り込むかは、Gitサービスが決定します。

Gitサービス:データの移動と更新の調整

Gitサービスは、Git操作を実行し、サンドボックスとストレージの間でリポジトリの内容とバージョン履歴を転送します。これは、Git自体のサーバーコマンドを薄くラップしたものです。クライアントにリポジトリのヘッドの位置を伝えるref discovery、クローンとフェッチのためのupload-pack、新しいオブジェクトとリポジトリのヘッドを進める要求を受け取るためのreceive-packをサポートしています。また、Git LFS APIも提供しています。大容量ファイルやバイナリファイルについては短期間有効なリンクを発行し、サンドボックスがS3との間でそれらのファイルを直接アップロード・ダウンロードできるようにします。

当初は既製のフォージサーバーを使用していました。これにより、必要なGit操作だけに絞った独自の軽量フォージを構築するのと並行して、エージェントとユーザーのインターフェースのプロトタイプを作成できました。

このサービスは、同じサーバーの実行インスタンスであるレプリカ群で構成されています。セルは、共有ストレージと一連のレプリカ、およびそれらを支えるサービスをひとまとめにしたものです。これにより、セル内のレプリカは、すべてのリポジトリの独立したコピーを個別に保持するのではなく、同じ共有ファイルシステムにアクセスできます。

この共有ストレージにより、レプリカはステートレスなままでいられます。セル内のどのレプリカでも、そのセルのどのリポジトリにも対応できるため、レプリカの置き換えや追加の際にサーバー間でリポジトリデータを移動する必要はありません。キャッシュを有効に活用するため、あるリポジトリへのリクエストは通常同じレプリカに送られますが、リポジトリがその特定のインスタンスの稼働に依存することはありません。

受け入れステップでは、リポジトリの不変条件をチェックし、受け入れられたコミットが有効なリポジトリの状態であることを保証します。すべてのリポジトリに明示的な契約を定義しているため、機械的なチェックによってリポジトリが大きくなりすぎるのを防げます。PDFや画像などの大容量ファイルやバイナリファイルは、生のGit blobとしてではなく、Git LFSを通じて保存されます。コンパクションは、リポジトリのサイズを抑えつつ、直近の履歴を利用可能な状態に保ちます。送信が成功すると、CLIはGitサービスに対してリポジトリのコンパクションが必要かどうかを問い合わせます。保持されているコミット数がしきい値を超えると、Gitサービスは最新のコミットのうち、より少数のコミットだけを残します。古い履歴は起点となる単一のスナップショットに置き換えられ、直近のコミットが再適用されてコミットIDが更新されます。

コンパクションでは、最新のコミットを残し、古い履歴を単一のスナップショットルートにまとめ、直近のコミットを新しいIDで再適用します。新しいヘッドには同じファイルが保持されます。

シンプルな同期ステップによって、同時に書き込みを行う複数のライターを制御します。このステップは、最後に受け入れられたコミット、つまりヘッドを追跡します。送信を受け取ると、その送信がそのヘッドを基に作成されている場合にのみ受け入れます。そうでない場合は、構造化された古いベース(stale-base)レスポンスを返します。負けた側は、新しいヘッドの上に自身の変更を再適用して再送信する必要があります。この設計により、大幅な速度低下を招くことなく、同時に動作するエージェント同士が互いの変更を気づかないうちに上書きしてしまうことを防げます。当社の本番ワークフローでは、古いベースとして拒否される送信は2%未満です。全体として、送信レイテンシの中央値は1.7秒未満、p99レイテンシは8秒未満です。

2つのエージェントが同時に送信します。一方は受け入れられ、もう一方は古いベース(stale-base)レスポンスを受け取り、新しいヘッドの上で再適用して再送信します。

Gitサービスは、受け入れたすべての変更も記録します。受け入れた書き込みごとに、アウトボックスにエントリーを追加します。アウトボックスとは、リポジトリ、新しいコミット、変更を行ったユーザー、変更されたパスを一覧にしたログです。パーサーなどの下流サービスは、このログを読んで新しい処理対象を見つけます。

これらの要素が組み合わさることで、エージェントに不要な部分を省いたリポジトリ管理レイヤーが形成されます。人間が個々の変更をレビューすることはないため、pull requestは必要ありません。代わりに、Gitサービスが送信時に契約をチェックし、競合に負けたエージェントは自身の変更を再適用し、コミット履歴が誰が何を変更したかを記録します。リポジトリの増大を人間のオペレーターが管理するのではなく、決定論的なチェックによって各リポジトリを契約の範囲内に保ちます。

ストレージ:作業を永続的に保つ

ストレージレイヤーは、サンドボックスやGitサービスのレプリカとは独立して、リポジトリデータを永続的に保存します。リポジトリはAWS S3Files上に置かれています。S3Filesは、S3ベースのストレージをファイルシステムインターフェースとして公開するため、標準のGitを変更せずにそのまま使用できます。各リポジトリは共有ファイルシステム上のベアGitリポジトリであり、作業用のチェックアウトではなく、リポジトリのオブジェクトと参照を格納しています。

ストレージとコンピューティングを分離することで、サンドボックスを一時的なものにしておけます。各エージェントは専用のサンドボックスを持ち、多くのエージェントがマシンを共有することなく同じプロジェクトで作業できます。サンドボックスがクラッシュしても、失われるおそれがあるのはまだ送信されていない作業だけです。

代わりに、各プロジェクトに、そのすべてのエージェントが共有する長期稼働のサンドボックスを用意し、プロジェクトのファイルをそのマシンのディスク上に置くこともできます。しかしその場合、エージェントのコンピューティングがそのマシンの稼働場所に縛られ、最も負荷の高い時期に合わせてマシンのサイズを決める必要があります。また、マシンを何か月にもわたって正常な状態に保つ必要があり、バックアップと復旧もプロジェクトのストレージシステムの一部として管理しなければなりません。

プロジェクトのエージェントが共有する1つの長期稼働サンドボックスと、ステートレスなGitサービスのレプリカを通じて永続的な共有ストレージにアクセスする、エージェントごとの短期稼働サンドボックスの比較。

下流処理:ドキュメントをエージェントが読める形にする

主要な下流コンポーネントは、新たに追加または更新されたドキュメントからテキストとメタデータを抽出し、プロジェクトファイルをエージェントが読みやすくするパーサーパイプラインです。パーサーワーカーは変更ストリームのキューから更新を取り出し、コミットのメタデータから変更されたファイルを特定し、コンテンツハッシュを使って生のバイトデータを取得します。その後、ドキュメントをパースし、抽出テキストとドキュメントのメタデータを生成します。

パース結果は、リポジトリの外にある、ソースドキュメントのハッシュをキーとするストアに保存されます。これらのアーティファクトがリポジトリにコミットされることはありません。コミットすると履歴が不必要に汚れ、同時書き込みの数も増えてしまうためです。代わりに、パース結果はチェックアウトと同期を通じてサンドボックスに届けられます。各チェックアウトには、CLIがアーティファクトの実体化に使用する予約ディレクトリがあります。パースは非同期で行われるため、バックグラウンドのパーサーワーカーがチェックアウトをブロックすることはありません。

エージェントによるワークスペースの使い方

エージェントは、標準的なファイルシステムツールを使って、サンドボックスに読み込まれたリポジトリのバージョンを探索します。検索してから読むという固定のパイプラインに従うのではなく、フォルダーを一覧表示し、grepでファイルの内容を検索し、関連する箇所を読み、見つけた情報をもとに次にどこを調べるかを判断します。

Computerと同様に、検索でも標準的なファイルツールを通じて同じアプローチを使用しています。別の探索エージェントが、より限られた予算内でプロジェクトを検索し、関連するファイルパスと裏付けとなる抜粋を返します。その後、回答モデルが選択されたファイルを読み、回答を生成します。既存のプロジェクトサンドボックスを再利用することでセットアップ時間を短縮できます。ウォームなチェックアウトへのアタッチにかかる時間は約2.9秒で、コールドスタートの場合の約8秒を下回ります。

Computerのサンドボックス、Askの探索サブエージェント、バックグラウンドのナレッジライター、パーサーワーカーがすべて、同じリポジトリとアウトボックスに対して読み書きを行います。

パース済みテキストにより、PDFやOfficeファイルなどの大きなドキュメントも同じツールで検索できるようになります。エージェントはまず、どのドキュメントに抽出テキストがあるかを示すリストであるマニフェストを確認し、そのテキストを通常のファイルとして検索して読みます。元のドキュメントをダウンロードするのは、図、レイアウト、埋め込みデータを確認する場合など、テキストだけでは不十分なときに限られます。

このアプローチを、同じコーパスに対する150件の単一ドキュメント型の金融に関する質問で、インデックスを用いたセマンティック検索と比較するベンチマークを行いました。探索エージェントは、正しいドキュメントの取得で0.867、正答で0.627のスコアを記録しました。これに対し、セマンティック検索はそれぞれ約0.39と0.37で、探索エージェントは回答で約1.7倍、取得で約2倍優れており、レイテンシも約25%低くなりました。探索エージェントは主に抽出テキストを使って作業し、元のPDFをダウンロードすることはほとんどありません。

社内ベンチマークにおける探索型ファイル検索とセマンティックファイル検索の比較。

1つの共有インフラストラクチャ

Computerの永続メモリも、同じファイルシステム基盤を使用しています。ComputerのナレッジwikiであるPerplexity Brainは、独自の履歴とポリシーを持つナレッジリポジトリにメモリを保存します。ナレッジをプロジェクトファイルと分けておくことで、バックグラウンドでのメモリ更新がそれらのファイルの編集と競合しません。両方を必要とするセッションでは、両方のリポジトリを読み込みます。

バックグラウンドエージェントは、既存のナレッジを読み、新しい情報を取り入れ、更新したページをコミットとして保存します。他のエージェントは通常のファイルツールでそれらのページを読み、人も編集できます。書き手は各読み手と直接調整するのではなく、リポジトリを通じて作業を共有します。コミット履歴は誰が何を変更したかを記録し、アウトボックスは受け入れられた更新を下流のサービスに通知します。人とエージェントは、ナレッジがどのように発展してきたかの記録とともに、互いの作業を土台にして積み重ねていけます。

まとめ

人とエージェントがプロジェクトを共有する場合、ワークスペースは単にファイルを保存する以上の役割を果たします。貢献をまとめ、何が変わったかを追跡するための共通の場を提供するのです。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}
}