連載の現在地:Phase 5「自動化」(2 / 3本目) — 人が呼ばなくても裏で働く仕組みにする
1. 公開 → 2. 費用と安定 → 3. 安全 → 4. 品質 → 5. 自動化

こんな悩みの人へ

毎朝決まった時刻に、AIでニュースを要約して社内に送りたい。アップロードされた大量の文書を、1件ずつAIで処理したい。途中で失敗したときは、最初からではなく続きからやり直したい。そんな方に向けた記事です。

結論

目的に合わせて、Cloudflareの3つの仕組みを使い分けます。

目的

使う仕組み

決まった時刻に動かす

Cron Triggers

大量の処理を分割し、非同期でさばく

Queues

複数の手順の結果を保存し、再試行・再開する

Workflows

3つとも、Workersの無料プランから使えます。定期実行のためだけにサーバーを置く必要はありません。

それぞれの動き方を並べると、次のようになります。

Cron Triggers   毎朝7時(UTCでは22時) --> レポート作成 --> 送信

Queues          アップロード --> キューに積む --> Consumerが10件ずつ処理
                                                  (失敗した分は再試行)

Workflows       集める --> 要約する --> 待つ(時間・承認) --> 配信する
                (成功したステップの結果を保存し、失敗したステップから再試行)

3つは組み合わせて使うこともできます。例えば、Cron Triggersで毎朝起動し、そこからWorkflowsの手順を始める、という形です。

なぜ「あとで動かす処理」は難しいのか

「あとで動かす処理」が難しいのは、まず、画面からの1回のリクエストでは終わらないからです。AIで100件の文書を処理すると、数分以上かかります。その間、画面を待たせておくことはできません。

失敗したときの扱いも、決めておかなければなりません。50件目で失敗したら、最初からやり直すのか、続きからなのか。決めていないと、二重処理や処理漏れが起きます。

もう一つよく見かけるのが、cronを動かすためだけに、サーバーを常に起動しているケースです。

解決策の比較

方法

費用

手間

向いている状況

Cron Triggers

Workersの料金に含まれる

少ない

決まった時刻の軽い処理

Queues

無料プランは1日1万回の操作

中くらい

大量の処理を分けて流す

Workflows

無料プランは1日3,000ステップ

中くらい

手順が多く、途中から再開したい

GitHub Actionsのschedule・外部のジョブサービス

無料枠あり

少ない

Cloudflare以外で動いている処理

どれを選ぶか

「毎朝7時にレポート」のような単発の定期処理なら、Cron Triggersで十分です。「アップロードされた500件を分けて要約」のような処理は、受け付けた時点でQueuesに積み、バッチ単位で処理します。「取得→要約→承認待ち→配信」のように手順があり、途中で待つ必要があるなら、Workflowsが向いています。すでにGitHubで動いているスクリプトを定時に回すだけなら、GitHub Actionsで足ります。

手順

以下は設定と処理の関係を示す抜粋です。Env の型や、レポート作成・要約・配信の補助関数はアプリごとに実装します。

1. Cron Triggers:決まった時刻に動かす

設定ファイルに時刻を書き、scheduled という処理を用意します。時刻は世界標準時(UTC)で書きます。 日本時間の朝7時は、UTCの前日22時です。

{
  "triggers": { "crons": ["0 22 * * *"] }
}
export default {
  async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext) {
    ctx.waitUntil(sendMorningReport(env)); // レポート作成と送信(省略)
  },
};

2. Queues:大量の処理を少しずつさばく

受け付けた処理を「キュー」に積み、Consumerが取り出して実行します。投入順での配信は保証されず、同じメッセージが複数回届くことがあります。失敗時は設定した上限まで再試行されます。

{
  "queues": {
    "producers": [{ "binding": "JOBS", "queue": "summarize-jobs" }],
    "consumers": [{ "queue": "summarize-jobs", "max_batch_size": 10 }]
  }
}
export default {
  // 受け付け:アップロードされた文書をキューに積む
  async fetch(request: Request, env: Env) {
    const { docIds } = (await request.json()) as { docIds: string[] };
    for (let i = 0; i < docIds.length; i += 100) {
      await env.JOBS.sendBatch(docIds.slice(i, i + 100).map((id) => ({ body: { id } })));
    }
    return Response.json({ queued: docIds.length });
  },
  // 処理:キューから取り出して1件ずつ要約
  async queue(batch: MessageBatch<{ id: string }>, env: Env) {
    for (const msg of batch.messages) {
      await summarizeDocument(env, msg.body.id); // 要約処理(省略)
      msg.ack();
    }
  },
};

msg.ack() を1件ごとに呼んでいるのは、途中の1件が失敗しても、処理が終わった分まで再試行されないようにするためです。ackしていないメッセージだけが、再試行の対象になります。

sendBatch() は1回100メッセージ、配列全体256KBまでです。この例は短い文書IDを送る前提で、大きな本文はストレージへ置き参照を送ります。1メッセージの書き込み・読み取り・削除で通常3回の操作を使うため、無料の1万操作は1万文書という意味ではありません。再試行でも操作数が増えます。

3. Workflows:手順の結果を保存して再試行・再開する

成功したステップの結果が保存されるので、再開時にその結果を再利用できます。失敗したステップは再試行されるため、外部への配信などは二重実行に耐える設計にします。再試行上限やCPU上限に達すれば実行は失敗し、完了が無条件に保証されるわけではありません。数時間〜数日待つこともできます。

