イベント駆動型 vs スケジュール型エージェント:どの仕事にどのパターンを使うか
ユーザーアクションが即時応答を必要とする場合はイベント駆動型を使う——数秒以上の遅延で体験が壊れる。タイミングが予測可能なバッチ処理や定期処理にはスケジュール型を使う。制約:イベント駆動型はステートレスかつ高速でなければならない。スケジュール型はステートフルで多少遅くても構わない。
毎週水曜。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: ユーザーアクションが即時応答を必要とする場合はイベント駆動型を使う——数秒以上の遅延で体験が壊れる。タイミングが予測可能なバッチ処理や定期処理にはスケジュール型を使う。制約:イベント駆動型はステートレスかつ高速でなければならない。スケジュール型はステートフルで多少遅くても構わない。
[オペレーターの視点] 私はコンサルティングブランドとPickleland(テキサス州Pflugerville のピックルボール施設)で30以上の本番エージェントを運用している。どれも2つのパターンのどちらかに当てはまる:イベントで起動するか、時計で起動するか。この選択を誤るとお金が無駄になり、壊れた体験を届けることになる。
2つのパターンをわかりやすく
イベント駆動型エージェントは何かが起きたから目を覚ます。予約が入った。コメントが投稿された。フォームが送信された。トリガーは外部にあり、タイミングは予測不能だ。仕事:素早く反応すること。
スケジュール型エージェントは時計が言ったから目を覚ます。毎朝7時。毎週日曜18時。毎時0分。トリガーは内部にあり、完全に予測可能だ。仕事:丁寧な処理を行うこと。
それだけだ。複雑に考えるな。アーキテクチャは1つの問いへの答えから決まる:ユーザーやシステムは今すぐ応答が必要か、特定の時刻まで待てるか?
イベント駆動型:ソーシャルリプライエージェント
私のソーシャルリプライエージェントは、監視しているFacebookの投稿に新しいコメントが届くたびに起動する。エージェントはコメントを読み、意図を分類し(質問、クレーム、褒め言葉、スパム)、返信を下書きして投稿する——信頼度が低ければ人間によるレビュー用にフラグを立てる。
往復全体が30秒以内に完了しないと、返信が古く感じられる。これはイベント駆動型の問題だ。
ソーシャル監視サービスからのwebhookを処理する、簡略化したCloudflare Workerを示す:
// 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グループ向けに投稿の下書きを作成し、何かが公開される前に私のレビュー用に提示する。
// 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設定:
# wrangler.toml
[[triggers]]
crons = ["0 18 * * 0"] # 毎週日曜18時 UTCスケジュール型エージェントがイベント駆動型にできないことに注目してほしい:複数のイベントを繰り返し処理し、データベースに書き込み、サマリー通知を送る。バッチ処理をしているのだ。イベント駆動型エージェントは軽量に保ち、素早く応答する必要がある。
スケジュール型:デイリーブリーフ
毎朝7時にデイリーブリーフエージェントが動く。夜間のメール、カレンダー、最重要タスク、関連としてフラグを立てたニュースを取得する。すべてを1つのドキュメントにまとめてAI Workspaceフォルダに置く。
これは純粋なスケジュール型だ。トリガーとなるイベントは存在しない——ただ毎朝仕事を始める前に欲しいのだ。
// 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);
}[[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の予約パイプラインはこう動く:
- イベント駆動型:新規予約webhook → 予約を確認、顧客に領収書を送信、空き状況を更新。10秒以内に完了する必要がある。
- スケジュール型:毎週日曜 → 先週の予約をすべてレビュー、サマリーレポートを生成、異常をフラグ(重複予約、通常外のキャンセル率)。
同じドメイン、2つのパターン、2つのエージェント。イベント駆動型がリアルタイムのユーザー体験を担う。スケジュール型が週次の運営レビューを担う。データベースは共有するがそれ以外は分離している。
「全部やる」1つのエージェントにまとめようとするな。イベントには遅すぎ、バッチ処理にはリアルタイムフローと結合しすぎるものができあがる。
Cloudflare Workers:両方に最適なインフラである理由
Cloudflare Workersは両方のパターンをネイティブに処理する:
fetchハンドラー → イベント駆動型(webhook、APIコール)scheduledハンドラー → cron型(wrangler.tomlの[[triggers]]経由)queueコンシューマー → HTTPレイヤーから分離した非同期処理
エッジデプロイメントにより、イベント駆動型エージェントはグローバルに高速応答する。無料枠は両方のパターンを何も使わずにプロトタイプするのに十分寛大だ。統一されたwrangler.toml設定により、2つのパターンに対して2つの別々のインフラ設定を管理する必要がない。
Workersがうまく解決できない唯一のこと:数分以上実行する必要のあるエージェント。その場合はDurable Objectsを使うか、より長時間動くバックエンドに委任する。
オペレーターの結論
エージェントのコードを1行書く前にパターンを決める。人間が待っているものにはイベント駆動型、時計で動くものにはスケジュール型。イベント駆動型のハンドラーは軽量に——素早く返し、作業をキューに入れる。スケジュール型エージェントは並列に——並列化できるものを直列化するな。アーキテクチャはシンプルだ。それを破ることが複雑さの源になる。
毎週水曜。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.