LLMバッチ処理の安定稼働を実現!AWS Lambdaにおける最適なリトライ戦略とアーキテクチャ比較

本記事は、OPTiM TECH BLOG Advent Calendar 2025 Day 10 および Serverless Advent Calendar 2025 Day 12 の記事です!

こんにちは。技術統括本部の今枝です。普段は SaaS 管理プロダクトのOPTiMサスマネや、最近は MDM 製品の OPTiM BizOPTiM Biz Premium の開発も担当しています。

LLM のバッチ処理を AWS Lambda (以下Lambda) で行う場合、LLM を安定して呼び出すためにはリトライ戦略が必要です。 今回は、LLM のバッチ・非同期処理を Lambda で行う場合のリトライ戦略を考えてみたいと思います。

本記事では、各アプローチのメリット・デメリットを比較し、個人的におすすめなリトライ戦略をまとめます。

背景と課題

LLM のバッチ処理を Lambda で実装する際、以下のような課題に直面します。

典型的な課題

  1. レート制限エラー (429 Too Many Requests)

    • LLM プロバイダー側のレート制限により、大量のリクエストを短時間で処理できない
    • 並列実行数を制御しないと、すぐにレート制限に到達してしまう
  2. Lambda の実行時間制限 (最大 15 分)

    • バッチ処理が長時間化すると、Lambda がタイムアウトしてしまう
    • リトライ待機時間中も Lambda が課金され続けるため、コスト効率が悪い
  3. 状態管理の複雑さ

    • 障害発生時に処理済みデータを保持し、途中から再開する仕組みが必要
    • 同じデータを複数回処理しないよう、冪等性を確保する必要がある

これらの課題を解決する適切なリトライ戦略とアーキテクチャの選択が重要になります。

リトライ戦略: 指数バックオフ(Exponential Backoff)とジッター(Jitter)

不安定な LLM API エラーや、レート制限エラーに対処する最も効果的な方法は、指数バックオフ(Exponential Backoff) + ランダムジッター(Jitter)を用いた自動リトライです。 OpenAI エラー軽減プラクティス でも、この戦略を推奨しています。

指数バックオフ(Exponential Backoff)とは

リトライ時の待機時間を指数関数的に増加させる手法です:

例) 1 秒待機, 2 秒待機, 4 秒待機 ...

backoff

ジッター(Jitter)とは

複数のリクエストが同時にリトライすると、再び同時にレート制限に到達してしまいます。 ジッター(Jitter)のランダムな遅延を加えることで、リトライのタイミングを分散させます。

Jitter

レート制限時のバッチ処理で満たすべき要件

  1. レート制限の遵守
    • 並列実行数の制御
    • リクエスト間の適切な待機時間
    • 動的なスロットリング調整
  2. リトライ戦略
    • 要件: 一時的なエラー(429 Too Many Requests など)からの自動復旧
    • アプローチ:
      • 指数バックオフ + ジッターによるリトライ
      • リトライ回数の制限
      • エラー種別に応じた処理分岐
  3. 状態管理とチェックポイント
    • 要件: 障害時に処理済みのデータを保持し、途中から再開できること
    • アプローチ:
      • 処理進捗の永続化
      • チェックポイント機構
      • 冪等性の確保
  4. コスト効率
    • 要件: 待機時間中の不要な課金を避けること
    • アプローチ:
      • 待機時は Lambda を終了し、外部で状態を保持
      • Lambda のメモリ・タイムアウト設定の最適化
  5. 可観測性とデバッグ性
    • 要件: 処理状況の把握とエラー時の原因特定が容易であること
    • アプローチ:
      • 構造化ログの出力
      • メトリクスの記録
      • トレーシング機能

比較対象のアプローチ

上記要件を踏まえ、以下の 4 つのアプローチを比較します:

  1. 通常の Lambda のみ - SQS + Lambda + リトライ機構
  2. AWS Step Functions - Standard/Express Workflows
  3. Lambda 内に エージェントフレームワーク(Mastra) を記述
  4. Lambda Durable Functions - 新しいコードファーストアプローチ

アプローチ 1: Lambda + SQS + カスタムリトライ

概要

最もシンプルなアプローチで、Amazon SQS (以下SQS) のメッセージキューと Lambda の組み合わせにより、リトライ機構を実装します。 SQS の可視性タイムアウトと DLQ (Dead Letter Queue) を活用し、失敗したメッセージを自動的に再処理します。

Lambda to SQS

メリット

  • シンプルで理解しやすい: AWS の基本的なサービスのみを使用
  • コストが安い: SQS と Lambda の従量課金のみ
  • 既存システムとの統合が容易: SQS は多くのサービスと連携可能
  • スケーラビリティが高い: Lambda の並列実行数で自動スケール

