Alejandro Rioja.
AI Agents

本番環境で30以上のAIエージェントを動かすスタック構成(Pythonなし)

Alejandro Rioja
Alejandro Rioja
1 分で読める
TL;DR

TypeScript、Cloudflare Workers/Queues/KV、Claudeモデルを使って30以上の本番AIエージェントを運用している。PythonもエージェントフレームワークもなしでOK。スタックは意図的にシンプルにしている:Workersがスケジューリングとキューイングを担い、KVが状態を保持し、Anthropic SDKが直接モデル呼び出しを制御する。重要な制約はAIレイヤーではなく、その周囲のインフラだ。

無料ニュースレター

毎週水曜。28,400人以上の読者。無駄なし。

目次

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つ:

TypeScriptのAnthropic SDKは直感的だ。すべてのモデル呼び出しで使うパターンはこうだ:

typescript
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.createmessages.stream に切り替える。フレームワークに任せていない——そうしたくもない。

実際のエージェント:コンテンツパイプライン

私が動かす最も複雑なエージェントはコンテンツパイプラインだ。ブログ記事を生成し、12言語に翻訳し、OGカードSVGをレンダリングし、LinkedInプロモーションをドラフトする——すべてドラフトとして、何も公開される前に私のレビューを経る。

Workerのエントリーポイントはこうなっている:

typescript
// 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グループ投稿をドラフトし、何かが公開される前に私のレビューに提出するエージェントを作った。

システムプロンプト:

typescript
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はストレージ使用量を表示する。

それ以外に:

規律は技術的なものではない。エージェントが自律的にできることと、私の承認が必要なことを決めることだ。コンテンツドラフト:自律的。顧客に触れるもの:人間によるレビュー。お金を動かすもの:エージェントの仕事ではない。

今から始めるなら変えること

1つ:最初から構造化出力(JSONモード)を設定する。既にリリースしたエージェントに後付けするのではなく。Claudeの自由形式テキストを解析することは税金だ。Zodスキーマを定義して期待するレスポンス形式として渡すと、型付きデータが返ってくる。下流のWorkersが推測する必要がなくなる。

typescript
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だ——最も派手な組み合わせだからではなく、各レイヤーが独立して観測可能、デプロイ可能、交換可能だからだ。フレームワークは抽象化への賭けだ。私は配管を自分で所有することを好む。

続きを読む

AIプレイブックをメールでお届け

毎週水曜。28,400人以上の読者。無駄なし。

↵ すべての結果を見る esc esc で閉じる