Project Think: the next generation of the Agents SDK を紹介します。Project Think は、長時間実行されるエージェントを構築するための新しいプリミティブ群(耐久実行、サブエージェント、サンドボックス化されたコード実行、永続セッション)と、それらを結びつける意見に基づくベースクラスを提供します。プリミティブを使って必要なものだけを組み立てることも、Think ベースクラスを使って素早く始めることもできます。今日、私たちのAIに対する考え方を変えた出来事がありました。Pi、OpenClaw、Claude Code、Codex のようなツールは単純だが強力な考えを示しました: LLM にファイルを読み書きさせ、コードを実行させ、学習内容を記憶させると、それは開発者向けツールというより汎用アシスタントに見えるようになる、ということです。これらのコーディングエージェントはもはや単にコードを書くだけではありません。カレンダー管理、データセット解析、購入交渉、確定申告、ビジネスワークフローの自動化などに使われています。パターンは常に同じです: エージェントがコンテキストを読み、推論し、アクションのためにコードを書き、結果を観察し、反復します。コードは普遍的な行為の手段です。
私たちのチームもこれらのコーディングエージェントを日々利用しており、同じ壁に何度も当たりました:
- ローカルのラップトップや高価なVPS上でしか動かない: 共有や共同作業、デバイス間のハンドオフができない。
- アイドル時も高コスト: エージェントが動いているかどうかに関わらず固定の月額費用が発生する。チームや企業規模に拡大すると急速に費用がかさむ。
- 管理と手動セットアップが必要: 依存関係のインストール、アップデート管理、アイデンティティやシークレットの設定が必要。
さらに構造的な問題があります。従来のアプリケーションは一つのインスタンスが多くのユーザーにサービスを提供しますが、エージェントは一対一(one-to-one)です(Welcome to Agents Week 投稿でも述べた通り)。各エージェントは一意のインスタンスで、1 人のユーザーに対して1つのタスクを実行します。レストランが大量に料理をさばくのに最適化されているのに対して、エージェントは毎回材料も技法もツールも異なるパーソナルシェフに近く、スケールの計算が根本的に変わります。もし1億人の知識労働者が控えめな同時利用率でも各々エージェントを使うなら、数千万の同時セッション用のキャパシティが必要になります。現在のコンテナごとのコストだと持続不可能です。違う基盤が必要です。そこで私たちはこれを作ってきました。
Project Think の紹介
Project Think は Agents SDK 向けの新しいプリミティブ群を提供します:
- Durable execution with fibers: クラッシュ復旧、チェックポイント、自動キープアライブ
- Sub-agents: 独立した子エージェント(各々の SQLite と型付き RPC を持つ)
- Persistent sessions: ツリー構造のメッセージ、フォーク、コンパクション、全文検索
- Sandboxed code execution: Dynamic Workers、codemode、ランタイム npm 解決
- The execution ladder: workspace, isolate, npm, browser, sandbox
- Self-authored extensions: 実行時に自分自身のツールを書けるエージェント
これらのいずれも Agent ベースクラスと直接併用できます。プリミティブで必要なものだけ構築することも、Think ベースクラスで素早く始めることも可能です。以下で各機能の詳細を見ていきます。
長時間実行するエージェント
現状のエージェントはエフェメラル(短命)です。セッション単位で動作し、単一のプロセスやデバイスに結び付けられ、終わると消えます。ラップトップがスリープしたら死んでしまうコーディングエージェントはツールです。要求に応じて起き、割り込み後に作業を続け、ローカルランタイムに依存せず状態を保持するエージェントはインフラに近づきます。これがエージェントのスケーリングモデルを完全に変えます。
Agents SDK は Durable Objects を拡張して、各エージェントにアイデンティティ、永続状態、メッセージで起動する能力を与えます。これはアクターモデルです: 各エージェントはアドレス可能なエンティティで、自身の SQLite データベースを持ちます。ハイバーネート中は計算を消費しません。HTTP リクエスト、WebSocket メッセージ、スケジュール済みアラーム、受信メールなど何かが起きるとプラットフォームがエージェントを起こし、状態をロードしてイベントを渡します。エージェントは作業を行い、再びスリープします。
比較:
- VMs / Containers
- Idle cost
- Full compute cost, always
- Zero (hibernated)
- Scaling
- Provision and manage capacity
- Automatic, per-agent
- State
- External database required
- Built-in SQLite
- Recovery
- You build it (process managers, health checks)
- Platform restarts, state survives
- Identity / routing
- You build it (load balancers, sticky sessions)
- Built-in (name → agent)
例えば、10,000 個のエージェントがそれぞれ 1% の時間だけアクティブなケースを考えると、従来の常時稼働インスタンスなら 10,000 台必要ですが、Durable Objects ベースだと実際に同時アクティブになるのは約 100 台です。この変化により、"power user ごとに高価なエージェントを置く" のではなく、"顧客ごとにエージェント"、"タスクごとにエージェント"、"メールスレッドごとにエージェント" といった設計が現実的になります。新しいエージェントを生成する限界コストはほぼゼロです。
クラッシュを乗り越える: fibers による耐久実行
LLM の呼び出しは 30 秒かかることがあります。マルチターンのエージェントループはさらに長くなることがあります。その間に実行環境が消えるかもしれません: デプロイ、プラットフォーム再起動、リソース制限の到達など。モデルプロバイダへの上流接続が切れ、メモリ内状態が失われ、クライアントはストリームが説明なく止まったように見えます。
runFiber() がこれを解決します。fiber は耐久的な関数呼び出しです: 実行前に SQLite に登録され、いつでも stash() でチェックポイントを取り、onFiberRecovered 経由で再起動時に復元可能です。
import { Agent } from "agents";
export class ResearchAgent extends Agent {
async startResearch(topic: string) {
void this.runFiber("research", async (ctx) => {
const findings = [];
for (let i = 0; i < 10; i++) {
const result = await this.callLLM(`Research step ${i}: ${topic}`);
findings.push(result);
ctx.stash({ findings, step: i, topic });
this.broadcast({ type: "progress", step: i });
}
return { findings };
});
}
async onFiberRecovered(ctx) {
if (ctx.name === "research" && ctx.snapshot) {
const { topic } = ctx.snapshot;
await this.startResearch(topic);
}
}
}
SDK は fiber 実行中にエージェントを自動的にアクティブに保ちます。追加設定は不要です。分単位の作業については keepAlive() / keepAliveWhile() がアクティブ作業中の削除を防ぎます。より長い操作(CI パイプライン、デザインレビュー、ビデオ生成など)では、エージェントが作業を開始してジョブ ID を永続化し、ハイバーネートし、コールバックで起きる、というワークフローが可能です。
仕事の委譲: Facets によるサブエージェント
一つのエージェントがすべてを行うべきではありません。サブエージェントは Facets を介して親と同居する子 Durable Objects で、それぞれ独立した SQLite と実行コンテキストを持ちます:
import { Agent } from "agents";
export class ResearchAgent extends Agent {
async search(query: string) { }
}
export class ReviewAgent extends Agent {
async analyze(query: string) { }
}
export class Orchestrator extends Agent {
async handleTask(task: string) {
const researcher = await this.subAgent(ResearchAgent, "research");
const reviewer = await this.subAgent(ReviewAgent, "review");
const [research, review] = await Promise.all([
researcher.search(task), reviewer.analyze(task)
]);
return this.synthesize(research, review);
}
}
サブエージェントはストレージレベルで隔離されます。各サブエージェントは自身の SQLite データベースを持ち、データの暗黙的な共有はありません。ランタイム側でサブエージェント RPC は関数呼び出しのような低遅延となり、TypeScript がコンパイル時に誤用を捕捉します。
会話を永続化する: Session API
数日や数週間動くエージェントは、従来の平坦なメッセージリスト以上のものを必要とします。実験的な Session API はこれを明示的にモデル化します。Agent ベースクラスで利用可能なセッションはツリー構造として会話を保存し、各メッセージは parent_id を持ちます。これによりフォーク(元の経路を失わずに別案を探索)、非破壊的コンパクション(古いメッセージを削除する代わりに要約)、そして FTS5 による会話履歴の全文検索が可能になります:
import { Agent } from "agents";
import { Session, SessionManager } from "agents/experimental/memory/session";
export class MyAgent extends Agent {
sessions = SessionManager.create(this);
async onStart() {
const session = this.sessions.create("main");
const history = session.getHistory();
const forked = this.sessions.fork(session.id, messageId, "alternative-approach");
}
}
Session は Agent と直接併用可能で、Think ベースクラスが依存するストレージレイヤーでもあります。
ツール呼び出しからコード実行へ
従来のツール呼び出しは使い勝手が悪くなりがちです。モデルがツールを呼び、結果をコンテキスト窓に引き戻し、別のツールを呼び、また引き戻す、という連続が続きます。ツールの数が増えると、トークンコストも手間も増大します。100 ファイルあればモデルへの往復が 100 回になります。しかしモデルはツール呼び出しゲームを逐次的に行うよりも、システムを使うためのコードを書く方が得意です。
これが @cloudflare/codemode の背後にある洞察です: 逐次的なツール呼び出しの代わりに、LLM に単一のプログラムを書かせて全体のタスクを処理させます。
const files = await tools.find({ pattern: "**/*.ts" });
const results = [];
for (const file of files) {
const content = await tools.read({ path: file });
if (content.includes("TODO")) {
results.push({ file, todos: content.match(/\/\/ TODO:.*/g) });
}
}
return results;
100 回の往復の代わりに単一のプログラムを実行するだけです。これによりトークン消費が減り、実行が速くなり、結果も良くなります。Cloudflare API MCP サーバはこれを大規模で示しています。私たちは search() と execute() の 2 つのツールだけを公開し、それでも約 1,000 トークンを消費します。これは naive なエンドポイントごとのツール設計(約 1.17 million tokens)に比べ 99.9% の削減になります。
欠けていたプリミティブ: 安全なサンドボックス
モデルにユーザーの代わりにコードを書かせると決めたとき、次の問いは "そのコードはどこで実行するか?" です。将来的にではなく今、このユーザーのために、このシステムに対して、厳密に定義された権限で実行できる場所が必要です。Dynamic Workers はそのサンドボックスです。ランタイムでミリ秒単位にスピンアップする新しい V8 isolate、数メガバイトのメモリ。これはコンテナに比べておよそ 100x 速く、最大 100x メモリ効率的です。各リクエストごとに新しい isolate を起動してスニペットを実行し、破棄できます。
重要な設計選択はケイパビリティモデルです。一般目的のマシンから始めて制約しようとする代わりに、Dynamic Workers はほとんどの周辺権限を持たない状態から始めます(globalOutbound: null、ネットワークアクセスなし)で、開発者がバインディングを通じて個別にリソース単位で権限を与えます。これにより「このコードがやりすぎないようにどう止めるか?」ではなく、「このコードに正確に何をさせたいか?」を設計することになります。エージェント基盤には適切な問いです。
実行のラダー(Execution ladder)
このケイパビリティモデルから自然に、エージェントが必要に応じて段階的にエスカレーションする計算環境のスペクトラム、いわゆる実行ラダーが導かれます:
- Tier 0: Workspace — SQLite と R2 にバックされた耐久的な仮想ファイルシステム。読み書き、編集、検索、grep、diff。
- Powered by @cloudflare/shell
- Tier 1: Dynamic Worker — ネットワークアクセスなしのサンドボックス化された isolate 上で LLM が生成した JavaScript を実行。
- Powered by @cloudflare/codemode
- Tier 2: npm を追加 — @cloudflare/worker-bundler がレジストリからパッケージを取得し、esbuild でバンドルして Dynamic Worker にロード。エージェントは import { z } from "zod" と書くだけで動く。
- Tier 3: Cloudflare Browser Run を使ったヘッドレスブラウザ — ナビゲート、クリック、抽出、スクリーンショット。サービスが MCP や API をまだサポートしていない場合に有用。
- Tier 4: Cloudflare Sandb... (元記事の続きの説明がここに続きます)
以上が Project Think の主要コンポーネントと設計方針の概要です。プリミティブを組み合わせることで、長時間実行できる、回復性があり、分離されたサブエージェントと持続的な会話を持つ、安全なコード実行環境を備えた次世代のエージェントを Cloudflare 上で構築できます。