import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";

export class NewsletterFlow extends WorkflowEntrypoint<Env, { topic: string }> {
  async run(event: WorkflowEvent<{ topic: string }>, step: WorkflowStep) {
    const articles = await step.do("記事を集める", () => collect(event.payload.topic));
    const draft = await step.do("AIで要約する", () => summarize(articles));
    await step.sleep("配信まで待つ", "1 hour");
    await step.do("配信する", () => deliver(draft));
  }
}

この step.sleep() は時間を空けるだけで、担当者の承認は確認しません。承認後に配信する場合は、認証した承認画面からイベントを送り、step.waitForEvent() などで受け取る構成にします。

更新日:2026-11-03

つまずきやすい点

Cronの時刻は、UTCで書きます。日本時間から9時間引いて書き、日付をまたぐことにも注意してください。

経過時間とCPU時間は、別物です。Cron TriggersとQueue Consumerの経過時間の上限は、1回15分です。CPU時間には別の上限があり、Workers Freeでは10ミリ秒です。PaidのCronは、1時間未満の間隔ならCPU30秒、1時間以上の間隔ならCPU15分が上限です。Workflowsに分けても、CPU制限がなくなるわけではありません。

同じ処理が二重に実行されても困らない作りにしておくことも大切です。再試行の仕組みがあるため、同じメッセージが2回処理されることがあります。「処理済み」を記録しておき、2回目は何もしないようにします。

無料プランの保持期間にも注意します。Queues Freeのメッセージ保持期間は24時間です。Workflows Freeでは、完了後の状態・ログが通常3日間保持され、設定で短くすることもできます。実行中のWorkflowが3日で終了する、という意味ではありません。

この記事のまとめ:定期実行や時間のかかる処理が必要になった。悩み:毎朝の作業を自動化、大量の文書を処理、途中からやり直したい。解決の流れ:Cron Triggers→Queues→Workflows。進め方:起動時刻を設定→仕事をキューに投入→ステップに分けて保存→重複と失敗を検証。つまずきやすい点:CronはUTCで設定、CPUと経過時間は別、重複配送に備えるこの記事のまとめ:定期実行や時間のかかる処理が必要になった。悩み:毎朝の作業を自動化、大量の文書を処理、途中からやり直したい。解決の流れ:Cron Triggers→Queues→Workflows。進め方:起動時刻を設定→仕事をキューに投入→ステップに分けて保存→重複と失敗を検証。つまずきやすい点:CronはUTCで設定、CPUと経過時間は別、重複配送に備える

次に出やすい悩み

裏で動く処理が組めたら、あとは何を集めるかと、失敗が続いたときにどう止めるかです。状態を持つエージェントに定期処理を組み込みたいなら「AIエージェントを常時動かしたい」を、定期的にWebページを取得して要約したいなら「Webの情報収集や画面操作を自動化したい」を読んでみてください。一括処理の途中で、LLMのAPIが429を返し始めた場合は、Phase 2の「レート制限やエラーで、AIの機能が止まる」が参考になります。


「毎朝の手作業を自動化したいが、どれを使えばいいか分からない」という段階でもご相談いただけます。定期処理やバッチ処理の設計はCloudflareインフラ構築をご覧ください。

よくある質問

Q. Cron Triggers・Queues・Workflowsは、何を基準に選べばよいですか?

時刻をきっかけに動かすならCron Triggers、大量の仕事を分けて非同期に処理するならQueues、複数の手順の結果を保存して再試行・再開するならWorkflowsを選びます。定時に起動して仕事をキューへ入れるなど、組み合わせて使うこともできます。

Q. 日本時間の朝7時に動かしたい場合、Cronへ何時を指定しますか?

Cron TriggersはUTCで時刻を指定します。日本時間の朝7時はUTCの前日22時です。曜日や月末などの条件がある場合は、日付が変わることも含めて確認してください。Cron Triggersの仕様も参照できます。

Q. Queuesの同じメッセージが、複数回届くことはありますか?

あります。Queuesは少なくとも1回の配送を提供し、同じメッセージが複数回届く場合があります。処理IDと完了状態などを使い、同じ仕事を繰り返しても二重送信や二重更新にならないようにします。配送保証の説明を確認してください。

Q. Workflowsへ分ければ、CPU時間や実行の制限はなくなりますか?

制限がなくなるわけではありません。成功したステップの結果を残して再開できることと、CPU時間や各処理の制限は別です。待機時間と計算に使う時間を分けて考え、処理の分割、再試行の上限、状態やログの保持条件を確認して設計します。

執筆者情報

パブロフ株式会社

IT顧問、AI・業務アプリ開発、SESを通じて、企業のIT活用を支援しています。代表の藤澤 昌隆は、2007年からシステム開発・運用に携わり、アクセンチュアでの業務ITコンサルティングを経て、2019年にパブロフ株式会社を設立しました。

AI活用の支援事例として、営業工程を支える7つの業務別AIアプリの構築を公開しています。アプリの開発に加え、入力・出力の形式、評価基準、テストケースの整備にも取り組んでいます。

記事の内容に関するご質問や誤りのご指摘は、お問い合わせ窓口へお寄せください。

参考資料