OpenAICloudflare2026/03/24 13:00

Sandboxing AI agents, 100x faster

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

openaijamodel: gpt-5-mini-2025-08-07

Dynamic Worker LoaderでAIエージェントを100倍高速にサンドボックス実行

Key Points

  • 100倍高速
  • isolateベースの軽量サンドボックス
  • TypeScript RPC推奨

Summary

CloudflareのDynamic Worker Loaderは、実行時にコードを指定して新しいWorker(V8 isolate)をオンザフライ生成できるAPIです。コンテナより約100倍高速・10〜100倍メモリ効率的で、数ミリ秒で起動・数MBで動作するため、リクエスト単位で安全にサンドボックスを作成して破棄する運用が実現可能です。エージェントにはTypeScriptベースのRPCインターフェースを渡すことを推奨し、env.LOADER.load(...)でモジュール/env/globalOutboundを指定して実行します。HTTPの検査・認証注入や、Cloudflareが提供するV8パッチや追加サンドボックスによる防御深度も利用できます。

Key Points

  • 動的ロードは env.LOADER.load({ modules, mainModule, env, globalOutbound }) の呼び出しで実行可能。モジュール文字列をそのまま渡せる。
  • Isolateベースで起動は数ミリ秒・メモリは数MB:コンテナより約100x高速・10–100x効率的 → リクエストごとのサンドボックス化が現実的。
  • グローバルな同時実行上限がなく、世界中のエッジでほぼゼロレイテンシに実行できるスケーラビリティ。
  • TypeScript RPCを推奨:トークン消費が少なく、Cap’n Web RPCでサンドボックス境界を透過的に呼び出せる(簡潔なAPI定義でセキュリティ向上)。
  • globalOutboundを使ってHTTPリクエストを検査/書き換え/認証注入できるため、エージェントにシークレットを与えずに外部API利用が可能。
  • セキュリティ面はV8の自動パッチ配布、追加のサンドボックス層、MPK拡張、悪性コードスキャンなどの防御深度で補強済み。
  • 制約:最も高速なのはJavaScript/TypeScript(PythonやWasmは利用可能だがスニペット実行では遅延が増える)。

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

openaijamodel: gpt-5-mini-2025-08-07

AIエージェントのサンドボックス化、100x高速化

概要

2025年9月に公開した「Code Mode」では、エージェントがツール呼び出しを行うのではなく、APIを呼び出すコードを書くことでタスクを実行するというアイデアを紹介しました。MCPサーバーをTypeScript APIに変換するだけでトークン使用量を81%削減できることを示しました。Code ModeはMCPサーバーの前ではなく背後で動作させることもでき、Cloudflare MCPサーバーではわずか2つのツール、1,000トークン未満でCloudflare API全体を公開できることを実証しました。

しかし、もしエージェント(またはMCPサーバー)がAIによってオン・ザ・フライで生成されたコードを実行してタスクを行うなら、そのコードはどこかで実行される必要があり、その場所は安全でなければなりません。AI生成コードをアプリで直接eval()するわけにはいきません。悪意のあるユーザーは簡単にAIに脆弱性を注入させることができます。必要なのはサンドボックスです:アプリケーションや外部環境から隔離され、コードに与えたい特定の能力だけを許可する実行場所です。

サンドボックス化はAI業界で重要なテーマです。多くの人はコンテナに頼っています。Linuxベースのコンテナを使えば、任意のコード実行環境を立ち上げられます(Cloudflareもこの用途のためにコンテナランタイムやSandbox SDKを提供しています)。しかしコンテナは遅く高価で、起動に数百ミリ秒、メモリは数百MBを消費します。遅延を避けるためにウォームスタートを維持する必要があり、複数タスクでコンテナを再利用するとセキュリティを損なう恐れがあります。消費者規模のエージェント(各エンドユーザーがエージェントを持ち、エージェントがコードを書くようなケース)をサポートするには、コンテナだけでは不十分です。より軽量な仕組みが必要です。

Dynamic Worker Loader:軽量なサンドボックス

昨年のCode Modeの記事の中で、実験的機能として「Dynamic Worker Loader API」を発表しました。このAPIにより、Cloudflare Workerがランタイム時に指定されたコードで新しいWorkerを、そのWorkerごとのサンドボックス内にオンザフライでインスタンス化できます。Dynamic Worker Loaderは現在オープンベータで、すべての有料Workersユーザーが利用可能です。詳細はドキュメントを参照してください。

使い方は概ね次のとおりです(例):

