私たちは早い段階で、LLM に任意のコードを実行させるという賭けに出ました。この記事では、なぜ私たちがその賭けに出たのか、そして、ユーザーがエージェントとのチャットを開始・終了するたびに、何千ものサンドボックスを瞬時に立ち上げたり終了させたりするために何が必要なのかについて解説します。
ユーザーが Adapt エージェントと行うすべての会話は、それぞれ専用のコンピュータによって支えられています。共有サーバー上の制限されたコンテナではなく、モデルがソフトウェアのインストール、プログラムの作成と実行、Web ブラウジング、API との通信など、やりたいことを何でもできる隔離された VM です。私たちはこれらをサンドボックスと呼んでおり、Adapt を構築する上での核となるプリミティブの 1 つです。
完全な制御
LLM はコーディングの天才であり、私の仕事は主に、彼らが作業するための完璧な開発環境を構築することでした。
AI を外部の世界と接続する一般的な方法は、GitHub 用のカスタムコネクタ、HubSpot 用、Stripe 用といった具合に統合機能を個別に構築するか、各サービスが MCP サーバーをリリースするのを待つことです。しかし、これではスケールしませんし、私自身、毎日毎日統合コードを書くことに興味はありません。
そこで、その作業を自分たちで行う代わりに、モデルに任せることにしました。Adapt からは API を公開しているあらゆるサービスにアクセスできます。なぜなら、その API と通信するためのスクリプトやプログラムを書くために必要なすべてを LLM に提供しているからです。私たちが Adapt を「ホリゾンタル・インテリジェンス(水平型知能)」と呼ぶ理由の大部分はここにあります。Adapt は特定のツールリストに縛られることなく、必要なツールをその場で構築できるのです。
この基盤となるのは、LLM にサンドボックスへの完全なアクセス権を与えることです。ファイルシステムへのアクセスが制限された静的な言語や CLI ツールのセットをモデルに渡すのではなく、すべてに対する完全なアクセス権を与えています。モデルは root 権限で実行されます。また、私たちのサンドボックスには Node や Python といった一般的なランタイムが同梱されていますが、もし特定のサービスの API に最適な SDK が Go で書かれていたらどうでしょうか? モデルはそのまま Go をインストールして実行すればよいのです。

LLM が Go プログラムを書く必要があるなら? そのまま Go をインストールして実行してください。
では、モデルが好きなものを何でもインストールし、人間が検証していないコードを実行できるようにした場合、どのようにセキュリティを確保するのでしょうか? 幸いなことに、信頼できないコードを実行する必要があるのは私たちが初めてではありません。これには gVisor と Firecracker という 2 つの非常に人気のあるセキュアなランタイムがあります。これまでの道のりを通じて、私たちはその両方に精通するようになりました。
gVisor から Firecracker へ
LLM 用のセキュアなサンドボックスへの最初の試みは、「簡単な」アプローチでした。GKE(Google Kubernetes Engine)上で GKE Sandbox を使用し、各サンドボックスを gVisor で実行するというものです。私たちはすでに他のすべてのサービスを GKE 上で実行していたため、これは自然な流れでした。
gVisor はコンテナとホストカーネルの間に位置します。信頼できないコードに直接触れられたくない実際の Linux カーネルに対してプログラムがシステムコールを行うのを許可するのではなく、gVisor がそれらのコールを独自のユーザー空間カーネルでインターセプトし、自ら処理します。これにより、通常のコンテナの利便性の多くを享受しつつ、攻撃対象領域を大幅に縮小できます。そして GKE Sandbox は、これらすべてをパッケージ化してくれます。Pod(コンテナ)をデプロイすれば、インフラの設定をほとんど行うことなく、透過的に gVisor 下で実行されます。
当初、これは非常にうまく機能しました。私たちは「ベース」となるサンドボックスを Docker イメージとして定義し、必要な数のサンドボックスを GKE にスケールアウトさせました。サンドボックスに同梱されるソフトウェアの更新は、Dockerfile の更新とマニフェストでのバージョンアップだけで済みました。

