本番環境で30以上のAIエージェントを動かすスタック構成(Pythonなし)
TypeScript、Cloudflare Workers/Queues/KV、Claudeモデルを使って30以上の本番AIエージェントを運用している。PythonもエージェントフレームワークもなしでOK。スタックは意図的にシンプルにしている:Workersがスケジューリングとキューイングを担い、KVが状態を保持し、Anthropic SDKが直接モデル呼び出しを制御する。重要な制約はAIレイヤーではなく、その周囲のインフラだ。
毎週水曜。28,400人以上の読者。無駄なし。
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
目次
2026年5月更新。
TL;DR: TypeScript、Cloudflare Workers/Queues/KV、Claudeモデルを使って30以上の本番AIエージェントを運用している。PythonもエージェントフレームワークもなしでOK。スタックは意図的にシンプルにしている:Workersがスケジューリングとキューイングを担い、KVが状態を保持し、Anthropic SDKが直接モデル呼び出しを制御する。重要な制約はAIレイヤーではなく、その周囲のインフラだ。
[オペレーターの視点] 私は2つのビジネスを経営している:AIコンサルティングブランドと、テキサス州プフルーガービルにあるピックルボール施設「Pickleland」だ。両社合わせて今日現在、本番環境で30以上のエージェントが稼働している。これはデモではなく、実際のスタックだ。
なぜPythonを使わないのか
正直な答え:毎日ウェブサイトとプロダクト開発でTypeScriptを書いている。エージェントのために第二の言語を追加すると、2つのランタイム、2つの依存関係ツリー、2つのデプロイパイプラインが生まれる。生産性コストは理論上の話ではない——過去のプロジェクトで実際に払ったし、繰り返したくないと決めた。
2つ目の理由はCloudflareだ。WorkersはエッジでTypeScriptをネイティブに動かし、Queues、KV、Durable Objects、Cron Triggersが組み込まれている。スケジューリング、状態管理、非同期ジョブ処理など、必要なエージェントインフラはすべて wrangler deploy ひとつで手に入る。同等の運用シンプルさを持つPythonの代替は存在しない。
3つ目の理由:「PythonはAIに向いている」という主張のほとんどは「PythonにはMLライブラリが多い」を意味している。私はモデルをトレーニングしない。APIを呼ぶだけだ。Anthropic SDKはファーストクラスのTypeScriptだ。LangChainや類似ツールは必要のない複雑さだ。エージェントを研究するのではなく出荷するのであれば、シンプルさが勝つ。
コアインフラ:3つのCloudflareプリミティブ
私が動かすすべてのエージェントは、以下の3つのうち少なくとも1つに触れる:
Cloudflare Workers — コンピューティングレイヤー。Workerはエージェントのランタイムだ。トリガー(cron、Queueメッセージ、HTTP)を受け取り、モデル呼び出しを実行し、どこかに出力を書く。コールドスタートは5ms未満。実行制限は無料プランでCPU時間30秒、有料プランで15分。私が作るものはほぼ30秒に収まる。収まらないものはQueuesでファンアウトする。
Cloudflare Queues — 非同期ジョブ処理。タスクがリクエスト以上の時間を要する可能性があるとき、またはファンアウトが必要なとき(12の翻訳を並列生成など)、Queueにメッセージをプッシュし、バインドされたコンシューマーが独立して処理する。ポーリングなし、setTimeoutハックなし。
Cloudflare KV — 軽量な状態管理。エージェントの実行履歴、最後に処理されたタイムスタンプ、キャッシュされたAPIレスポンス。KVは結果整合性だが、エージェントにはそれで十分だ——トランザクションは実行しない。データベースを立ち上げることなく、どのWorkerからも読み書きできるシンプルなキーバリューストアを提供してくれる。
モデルレイヤー:Anthropic SDK、2モデル
使うClaudeモデルはちょうど2つ:
claude-sonnet-4-6— 真の推論が必要なタスク:ブログ記事の執筆、イベントデータの分析、ソーシャルコピーの生成、シーケンスの計画claude-haiku-4-5— 高速・低コストな分類、ルーティング判断、完全な推論が不要な短い抽出
TypeScriptのAnthropic SDKは直感的だ。すべてのモデル呼び出しで使うパターンはこうだ:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
async function runAgent(prompt: string, systemPrompt: string): Promise<string> {
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: systemPrompt,
messages: [{ role: "user", content: prompt }],
});
const block = message.content[0];
if (block.type !== "text") throw new Error("Unexpected content type");
return block.text;
}これがモデルインターフェースのすべてだ。その上に抽象化はない。ツール使用が必要な場合は tools 配列を追加する。ストリーミングが必要な場合は messages.create を messages.stream に切り替える。フレームワークに任せていない——そうしたくもない。
実際のエージェント:コンテンツパイプライン
私が動かす最も複雑なエージェントはコンテンツパイプラインだ。ブログ記事を生成し、12言語に翻訳し、OGカードSVGをレンダリングし、LinkedInプロモーションをドラフトする——すべてドラフトとして、何も公開される前に私のレビューを経る。
Workerのエントリーポイントはこうなっている:
// src/workers/content-pipeline.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { topic, slug } = await request.json<{ topic: string; slug: string }>();
// Step 1: EN記事を生成
const enPost = await generatePost(topic, env);
await env.CONTENT_KV.put(`draft:${slug}:en`, enPost);
// Step 2: Queue経由で翻訳をファンアウト
const locales = ["ar", "de", "es", "fr", "hi", "it", "ja", "ko", "nl", "pt", "ru", "zh"];
for (const locale of locales) {
await env.TRANSLATION_QUEUE.send({ slug, locale, content: enPost });
}
return Response.json({ status: "queued", slug });
},
};各翻訳は独自のWorker呼び出しで実行される。失敗した場合はQueueが自動的にリトライする。スレッド、Promise、レートリミットバックオフを自分で管理することなく、12の並列翻訳が手に入る。
実際のエージェント:イベントプロモーター
Picklelandはピックルボールイベントを開催している。予約プラットフォームをスキャンして今後4日間のイベントを確認し、イベントごとにFacebookグループ投稿をドラフトし、何かが公開される前に私のレビューに提出するエージェントを作った。
システムプロンプト:
const systemPrompt = `You are a community manager for a pickleball facility.
Write Facebook group posts for upcoming events.
Rules:
- Max 150 words per post
- Lead with what's fun about the event, not the price
- Include the booking URL exactly as provided
- Do not use exclamation marks more than once per post
- Tone: friendly, local, not corporate`;ここで重要な制約はモデルではなくワークフローだ。エージェントは毎朝8時にcronトリガーで実行される。ドラフト投稿はレビューキューに落ちる。承認または編集した後、別の公開Workerが発火する。人間が見ていないイベントは投稿されない。
30以上のエージェントを管理する方法
CloudflareのダッシュボードがコントロールプレーンだWorkersは呼び出し回数、エラー率、CPU時間を表示する。各Queueはメッセージスループットと失敗を表示する。KVはストレージ使用量を表示する。
それ以外に:
- 各エージェントは各実行の最後に構造化JSONオブジェクトをログに記録する:
{ agent, status, durationMs, inputTokens, outputTokens, costUsd } - シンプルなAirtableベースでエージェントごとの月間累積コストを追跡している
- コスト閾値を超えたエージェントはレビュー対象にフラグが立つ——通常、プロンプトが冗長すぎるか、Haiku で済むところに Sonnet を使っていることを意味する
規律は技術的なものではない。エージェントが自律的にできることと、私の承認が必要なことを決めることだ。コンテンツドラフト:自律的。顧客に触れるもの:人間によるレビュー。お金を動かすもの:エージェントの仕事ではない。
今から始めるなら変えること
1つ:最初から構造化出力(JSONモード)を設定する。既にリリースしたエージェントに後付けするのではなく。Claudeの自由形式テキストを解析することは税金だ。Zodスキーマを定義して期待するレスポンス形式として渡すと、型付きデータが返ってくる。下流のWorkersが推測する必要がなくなる。
import { z } from "zod";
const EventPostSchema = z.object({
headline: z.string().max(80),
body: z.string().max(600),
bookingUrl: z.string().url(),
suggestedPostTime: z.enum(["morning", "afternoon", "evening"]),
});オペレーターの結論
本番環境で機能するエージェントスタックは、何かが壊れた夜10時にデバッグできるものだ。私にとって、それはTypeScript + Cloudflare + Anthropic SDKだ——最も派手な組み合わせだからではなく、各レイヤーが独立して観測可能、デプロイ可能、交換可能だからだ。フレームワークは抽象化への賭けだ。私は配管を自分で所有することを好む。
毎週水曜。28,400人以上の読者。無駄なし。
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
AIプレイブックをメールでお届け
毎週水曜。28,400人以上の読者。無駄なし。
Check your inbox.
We sent you a confirmation email — click the link inside to complete your subscription. Check spam if you don't see it within a minute.
You're subscribed.
Welcome — the next edition lands in your inbox soon.
You're already on the list — look for it every Wednesday.