Alejandro Rioja.
AI Agents

イベント駆動型 vs スケジュール型エージェント:どの仕事にどのパターンを使うか

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

ユーザーアクションが即時応答を必要とする場合はイベント駆動型を使う——数秒以上の遅延で体験が壊れる。タイミングが予測可能なバッチ処理や定期処理にはスケジュール型を使う。制約:イベント駆動型はステートレスかつ高速でなければならない。スケジュール型はステートフルで多少遅くても構わない。

無料ニュースレター

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

目次

2026年5月更新。

TL;DR: ユーザーアクションが即時応答を必要とする場合はイベント駆動型を使う——数秒以上の遅延で体験が壊れる。タイミングが予測可能なバッチ処理や定期処理にはスケジュール型を使う。制約:イベント駆動型はステートレスかつ高速でなければならない。スケジュール型はステートフルで多少遅くても構わない。

[オペレーターの視点] 私はコンサルティングブランドとPickleland(テキサス州Pflugerville のピックルボール施設)で30以上の本番エージェントを運用している。どれも2つのパターンのどちらかに当てはまる:イベントで起動するか、時計で起動するか。この選択を誤るとお金が無駄になり、壊れた体験を届けることになる。

2つのパターンをわかりやすく

イベント駆動型エージェントは何かが起きたから目を覚ます。予約が入った。コメントが投稿された。フォームが送信された。トリガーは外部にあり、タイミングは予測不能だ。仕事:素早く反応すること。

スケジュール型エージェントは時計が言ったから目を覚ます。毎朝7時。毎週日曜18時。毎時0分。トリガーは内部にあり、完全に予測可能だ。仕事:丁寧な処理を行うこと。

それだけだ。複雑に考えるな。アーキテクチャは1つの問いへの答えから決まる:ユーザーやシステムは今すぐ応答が必要か、特定の時刻まで待てるか?

イベント駆動型:ソーシャルリプライエージェント

私のソーシャルリプライエージェントは、監視しているFacebookの投稿に新しいコメントが届くたびに起動する。エージェントはコメントを読み、意図を分類し(質問、クレーム、褒め言葉、スパム)、返信を下書きして投稿する——信頼度が低ければ人間によるレビュー用にフラグを立てる。

往復全体が30秒以内に完了しないと、返信が古く感じられる。これはイベント駆動型の問題だ。

ソーシャル監視サービスからのwebhookを処理する、簡略化したCloudflare Workerを示す:

typescript
// workers/social-reply.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    // webhookの署名を検証
    const sig = request.headers.get("x-webhook-signature") ?? "";
    const body = await request.text();
    const valid = await verifySignature(body, sig, env.WEBHOOK_SECRET);
    if (!valid) return new Response("Unauthorized", { status: 401 });

    const event = JSON.parse(body) as SocialCommentEvent;

    // 分類して返信 — 200を素早く返すためにasyncで処理
    env.REPLY_QUEUE.send(event);

    return new Response("OK", { status: 200 });
  },
};

// キューコンシューマー — 実際のAI処理を行う
export const queue: ExportedHandlerQueueHandler<Env, SocialCommentEvent> =
  async (batch, env) => {
    for (const msg of batch.messages) {
      const comment = msg.body;

      const classification = await classifyComment(comment.text, env);
      if (classification.intent === "spam") {
        msg.ack();
        continue;
      }

      const reply = await draftReply(comment, classification, env);

      if (classification.confidence > 0.85) {
        await postReply(comment.postId, comment.id, reply, env);
      } else {
        await flagForReview(comment, reply, env);
      }

      msg.ack();
    }
  };

2点に注目してほしい。第一に、fetchハンドラーはすぐに200を返し、実際の処理をキューに委譲する。これによりwebhookの応答を高速に保ち、監視サービスがリトライするのを防ぐ。第二に、キューコンシューマーがオープンなHTTP接続からの時間的プレッシャーなしに実際のAI呼び出しを実行する。

スケジュール型:Picklelandイベントプロモーター

Picklelandはコートとイベントを管理している。毎週、席を埋めるために誰かが今後のイベントを適切なFacebookグループに投稿する必要がある。これは純粋な定期バッチ処理だ——ユーザーアクションはトリガーにならず、リアルタイムである必要もない。

Picklelandイベントプロモーターはcronで動き、予約システムから4日以内のイベントを確認し、マッチした各Facebookグループ向けに投稿の下書きを作成し、何かが公開される前に私のレビュー用に提示する。

typescript
// workers/event-promoter.ts
export default {
  async scheduled(
    event: ScheduledEvent,
    env: Env,
    ctx: ExecutionContext
  ): Promise<void> {
    ctx.waitUntil(runPromoter(env));
  },
};

async function runPromoter(env: Env): Promise<void> {
  // 予約システムからイベントを取得
  const upcomingEvents = await fetchUpcomingEvents(env, { daysAhead: 4 });

  if (upcomingEvents.length === 0) return;

  const drafts: PromoDraft[] = [];

  for (const event of upcomingEvents) {
    // 各イベントを適切なFBグループにマッチ
    const groups = await matchFacebookGroups(event, env);

    for (const group of groups) {
      const post = await draftPromoPost(event, group, env);
      drafts.push({ event, group, post });
    }
  }

  // レビュー用にAirtableに下書きを保存 — 何も自動投稿しない
  await saveDraftsForReview(drafts, env);

  // Slackで私に通知
  await notifyOperator(
    `${drafts.length}件のプロモ下書きがレビュー待ち`,
    env
  );
}

