数週間前、私たちはDynamic Workersを発表しました。これはWorkersプラットフォームの新機能で、Workerコードをセキュアなサンドボックスにオンザフライで読み込むことができます。Dynamic Worker Loader APIは、Workersがずっと基盤としてきた基本的なコンピュート分離プリミティブ(コンテナではなくisolates)への直接アクセスを本質的に提供します。isolatesはコンテナよりもはるかに軽量で、100倍高速に読み込み、メモリ使用量は1/10です。非常に効率的なため、「使い捨て」として扱うことができます:数行のコードを実行するために起動し、その後破棄します。セキュアなeval()のようなものです。
Dynamic Workersには多くの用途があります。最初の発表では、ツール呼び出しの代替として、AIエージェントが生成したコードを実行する方法に焦点を当てました。この使用例では、AIエージェントがユーザーのリクエストに応じて数行のコードを書いて実行することでアクションを実行します。コードは単発使用で、一度だけ一つのタスクを実行することを意図しており、実行後すぐに破棄されます。
より永続的なコードの生成
しかし、AIにより永続的なコードを生成させたい場合はどうでしょうか?AIに、ユーザーが操作できるカスタムUIを持つ小さなアプリケーションを構築させたい場合は?そのアプリケーションに長期間存続する状態を持たせたい場合は?もちろん、それでもセキュアなサンドボックスで実行させたいでしょう。
これを行う一つの方法は、Dynamic Workersを使用し、単純にWorkerにストレージへのアクセスを提供するRPC APIを提供することです。bindingsを使用して、Dynamic WorkerにリモートのSQLデータベース(Cloudflare D1でサポートされているか、Hyperdrive経由でアクセスするPostgresデータベース - 選択はあなた次第)を指すAPIを提供できます。
しかし、Workersには、この使用例に完璧に適合する可能性のある、ユニークで非常に高速なストレージタイプもあります:Durable Objectsです。
Durable Objectsとは
Durable Objectは、一意の名前を持つ特別な種類のWorkerで、名前ごとに世界中で一つのインスタンスがあります。そのインスタンスには、Durable Objectが実行されるマシンのローカルディスクに存在するSQLiteデータベースが接続されています。これにより、ストレージアクセスが驚くほど高速になります:実質的にレイテンシがゼロです。
おそらく、あなたが本当に望んでいるのは、AIにDurable Object用のコードを書かせ、そのコードをDynamic Workerで実行することです。しかし、どのように?
課題
これは奇妙な問題を提示します。通常、Durable Objectsを使用するには以下が必要です:
DurableObjectを拡張するクラスを書く
- Workerのメインモジュールからエクスポートする
- Wranglerの設定で、このクラス用にストレージをプロビジョニングすることを指定する。これにより、受信リクエストを処理するためのクラスを指すDurable Objectネームスペースが作成される
- ネームスペースを指すDurable Objectネームスペースバインディングを宣言し(または
ctx.exportsを使用)、それを使用してDurable Objectにリクエストを送信する
これはDynamic Workersに自然に拡張されません。
明らかな問題
コードは動的です。Cloudflare APIを全く呼び出すことなく実行します。しかし、Durable Objectストレージは API経由でプロビジョニングする必要があり、ネームスペースは実装クラスを指す必要があります。Dynamic Workerを指すことはできません。
より深い問題
Durable ObjectネームスペースをDynamic Workerに直接指すように設定できたとしても、それを望むでしょうか?エージェント(またはユーザー)がDurable Objectsでいっぱいのネームスペース全体を作成できるようにしたいですか?世界中に分散された無制限のストレージを使用できるようにしたいですか?
おそらくそうではありません。おそらく何らかの制御が必要でしょう。作成するオブジェクトの数を制限したい、または少なくとも追跡したいかもしれません。一つのオブジェクトに制限したいかもしれません(個人的なアプリには十分でしょう)。ログ記録やその他の可観測性を追加したいかもしれません。メトリクス。課金。など。
これらすべてを行うために、本当に望んでいるのは、これらのDurable Objectsへのリクエストが最初にあなたのコードに送られ、そこですべての「ロジスティクス」を行い、その後リクエストをエージェントのコードに転送することです。すべてのDurable Objectの一部として実行されるスーパーバイザーを書きたいのです。
解決策:Durable Object Facets
本日、この問題を解決する機能をオープンベータでリリースします。Durable Object Facetsにより、Durable Objectクラスを動的に読み込んでインスタンス化し、ストレージ用のSQLiteデータベースを提供できます。
Facetsでは:
- まず、あなたが書いたクラスを指す通常のDurable Objectネームスペースを作成します
- そのクラスで、エージェントのコードをDynamic Workerとして読み込み、それを呼び出します
- Dynamic Workerのコードは、Durable Objectクラスを直接実装できます。つまり、文字通り
extends DurableObjectとして宣言されたクラスをエクスポートします
- そのクラスを自分のDurable Objectの「facet」としてインスタンス化します
- facetは独自のSQLiteデータベースを取得し、通常のDurable Objectストレージ APIを介してそれを使用できます
- このデータベースはスーパーバイザーのデータベースとは分離されていますが、両方は同じ全体的なDurable Objectの一部として一緒に保存されます
動作方法
以下は、Durable Objectクラスを動的に読み込んで実行するアプリプラットフォームの簡単で完全な実装です:
import { DurableObject } from "cloudflare:workers";
const AGENT_CODE = `
import { DurableObject } from "cloudflare:workers";
// 呼び出された回数を記憶して返すシンプルなアプリ
export class App extends DurableObject {
fetch(request) {
// 簡単にするためにstorage.kvを使用していますが、
// storage.sqlも利用可能です。両方ともSQLiteでサポートされています。
let counter = this.ctx.storage.kv.get("counter") || 0;
++counter;
this.ctx.storage.kv.put("counter", counter);
return new Response("You've made " + counter + " requests.\\n");
}
}
`;
export class AppRunner extends DurableObject {
async fetch(request) {
let facet = this.ctx.facets.get("app", async () => {
let worker = this.#loadDynamicWorker();
let appClass = worker.getDurableObjectClass("App");
return { class: appClass };
});
return await facet.fetch(request);
}
setCode(code) {
this.ctx.storage.kv.put("codeId", crypto.randomUUID());
this.ctx.storage.kv.put("code", code);
}
#loadDynamicWorker() {
let codeId = this.ctx.storage.kv.get("codeId");
return this.env.LOADER.get(codeId, async () => {
let code = this.ctx.storage.kv.get("code");
return {
compatibilityDate: "2026-04-01",
mainModule: "worker.js",
modules: {
"worker.js": code
},
globalOutbound: null,
};
});
}
}
export default {
async fetch(req, env, ctx) {
let obj = ctx.exports.AppRunner.getByName("my-app");
await obj.setCode(AGENT_CODE);
return await obj.fetch(req);
}
}
この例について
- AppRunnerは、プラットフォーム開発者(あなた)によって書かれた「通常の」Durable Objectです
- AppRunnerの各インスタンスは一つのアプリケーションを管理します。アプリコードを保存し、オンデマンドで読み込みます
- アプリケーション自体は、プラットフォームが
Appという名前であることを期待するDurable Objectクラスを実装してエクスポートします
- AppRunnerはDynamic Workersを使用してアプリケーションコードを読み込み、そのコードをDurable Object Facetとして実行します
- AppRunnerの各インスタンスは、2つのSQLiteデータベースで構成される一つのDurable Objectです:一つは親(AppRunner自体)に属し、もう一つはfacet(
App)に属します
- これらのデータベースは分離されています:アプリケーションはAppRunnerのデータベースを読み取ることはできず、自分のもののみです
実行方法
例を実行するには、上記のコードをworker.jsファイルにコピーし、以下のwrangler.jsoncと組み合わせて、npx wrangler devでローカルで実行してください。
{
"compatibility_date": "2026-04-01",
"main": "worker.js",
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"AppRunner"
]
}
],
"worker_loaders": [
{
"binding": "LOADER"
}
]
}
構築を開始
FacetsはDynamic Workersの機能で、Workers Paidプランのユーザーに即座にベータ版として利用可能です。Dynamic WorkersとFacetsについて詳しく学ぶには、ドキュメントをご確認ください。
Cloudflareの接続クラウドは、企業ネットワーク全体を保護し、顧客がインターネット規模のアプリケーションを効率的に構築するのを支援し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を防ぎ、ハッカーを寄せ付けず、Zero Trustへの道のりをサポートします。任意のデバイスから1.1.1.1にアクセスして、インターネットをより高速で安全にする無料アプリを始めましょう。より良いインターネットの構築を支援するという私たちの使命について詳しく学ぶには、こちらから始めてください。新しいキャリアの方向性をお探しの場合は、私たちの求人情報をご確認ください。