AIエージェントのサンドボックス化、100倍高速化
2026年3月24日 | Kenton Varda、Sunil Pai、Ketan Gupta | 9分で読める
昨年9月、私たちはCode Modeを発表しました。これは、エージェントがツール呼び出しを行うのではなく、APIを呼び出すコードを書くことでタスクを実行すべきだという考えです。MCPサーバーをTypeScript APIに変換するだけで、トークン使用量を81%削減できることを示しました。また、Code ModeがMCPサーバーの前ではなく後ろで動作することも実証し、わずか2つのツールと1,000トークン未満でCloudflare API全体を公開する新しいCloudflare MCPサーバーを作成しました。
しかし、エージェント(またはMCPサーバー)がタスクを実行するためにAIによって動的に生成されたコードを実行する場合、そのコードはどこかで実行される必要があり、その場所は安全である必要があります。アプリケーション内でAI生成コードを直接eval()することはできません。悪意のあるユーザーが簡単にAIに脆弱性を注入するよう促すことができるからです。
サンドボックスが必要です。コードが意図してアクセスすべき特定の機能を除いて、アプリケーションや外部世界から隔離されたコード実行環境です。
サンドボックス化はAI業界のホットトピック
このタスクのために、多くの人がコンテナに手を伸ばしています。Linuxベースのコンテナを使用すれば、任意の種類のコード実行環境を起動できます。CloudflareもこのためにコンテナランタイムとSandbox SDKを提供しています。
しかし、コンテナは高価で起動が遅く、起動に数百ミリ秒、実行に数百メガバイトのメモリが必要です。遅延を避けるためにウォームに保つ必要があり、セキュリティを損なう複数のタスクで既存のコンテナを再利用したくなるかもしれません。
すべてのエンドユーザーがエージェント(または複数!)を持ち、すべてのエージェントがコードを書くコンシューマー規模のエージェントをサポートしたい場合、コンテナでは不十分です。より軽量なものが必要です。
そして、私たちはそれを持っています。
Dynamic Worker Loader:軽量サンドボックス
9月のCode Modeの投稿に隠れていたのは、新しい実験的機能の発表でした:Dynamic Worker Loader APIです。このAPIにより、Cloudflare Workerは実行時に指定されたコードで、独自のサンドボックス内に新しいWorkerをその場でインスタンス化できます。
Dynamic Worker Loaderは現在オープンベータで、すべての有料Workersユーザーが利用できます。完全な詳細についてはドキュメントを読んでくださいが、以下のような感じです:
let agentCode: string = `
export default {
async myAgent(param, env, ctx) {
// ...
}
}
`;
let chatRoomRpcStub = ...;
let worker = env.LOADER.load({
compatibilityDate: "2026-03-01",
mainModule: "agent.js",
modules: {
"agent.js": agentCode
},
env: {
CHAT_ROOM: chatRoomRpcStub
},
globalOutbound: null,
});
await worker.getEntrypoint().myAgent(param);
それだけです。
100倍高速
Dynamic Workersは、8年前のローンチ以来Cloudflare Workersプラットフォーム全体が構築されてきた同じ基盤サンドボックスメカニズムを使用します:isolatesです。
isolateは、Google Chromeで使用されているのと同じエンジンであるV8 JavaScript実行エンジンのインスタンスです。これがWorkersの仕組みです。
isolateは起動に数ミリ秒、メモリ使用量は数メガバイトです。これは典型的なコンテナより約100倍高速で、10倍から100倍メモリ効率が良いです。
つまり、ユーザーリクエストごとにオンデマンドで新しいisolateを開始し、1つのコードスニペットを実行してから破棄することができます。
無制限のスケーラビリティ
多くのコンテナベースのサンドボックスプロバイダーは、グローバル同時サンドボックス数とサンドボックス作成レートに制限を課しています。Dynamic Worker Loaderにはそのような制限がありません。これまでずっとWorkersが毎秒数百万のリクエストにシームレスにスケールできる同じ技術への単なるAPIだからです。
毎秒100万のリクエストを処理し、すべてのリクエストが個別のDynamic Workerサンドボックスをロードして同時実行したいですか?問題ありません!
ゼロレイテンシ
一回限りのDynamic Workersは通常、それを作成したWorkerと同じマシン(同じスレッドでさえ)で実行されます。ウォームサンドボックスを見つけるために世界中と通信する必要はありません。isolateは非常に軽量なので、リクエストが到着した場所で実行できます。
Dynamic Workersは世界中のCloudflareの数百の拠点すべてでサポートされています。
すべてJavaScript
コンテナと比較した唯一の制約は、エージェントがJavaScriptを書く必要があることです。技術的には、Workers(動的なものを含む)はPythonとWebAssemblyを使用できますが、エージェントによってオンデマンドで書かれるような小さなコードスニペットの場合、JavaScriptの方がはるかに高速にロードして実行されます。
私たち人間はプログラミング言語に強い好みを持つ傾向があり、多くの人がJavaScriptを愛する一方で、Python、Rust、その他無数の言語を好む人もいます。しかし、ここでは人間の話をしているのではありません。AIの話をしているのです。
AIは望む任意の言語を書きます。LLMはすべての主要言語のエキスパートです。JavaScriptでの訓練データは膨大です。JavaScriptは、Web上での性質により、サンドボックス化されるように設計されています。これは仕事に適した正しい言語です。
TypeScriptで定義されたツール
エージェントが有用なことを実行できるようにするには、外部APIと通信する必要があります。アクセスできるAPIについてどのように伝えるのでしょうか?
MCPはフラットなツール呼び出しのスキーマを定義しますが、プログラミングAPIは定義しません。OpenAPIはREST APIを表現する方法を提供しますが、スキーマ自体とそれを呼び出すために書く必要があるコードの両方で冗長です。
JavaScriptに公開されるAPIには、単一の明白な答えがあります:TypeScriptです。
エージェントはTypeScriptを知っています。TypeScriptは簡潔になるように設計されています。非常に少ないトークンで、エージェントにAPIの正確な理解を与えることができます。
interface ChatRoom {
getHistory(limit: number): Promise<Message[]>;
subscribe(callback: (msg: Message) => void): Promise<Disposable>;
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を呼び出すことができます。
つまり、エージェントは次のようなコードを書くことができます:
let history = await env.CHAT_ROOM.getHistory(1000);
return history.filter(msg => msg.author == "alice");
HTTPフィルタリングと認証情報注入
エージェントにHTTP APIを提供したい場合、それは完全にサポートされています。ワーカーローダーAPIのglobalOutboundオプションを使用して、すべてのHTTPリクエストで呼び出されるコールバックを登録できます。このコールバックでは、リクエストを検査、書き換え、認証キーの注入、直接応答、ブロック、その他任意の処理を行うことができます。
例えば、これを使用して認証情報注入(トークン注入)を実装できます:エージェントが認証を必要とするサービスにHTTPリクエストを行うとき、送信時にリクエストに認証情報を追加します。この方法で、エージェント自体は秘密の認証情報を知ることがなく、したがってそれらを漏洩させることができません。
プレーンなHTTPインターフェースの使用は、エージェントが訓練セットにある既知のAPIと通信する場合や、REST APIに基づいて構築されたライブラリをエージェントに使用させたい場合(ライブラリはエージェントのサンドボックス内で実行できます)に望ましい場合があります。
とはいえ、互換性要件がない場合、TypeScript RPCインターフェースはHTTPより優れています:
- 上記のように、TypeScript インターフェースはHTTPインターフェースよりもはるかに少ないトークンで記述できます
- エージェントは同等のHTTPよりもはるかに少ないトークンでTypeScriptインターフェースを呼び出すコードを書くことができます
- TypeScriptインターフェースでは、独自のラッパーインターフェースを定義するため、シンプルさとセキュリティの両方のために、エージェントに提供したい機能を正確に公開するようにインターフェースを絞り込むことが簡単です
- HTTPでは、既存のAPIに対するリクエストのフィルタリングを実装する可能性が高くなります。これは困難です。プロキシがすべてのAPI呼び出しの意味を完全に解釈して適切に許可するかどうかを決定する必要があり、HTTPリクエストは多くのヘッダーやその他のパラメータを持つ複雑なもので、すべてが意味を持つ可能性があるからです。結局、許可したい機能のみを実装するTypeScriptラッパーを書く方が簡単になります
実戦で鍛えられたセキュリティ
isolateベースのサンドボックスの強化は、ハードウェア仮想マシンよりも複雑な攻撃面であるため、困難です。すべてのサンドボックスメカニズムにはバグがありますが、V8のセキュリティバグは典型的なハイパーバイザーのセキュリティバグよりも一般的です。
悪意のある可能性のあるコードをサンドボックス化するためにisolateを使用する場合、追加の多層防御が重要です。例えば、Google Chromeはこの理由で厳格なプロセス分離を実装しましたが、それが唯一の可能な解決策ではありません。
私たちはisolateベースプラットフォームのセキュリティ確保において約10年の経験があります:
- システムは数時間以内にV8セキュリティパッチを本番環境に自動デプロイします — Chrome自体よりも高速です
- セキュリティアーキテクチャは、リスク評価に基づくテナントの動的隔離を備えたカスタム第2層サンドボックスを特徴としています
- MPKなどのハードウェア機能を活用するためにV8サンドボックス自体を拡張しました
- Spectreに対する新しい防御を開発するために主要な研究者とチームを組み(雇用もしました)
- 悪意のあるパターンをスキャンして自動的にブロックしたり、追加のサンドボックス層を適用するシステムもあります
- その他多数
CloudflareでDynamic Workersを使用すると、これらすべてが自動的に得られます。
ヘルパーライブラリ
私たちは多数のライブラリを構築しました