デメリット

  • 可視性の低さ: 処理状況の把握には CloudWatch Logs の分析が必要
  • リトライ制御の限界: マネージドなリトライ設定だけで指数バックオフ + ジッターを表現するのは難しく、アプリケーションコードでの実装が必要になる
  • 長時間待機のコスト: Lambda 内で 429 エラー等でスリープすると課金が発生する

適用ケース

  • シンプルな単一ステップのバッチ処理
  • コストを最小限に抑えたい場合

アプローチ 2: AWS Step Functions (Standard Workflows)

概要

AWS Step Functions の Standard Workflows を使用して、複数ステップのワークフローとリトライ戦略を定義します。 ステートマシンとして処理フローを可視化でき、リトライ設定も JSON で宣言的に記述できます。

LLM を前段に置いて、LLM の判断で後続の Lambda の実行も制御することができます。

Step Functionsエージェント活用

特に優秀なのが、LLM のエラー内容で、指数バックオフと Jitter のリトライ戦略を明示的に設定することができ、その際の待機時間に対してコストは発生しないという点です。

SFNエラーハンドリング

実装例: OpenAI 等の特定のエラーリトライ戦略のみ変更する場合

{...
"llmCallLambda": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:llm-call-lambda",
      "InputPath": "$.body",
      "Next": "XXXXXXX",
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2,
          "JitterStrategy": "FULL"
        },
        {
          "Comment": "AppRateLimitErrorHandler",
          "ErrorEquals": [
            "RateLimitError",
            "OpenAIServerError",
            "OpenAIConnectionError",
            "OpenAIConflictError",
            "OpenAIUnprocessableEntityError"
          ],
          "MaxAttempts": 2,
          "IntervalSeconds": 60,
          "BackoffRate": 1.5,
          "JitterStrategy": "FULL"
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "HandleFailure",
          "ResultPath": "$.error"
        }
      ]
    }
}

メリット

  • 可視性が高い: ワークフローの実行状況が GUI で確認できる
  • 宣言的なリトライ設定: JSON でリトライポリシーを定義可能
  • 複雑なワークフローに対応: 条件分岐、並列処理、待機などを組み合わせ可能
  • 長時間実行が可能: Standard Workflows は最大 1 年間実行可能
  • 状態管理が不要: Step Functions が実行状態を管理

デメリット

  • 追加の費用が必要: ステート遷移ごとに課金 (1,000 回の遷移で約 $0.025)
  • 学習コストが高い: Amazon States Language (ASL) の習得が必要

適用ケース

  • 複数ステップの複雑なワークフロー(Agent ベースかつ、Lambda のリソース効率の最大化を図りたい場合)
  • 処理状況の可視化が重要な場合
  • 長時間実行が必要なバッチ処理

アプローチ 3: AI エージェントフレームワーク(Mastra)

概要

次に、TypeScript で記述できる Mastra と比較してみます。 Mastra は TypeScript で記述できる AI エージェントフレームワークで、API としても起動できますが、Lambda にもワークフローを記述できます。 コードファーストなアプローチで、TypeScript の型安全性を活かしながらワークフローを定義できます。 リトライ戦略も簡易的なものが組み込まれています。

Mastra

実装例

export const testWorkflow = createWorkflow({
  // ...
  retryConfig: {
    attempts: 5,
    delay: 2000,
  },
})
  .then(step1)
  .commit();

https://mastra.ai/reference/legacyWorkflows/step-retries#retry-parameters

Mastra は再試行の戦略に複雑な指定ができません。

メリット

  • コードファースト: 慣れた言語でワークフローを記述できる
  • 柔軟性が高い: 複雑なロジックも自由に実装可能
  • テストが容易: 通常の TypeScript コードとしてユニットテスト可能
  • Lambda 内で完結: 外部のオーケストレーションサービス不要

デメリット

  • 状態管理の実装が必要: チェックポイント機構を自前で実装
  • Lambda の実行時間制限: 15 分以内に完了する必要があり、Sleep は基本的に Lambda の実行時間に含まれるため、コストが発生する。
  • Mastra の場合、リトライ戦略が充実していない。

適用ケース

  • インフラ層でリトライロジックをさせたくない場合(アプリ層でリトライロジックも管理したい場合)
  • 既存の TypeScript コードベースに統合したい場合
  • Step Functions の ASL では表現しづらいロジックがある場合

アプローチ 4: Lambda Durable Functions

概要

2025 年 12 月に発表された AWS の新機能で、Azure Durable Functions のような永続的なワークフローを Lambda で実現できます。 コードファーストなアプローチで、async/await を使った自然な記述が可能です。

従来手法との違い