全体をつなぐwrangler設定:

toml
# wrangler.toml
[[triggers]]
crons = ["0 18 * * 0"]  # 毎週日曜18時 UTC

スケジュール型エージェントがイベント駆動型にできないことに注目してほしい:複数のイベントを繰り返し処理し、データベースに書き込み、サマリー通知を送る。バッチ処理をしているのだ。イベント駆動型エージェントは軽量に保ち、素早く応答する必要がある。

スケジュール型:デイリーブリーフ

毎朝7時にデイリーブリーフエージェントが動く。夜間のメール、カレンダー、最重要タスク、関連としてフラグを立てたニュースを取得する。すべてを1つのドキュメントにまとめてAI Workspaceフォルダに置く。

これは純粋なスケジュール型だ。トリガーとなるイベントは存在しない——ただ毎朝仕事を始める前に欲しいのだ。

typescript
// workers/daily-brief.ts
export default {
  async scheduled(
    event: ScheduledEvent,
    env: Env,
    ctx: ExecutionContext
  ): Promise<void> {
    ctx.waitUntil(buildDailyBrief(env));
  },
};

async function buildDailyBrief(env: Env): Promise<void> {
  const [emails, calendar, tasks] = await Promise.all([
    fetchOvernightEmails(env),
    fetchTodayCalendar(env),
    fetchTopTasks(env),
  ]);

  const brief = await synthesizeBrief({ emails, calendar, tasks }, env);

  await writeToWorkspace(brief, env);
}
toml
[[triggers]]
crons = ["0 7 * * *"]  # 毎日7時 UTC

並列のPromise.allは意図的だ。スケジュール型エージェントには待っている人間がいない——でも必要以上に遅くすべきではない。すべてのデータソースを並列に取得し、AIの合成は一度だけ実行する。

イベント駆動型が失敗するとき

最もよく見る失敗パターン:ハンドラーでやりすぎるイベント駆動型エージェントを作ること。

予約が入る。エージェントは顧客プロフィールを取得し、3つの外部APIからエンリッチし、パーソナライゼーションモデルを実行し、CRMに書き込み、確認メールを送り、ダッシュボードを更新する。全体で45秒かかる。予約プラットフォームは200を素早く受け取れなかったのでリトライする。これでエージェントが2回動く。

修正方法はソーシャルリプライエージェントと同じ:即座に200を返し、イベントをキューに送り、キューコンシューマーに重い処理を非同期でやらせる。

もう1つの失敗パターン:実際には定期的な作業にイベント駆動型を使うこと。「週次サマリーを送る」はイベントではない。合成的なcron webhookに接続するな——適切なスケジュールトリガーを使え。

スケジュール型が失敗するとき

スケジュール型エージェントは、仕事が実際にlatency敏感なときに失敗する。ユーザーがフォームを送信して処理エージェントが5分cronで動いていたら、ユーザーは最大5分間スピナーを見続ける。これはスケジュールジョブではない——スケジュール済みのふりをした遅いイベント駆動型ジョブだ。

もう1つの失敗:無制限の処理に膨らむスケジュール型エージェント。cronが毎分動いて各呼び出しが何百ものレコードを処理できるなら、CloudflareのCPU制限にすぐ達する。cronの間隔を延ばすか、呼び出しごとの作業を制限するキューを追加するか、長時間の調整にはDurable Objectsに切り替える。

パターンを組み合わせる:予約パイプライン

一部のワークフローは本当に両方が必要だ。Pickleballの予約パイプラインはこう動く:

  1. イベント駆動型:新規予約webhook → 予約を確認、顧客に領収書を送信、空き状況を更新。10秒以内に完了する必要がある。
  2. スケジュール型:毎週日曜 → 先週の予約をすべてレビュー、サマリーレポートを生成、異常をフラグ(重複予約、通常外のキャンセル率)。

同じドメイン、2つのパターン、2つのエージェント。イベント駆動型がリアルタイムのユーザー体験を担う。スケジュール型が週次の運営レビューを担う。データベースは共有するがそれ以外は分離している。

「全部やる」1つのエージェントにまとめようとするな。イベントには遅すぎ、バッチ処理にはリアルタイムフローと結合しすぎるものができあがる。

Cloudflare Workers:両方に最適なインフラである理由

Cloudflare Workersは両方のパターンをネイティブに処理する:

エッジデプロイメントにより、イベント駆動型エージェントはグローバルに高速応答する。無料枠は両方のパターンを何も使わずにプロトタイプするのに十分寛大だ。統一されたwrangler.toml設定により、2つのパターンに対して2つの別々のインフラ設定を管理する必要がない。

Workersがうまく解決できない唯一のこと:数分以上実行する必要のあるエージェント。その場合はDurable Objectsを使うか、より長時間動くバックエンドに委任する。

オペレーターの結論

エージェントのコードを1行書く前にパターンを決める。人間が待っているものにはイベント駆動型、時計で動くものにはスケジュール型。イベント駆動型のハンドラーは軽量に——素早く返し、作業をキューに入れる。スケジュール型エージェントは並列に——並列化できるものを直列化するな。アーキテクチャはシンプルだ。それを破ることが複雑さの源になる。

続きを読む

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

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

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