GKE Sandbox 下で実行されている何百ものサンドボックス Pod。
しかし、gVisor を使いやすくしていた抽象化こそが、私たちが常に苦戦していた原因でもありました。gVisor は Linux のシステムコールインターフェースをユーザー空間で再実装しているため、すべてが実際のカーネル上とまったく同じように動作するわけではありません。そして、私たちのモデルが思いつくワークロードは、これ以上ないほど予測不可能です。安全性を得るためのインターセプトは、システムコールや I/O が多い作業ではコストとなります。また、ライフサイクル全体を GKE に依存していたため、起動時間、パッキング密度、ネットワーク、マシンのリサイクル頻度など、私たちが最も制御したかった部分をほとんど制御できませんでした。上記の「OutOfcpu」状態の Pod は、他人のスケジューラーを限界以上に酷使したときに見られる典型的な現象です。
それが、私たちが Firecracker に移行した理由です。
Firecracker microVM は本物の仮想マシンであり、それぞれが独自のゲストカーネルを持ち、ハードウェア仮想化で実行されますが、わずか数ミリ秒で起動するように軽量化されており、オーバーヘッドも数メガバイトしかありません。これは、AWS が膨大な数の Lambda や Fargate のワークロードを共有ハードウェア上に詰め込むために構築したのと同じ技術です。共有カーネルよりも強力な分離境界を提供し、瞬時に感じられるほど高速に起動し、1 つのホストに多数を詰め込めるほど小型です。
トレードオフとして、Firecracker は VM を提供するだけで、それ以外の機能はほとんどありません。スケジューリング、ネットワーク、ライフサイクルオーケストレーションを行う GKE のようなレイヤーは存在しません。そこで私たちはそれを自作し、「orc」と名付けました。
rootfs は単なるイメージ
コンテナから移行する際に諦めたくなかったことの 1 つは、サンドボックスをプレーンな Dockerfile として定義することでした。コンテナではそれは当たり前ですが、VM は通常そうではありません。microVM は OCI イメージではなく、ルートファイルシステムをブートするためです。
そこで orc が両者を橋渡しします。VM の作成を要求されると、通常の Docker/OCI イメージからその場で VM のルートファイルシステムを生成し、結果をキャッシュすることで、同じイメージの次回以降の起動を高速化します。私たちのベースとなるサンドボックスは依然として Dockerfile のままであり、orc が要求時にそれをブート可能な rootfs に変換します。
これにより、ワークフローは GKE 時代と変わらず、Dockerfile を編集して新しいサンドボックスをリリースするだけで、裏側では本物の VM 上で実行されます。そして、これは私たちが歩み始めたばかりの扉を開くことにもなりました。どんな OCI イメージも microVM になれるため、デフォルト以外のイメージからサンドボックスを起動できます。Postgres と pgvector が組み込まれた VM が必要ですか? orc でそのイメージを指定すれば、隔離された独自のマシンとして手に入ります。サンドボックスは単一の固定環境ではなく、「ジョブが必要とするあらゆるイメージを、独自の VM として起動したもの」へと進化するのです。
大規模な実行
そして、これがこの問題を真に困難にしている点です。すべてのチャットに独自のサンドボックスが割り当てられます。会話ごとに 1 台のマシンです。常に何千ものサンドボックスが稼働しており、その数は絶えず変化しています。誰かがチャットを開くたびにサンドボックスが出現し、チャットが静かになると、コストを抑えるために消滅します。私たちは常にサンドボックスを立ち上げたり終了させたりしているのです。
すべてを左右するのは 2 つの数値です。サンドボックスをどれだけ速く準備できるか、そして 1 つのホストにどれだけ詰め込めるかです。
起動レイテンシ。 Firecracker microVM は数百ミリ秒で起動します。これは、ウォームプールを維持する必要がないほどの速さであり、移行による静かな成果の 1 つです。GKE 時代には、起動時間を隠すために予備の容量を確保しておく必要がありました。orc を使えば、新しいサンドボックスはユーザーが気づく前に準備が整うため、チャット開始時にオンデマンドで作成し、終了時に破棄するだけで済みます。管理したりコストを払ったりするアイドルプールはもう必要ありません。
密度。 各 microVM は非常に小さいため、1 つの物理ホストに多数を詰め込むことができます。各サンドボックスの CPU とメモリを実際に必要なサイズに設定し、過剰なプロビジョニングを避けることで、何千ものサンドボックスを経済的に実行できています。
orc 自体は意図的に小さく作られています。これは、シンプルな API を話すコントロールプレーンです。指定されたイメージから N 個の vCPU と M メガバイトのメモリを持つ VM を作成し、コマンドをストリーミングし、内部のファイルを読み書きし、後で検索できるようにタグ付けし、完了したら削除する。各ゲストは PID 1 として小さな init プロセスを実行し、独自の隔離されたネットワークを持ちます。ほとんどの機能はこれだけです。魔法のようなトリックがあるわけではなく、これらのプリミティブが退屈なほどシンプルで、かつ艦隊を運用するのに十分なほど高速であるという点が重要なのです。
このすべての配管作業の成果は、私たちが最初に目指したものです。つまり、何でもインストールでき、プログラムを書き、API を叩き、答えを返してくれるモデルを、本物のコンピュータ上で実現することです。