Step Functions との比較:

  • コードで記述できる (ASL 不要)
  • より細かい制御が可能
  • TypeScript の型安全性を活用

AI エージェントフレームワーク(Mastra) との比較:

  • 状態管理が Lambda 内で自動で行われる
  • 待機中の Lambda 課金が発生しない
  • AWS ネイティブで統合が容易
  • Mastra 等のエージェントフレームワークの併用も可能

実装例:

const result = await context.step(
  "call-rate-limited-api",
  async () => {
    const response = await fetch("https://api.example.com/data");

    if (response.status === 429) throw new Error("RATE_LIMIT");
    if (response.status === 504) throw new Error("TIMEOUT");
    if (!response.ok) throw new Error(`API_ERROR_${response.status}`);

    return await response.json();
  },
  {
    retryStrategy: (error, attemptCount) => {
      // Only retry rate limits and timeouts
      const isRetryable =
        error.message === "RATE_LIMIT" || error.message === "TIMEOUT";

      if (!isRetryable || attemptCount >= 3) {
        return { shouldRetry: false };
      }

      // Exponential backoff: 1s, 2s, 4s (capped at 30s)
      const delay = Math.min(Math.pow(2, attemptCount - 1), 30);
      return { shouldRetry: true, delay: { seconds: delay } };
    },
  }
);

参考: https://docs.aws.amazon.com/lambda/latest/dg/durable-execution-sdk-retries.html

メリット

  • コードファーストで直感的: async/await を使った自然な記述
  • コスト効率が高い: 待機中は Lambda が停止し、課金されない
  • 自動的な状態管理: チェックポイントを自動で保存
  • AWS ネイティブ: Step Functions, Mastra 等のエージェントフレームワークと統合が容易
  • デバッグが容易: 通常の TypeScript コードとしてデバッグ可能

デメリット

  • 新しい機能: 2025 年 12 月リリースのため、事例が少なく、東京リージョンでは利用できない
  • 制約事項の把握が必要: オーケストレーター関数の制約 (非決定的コード禁止など) を理解する必要がある
  • エコシステムが未成熟: ベストプラクティスやツールが発展途上
  • 学習コストがある: Lambda Durable Functions の概念を理解する必要がある

適用ケース

  • コードベースで統一した開発をしたい場合
  • 長時間のバッチ処理でコストを抑えたい場合
  • Step Functions の ASL に抵抗がある場合

比較表とまとめ

機能比較マトリクス

要件 Lambda + SQS Step Functions Mastra Lambda Durable Functions
レート制限対応
状態管理/チェックポイント
コスト効率
可観測性/デバッグ性
開発容易性
学習コスト

評価基準:

  • ◎: 非常に優れている
  • ○: 優れている
  • △: 標準的/制約あり

個人的な見解

エラーハンドリングを Lambda 内で行いたい場合は、個人的には Step Functions がおすすめですが、今後の Durable Functions が東京リージョン等で利用可能になれば、そちらを試してみるのが良いかもしれません。

  • Step Functions: 既存の知見を活かしたい、可視化が重要な場合
  • Durable Functions: 今後発展してきて、コードで柔軟に記述したい場合。例えばOpenAI・GeminiのLLM呼び出しの近くでリトライロジックを実装できます。

まとめ

LLM のバッチ処理を Lambda で実装する際のリトライ戦略として、4 つのアプローチを比較しました。

  1. シンプルさ重視 → Lambda + SQS

    • 既存システムへの追加が容易・本当にシンプルな構成
    • 指数バックオフ等の戦略が取りづらい
  2. 可視性・リトライ戦略重視 → Step Functions

    • ワークフロー実行状況の確認が容易(どの処理が落ちたか、Jitter + 指数 Backoff のリトライ戦略が取りやすい)
    • エンタープライズ用途に適している(監査証跡が取れる)
  3. 今後に期待(コスト効率 & モダンな開発) → Lambda Durable Functions

    • 待機中の課金がなく、最もコスト効率が良い
    • コードファーストで直感的な記述が可能
    • 状態管理がわかりづらい
    • 東京リージョンでは利用できない
  4. Mastra 等の AI エージェントフレームワークを活用する場合、基本的にはリトライ戦略としては課金されてしまうため、Lambda の外側で行うことを推奨。

今後のリトライ戦略としては、Step Functions, Lambda Durable Functions のどちらかを採用しつつ、Mastra のような AI エージェントフレームワークも併用することで、開発体験とコスト・エンタープライズの安定性も確保できると思います。 LLM のバッチ処理は、今後ますます重要になっていきます。 それぞれのプロジェクトの要件に応じて、最適なアプローチを選択していきましょう。

www.optim.co.jp

参考資料