// Have your LLM generate code like this.
let agentCode: string = `
export default {
  async myAgent(param, env, ctx) {
    // ...
  }
}
`;

// Get RPC stubs representing APIs the agent should be able
// to access. (This can be any Workers RPC API you define.)
let chatRoomRpcStub = ...;

// Load a worker to run the code, using the worker loader
// binding.
let worker = env.LOADER.load({
  // Specify the code.
  compatibilityDate: "2026-03-01",
  mainModule: "agent.js",
  modules: { "agent.js": agentCode },
  // Give agent access to the chat room API.
  env: { CHAT_ROOM: chatRoomRpcStub },
  // Block internet access. (You can also intercept it.)
  globalOutbound: null,
});

// Call RPC methods exported by the agent code.
await worker.getEntrypoint().myAgent(param);

100x高速

Dynamic Workersは、Cloudflare Workersプラットフォームがローンチ以来採用してきた同じサンドボックス機構、すなわち「isolate」を利用しています。isolateはV8 JavaScript実行エンジンのインスタンスで、Google Chromeが使用するのと同じエンジンです。isolateは起動に数ミリ秒、メモリは数メガバイトしか使いません。これは典型的なコンテナに比べておおよそ100x高速で、メモリ効率は10x–100xに相当します。したがって、各ユーザーリクエストごとにオンデマンドで新しいisolateを起動し、1スニペットを実行して破棄する、という運用が現実的になります。

無制限のスケーラビリティ

多くのコンテナベースのサンドボックスプロバイダは、同時グローバルサンドボックス数やサンドボックス作成レートに制限を設けます。Dynamic Worker Loaderにはそのような制限がありません。というのも、このAPIは私たちのプラットフォームをこれまで支えてきた技術(Workersが何百万リクエスト/秒にシームレスにスケールできる仕組み)そのものへのインターフェースだからです。もし毎リクエストごとに別々のDynamic Workerサンドボックスを同時に実行して1秒間に100万リクエストを処理したい場合でも問題ありません。

ゼロレイテンシ

ワンオフのDynamic Workerは通常、作成元のWorkerと同じマシン、同じスレッドで動作します。ウォームサンドボックスを探して遠隔地と通信する必要はありません。isolateは非常に軽量なので、リクエストが到着した場所でそのまま実行できます。Dynamic WorkersはCloudflareの数百あるロケーションすべてでサポートされています。

全てJavaScript

コンテナと比べた唯一の制約は、エージェントがJavaScriptを書く必要がある点です。技術的にはWorkers(Dynamic Workersを含む)はPythonやWebAssemblyも利用できますが、エージェントがオンデマンドで書くような短いスニペットではJavaScriptの読み込みと実行が遥かに高速です。

人間は言語に強い好みを持ちますが、AI相手なら話は別です。LLMは主要言語すべてに精通しており、JavaScriptの学習データは膨大です。Webにおける性質上、JavaScriptはサンドボックス化が前提の言語であり、この用途に最適です。

TypeScriptで定義されたツール

エージェントに外部APIを呼ばせるには、どのAPIにアクセスできるかを伝える必要があります。MCPはフラットなツール呼び出しのスキーマを定義しますが、プログラミングAPIの表現には向いていません。OpenAPIはREST APIを表現する手段を提供しますが、スキーマ自体も呼び出し用コードも冗長になりがちです。JavaScript向けのAPIを公開する場合、明白な解はTypeScriptです。エージェントはTypeScriptを理解しますし、TypeScriptは簡潔にAPIの意味を伝えられます。

次はチャットルームと対話するためのTypeScriptインターフェースの例です:

// Interface to interact with a chat room.
interface ChatRoom {
  // Get the last `limit` messages of the chat log.
  getHistory(limit: number): Promise<Message[]>;
  // Subscribe to new messages. Dispose the returned object
  // to unsubscribe.
  subscribe(callback: (msg: Message) => void): Promise<Disposable>;
  // Post a message to chat.
  post(text: string): Promise<void>;
}
type Message = { author: string; time: Date; text: string; }

これを同等のOpenAPI仕様と比べてみてください(長いのでスクロールが必要になります):

openapi: 3.1.0
info:
  title: ChatRoom API
  description: >
    Interface to interact with a chat room.
  version: 1.0.0
paths:
  /messages:
    get:
      operationId: getHistory
      summary: Get recent chat history
      description: Returns the last `limit` messages from the chat log, newest first.
      parameters:
        - name: limit
          in: query
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: A list of messages.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Message"
    post:
      operationId: postMessage
      summary: Post a message to the chat room
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
              properties:
                text:
                  type: string
      responses:
        "204":
          description: Message posted successfully.
  /messages/stream:
    get:
      operationId: subscribeMessages
      summary: Subscribe to new messages via SSE
      description: >
        Opens a Server-Sent Events stream. Each event carries a JSON-encoded Message object. The client unsubscribes by closing the connection.
      responses:
        "200":
          description: An SSE stream of new messages.
          content:
            text/event-stream:
              schema:
                description: >
                  Each SSE `data` field contains a JSON-encoded Message object.
                $ref: "#/components/schemas/Message"
components:
  schemas:
    Message:
      type: object
      required:
        - author
        - time
        - text
      properties:
        author:
          type: string
        time:
          type: string
          format: date-time
        text:
          type: string

私たちはTypeScript APIの方が優れていると考えています。トークン数が少なくて済み、理解しやすい(エージェント側にも人間側にも)からです。Dynamic Worker Loaderを使えば、自分のWorker内でこのようなTypeScript APIを実装し、メソッド引数またはenvオブジェクト経由でDynamic Workerに渡すことが簡単にできます。Workers Runtimeはサンドボックスとハーネスコード間でCap'n Web RPCブリッジを自動的に設定するため、エージェントはローカルライブラリを使っているかのようにAPIを呼び出せます。

これにより、エージェントは次のようなコードを書けます:

// Thinking: The user asked me to summarize recent chat messages from Alice.
// I will filter the recent message history in code so that I only have to
// read the relevant messages.
let history = await env.CHAT_ROOM.getHistory(1000);
return history.filter(msg => msg.author == "alice");

HTTPフィルタリングと資格情報注入

HTTPインターフェースをエージェントに与えることも完全にサポートされています。worker loader APIのglobalOutboundオプションを使うと、外向きHTTPリクエストごとに呼び出されるコールバックを登録できます。そのコールバック内でリクエストを検査・書き換え・認証情報注入・直接応答・ブロックなどを行えます。

例えば、エージェントが認証が必要なサービスにHTTPリクエストを送る際に、送信側で資格情報を追加(トークン注入)することができます。こうすれば、エージェント自身は秘密情報を知ることがなく、漏洩のリスクを低減できます。

ただし、互換性が要求されない限り、TypeScript RPCインターフェースはHTTPより優れています:

  • TypeScriptインターフェースはHTTPインターフェースよりも少ないトークンで記述できる。
  • エージェントがTypeScriptインターフェースを呼ぶコードは、同等のHTTP呼び出しに比べてはるかに少ないトークンで書ける。
  • TypeScriptインターフェースでは、公開する機能を狭く限定できるため、単純さとセキュリティの面で有利。

HTTP経由だと、既存APIに対するリクエストをフィルタリングする必要が生じやすく、各APIコールの意味を完全に解釈して許可するか判断するのは難しいです。結果として、必要な関数のみを実装したTypeScriptラッパーを書く方が簡単です。

堅牢なセキュリティ

isolateベースのサンドボックスを堅牢化するのは難しく、ハードウェア仮想マシンよりも攻撃対象が複雑です。すべてのサンドボックス機構にバグは存在しますが、V8におけるセキュリティバグは一般的なハイパーバイザのバグより頻繁に見つかる傾向があります。isolateを使って悪意ある可能性のあるコードを分離実行する場合、追加の多層防御が重要です。

Google Chromeはこの理由で厳格なプロセス分離を導入していますが、それが唯一の解決策ではありません。私たちはisolateベースのプラットフォームをほぼ10年にわたって運用・保護してきました。システムはV8のセキュリティパッチを数時間以内に本番へ自動展開します—これはChrome自体より速い場合もあります。セキュリティアーキテクチャには、リスク評価に基づく動的テナント隔離を行うカスタムな第2層サンドボックスを採用しており、V8サンドボックスを拡張してMPKなどのハードウェア機能を活用しています。Spectreに対する新しい防御策を開発するために外部研究者と協力・採用も行っています。さらに、コードを走査して悪意あるパターンを検出し、自動的にブロックしたり追加のサンドボックス化を適用するシステム等、多数の防御が組み合わされています。

Dynamic WorkersをCloudflare上で使えば、これらすべてが自動的に適用されます。

ヘルパーライブラリ

ヘルパーライブラリをいくつか構築しました。

(ここでの記事は原文の掲載箇所で切れています。)