title: "SQS・Lambda・Step Functions・AgentCore Runtimeのリトライ設計――タイムアウトと二重実行を防ぐ"
tags:
- name: AWS
- name: StepFunctions
- name: lambda
- name: SQS
- name: AmazonBedrock
private: true
updated_at: ''
id: null
organization_url_name: null
slide: false
ignorePublish: false
はじめに
CloudFront・WAF・API Gatewayの後ろにLambda workerを置き、Amazon Bedrock AgentCore Runtimeへ処理を渡すバックエンドを考えます。処理が長くなると、HTTPリクエストの中で結果を待つ構成より、ジョブを受け付けて非同期に実行する構成が扱いやすくなります。
そこでSQSやStep Functionsを導入しますが、各サービスでリトライを増やすだけでは安全になりません。応答が失われた際、AgentCoreでは処理が続いているかもしれないからです。
この記事では、Step Functions Standardを業務工程のリトライの中心にし、SQSに受付・配送を担当させる構成を紹介します。2026年10月7日時点のAWS公式資料を基にした設計案です。設定値は初期案であり、実環境へのデプロイ・負荷試験を行った結果ではありません。
1. SQSとStep Functionsの役割を分ける
SQSは仕事をためるキュー、Step Functionsは仕事の手順を管理するオーケストレーターです。両方必要とは限りません。バースト吸収や受付と起動の分離が必要ならSQSを使い、不要なら受付Lambdaから直接StartExecutionする構成も選べます。
この図ではWAFをCloudFrontへ関連付けます。認証、ジョブ所有者の確認、アクセス権の判定はAPI・アプリケーション側で行います。
起動Lambdaはワークフロー開始を確認したらSQSメッセージの処理を成功として返します。AgentCoreの完了までSQSメッセージを保持しません。 開始後の失敗はStep Functionsに任せます。
受付では、キューへの投入成功を確認してから202を返します。状態DBとキューに別々に書き込む場合は、その間の障害も設計対象です。例えばDBに受付情報と送信待ち情報を一緒に記録し、後からキューへ確実に配送するOutbox方式、または未配送ジョブを回収する仕組みを用意します。202を返したジョブが起動されないまま残る状態を防ぐためです。
2. Standardを第一候補にする
長時間のAgentCore処理を待ち、工程ごとの失敗を追跡する用途ではStandard Workflowが第一候補です。
| 比較項目 | Standard | Express |
|---|---|---|
| 最大実行時間 | 1年 | 5分 |
| Callback | 対応する統合で利用可能 | 非対応 |
| 実行モデル | exactly-once。ただし明示的なRetryは再実行する | 非同期はat-least-once、同期はat-most-once |
| 主な適性 | 長時間・状態を保持する工程 | 短時間・高頻度・冪等な処理 |
Standardの実行保証は、外部APIの副作用まで一括して重複排除する保証ではありません。明示的なRetry、クライアントの再送、結果不明への対応を考えると、業務単位の冪等性は必要です。AWS:ワークフロータイプの選択
3. リトライの担当を決める
| 層 | 再試行する対象 | 方針 |
|---|---|---|
| クライアント | ジョブ受付の通信 | 同じIdempotency Keyを使う |
| 受付SQS | ワークフロー開始までの配送 | StartExecution成功後は責任を移す |
| AWS SDK | 短い通信・サービス障害 | 試行数を明示し少数回にする |
| Step Functions | 個々の業務工程 | エラーを分類してRetry・Catchする |
| AgentCore内のツール | 個々の外部操作 | 副作用がある操作は操作単位で冪等化 |
| 運用者 | 原因修正後の再実行 | 既存の結果・副作用を確認する |
例えば同じ処理をSQSで5回、Step Functionsで初回を含めて4回、SDKで合計3回試行すると、条件によっては5×4×3=60回の呼び出しに増えます。これは設計上の計算例で、AWSが常に60回実行するという意味ではありません。
SDKの既定値は言語・バージョン・設定に依存するため、利用するSDKで総試行数を確認して明示します。AWS:SDKのRetry設定
4. エラーを「一時障害」「恒久障害」「結果不明」に分ける
| 状況 | 推奨対応 |
|---|---|
| Throttling・429 | バックオフ。継続するなら投入量を減らす |
| 一時的な500・RetryableConflict | 冪等性を前提に少数回再試行 |
| 入力不正・Validation | 再試行せず入力修正へ |
| AccessDenied・リソース設定不備 | 運用通知・設定修正へ |
| クォータ超過 | 流量調整やクォータ確認へ |
| タイムアウト・接続断 | ジョブ状態を確認してから判断 |
| 業務条件を満たさない | Choiceなどで業務結果として扱う |
| LLM出力の品質不足 | 品質改善の反復回数・費用を別途制限 |
InvokeAgentRuntimeにはThrottlingException、InternalServerException、RetryableConflictExceptionなどが定義されています。RuntimeClientErrorは複数の原因を持ちうるため、内容を見ずに一律で再試行対象にしません。AWS:InvokeAgentRuntime API
APIが正常応答を返しても、アプリケーションの応答本文が失敗を示す場合があります。Lambdaがエラー文字列を通常の戻り値として返すだけでは、Step FunctionsのRetryは発火しません。アダプターで例外を送出するか、応答をChoiceで判定する契約を決めます。
5. Retryには指数バックオフ・上限・ジッターを設定する
初期案はIntervalSeconds=2、BackoffRate=2、MaxAttempts=3、MaxDelaySeconds=30、JitterStrategy=FULLです。
MaxAttemptsは再試行回数です。3を指定すると、初回を含め最大4回になります。FULLでは待機時間がランダム化され、この例の待機範囲は0〜2秒、0〜4秒、0〜8秒です。MaxDelaySecondsは待機を長くしすぎないための上限です。AWS:Retry・Catch
以下はLambdaアダプターを呼び出すTaskの抜粋です。状態名・ARNの置換と後続状態の実装が必要で、そのままデプロイする定義ではありません。App.*は独自のエラー名です。
{
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "<adapter-lambda-arn>",
"Payload.$": "$"
},
"ResultPath": "$.dispatch",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"BackoffRate": 2,
"MaxAttempts": 3,
"MaxDelaySeconds": 30,
"JitterStrategy": "FULL"
},
{
"ErrorEquals": ["App.TransientSafeToRetry"],
"IntervalSeconds": 5,
"BackoffRate": 2,
"MaxAttempts": 2,
"MaxDelaySeconds": 30,
"JitterStrategy": "FULL"
}
],
"Catch": [
{
"ErrorEquals": [
"App.OutcomeUnknown",
"States.Timeout",
"Sandbox.Timedout"
],
"ResultPath": "$.error",
"Next": "ReconcileJob"
},
{
"ErrorEquals": ["States.ALL"],
"ResultPath": "$.error",
"Next": "RecordFailure"
}
],
"Next": "WaitForCompletion"
}
アダプターはAgentCoreの開始確認までを短時間で行います。SDKの通信タイムアウトをLambdaの制限より短くし、エラー分類を返す余裕を残します。Task全体にも上限を設けますが、タイムアウトで外部処理が自動停止すると仮定してはいけません。
ReconcileJobはジョブ状態を確認する工程です。States.ALLをRetryに使うと入力不正なども繰り返すため、再試行対象を列挙します。またStates.ALLで捕捉できないエラーや実行全体のタイムアウトに備え、ワークフロー外の失敗監視も用意します。
6. タイムアウトを「失敗」と決めつけない
未開始を確認できない場合はUNKNOWNとして保持し、一定時間の照合や運用判断へ進めます。DBに状態がないという事実だけで、直ちに未実行と判断できるとは限りません。
冪等性には次の情報を使います。
- jobId:同じ業務依頼を識別する。
- 入力ハッシュ:同じキーで異なる入力を受け付けない。
- 状態:ACCEPTED、RUNNING、SUCCEEDED、FAILED、UNKNOWNなど。
- 世代番号:旧workerが遅れて更新することを防ぐ。
- 操作ID:外部更新・送信などの副作用を識別する。
AgentCore側でも、処理開始時に条件付き書き込みで実行権を取得します。完了済みなら既存結果を返し、実行中なら再起動しません。単に起動Lambdaでフラグを立てるだけでは、開始直後の障害や古いworkerの継続動作を十分に扱えません。
runtimeSessionIdは会話・セッションの識別子です。API仕様にジョブ重複排除の保証はないため、jobIdの代わりに使いません。
外部操作と状態DBを一つのトランザクションにできない場合、「外部更新は成功したが完了記録が失敗した」という窓が残ります。外部サービスの冪等性キー、既存結果の照会、補償処理などで対処します。
7. AgentCoreの非同期処理をどう待つか
AgentCore SDKは応答後のバックグラウンド処理に対応しています。ただしSDKのタスク追跡・セッションの健全性管理だけで、アプリケーションの永続的なジョブ管理が完成するわけではありません。結果やジョブ状態は外部ストレージへ保存します。AWS:AgentCoreの非同期処理
| 方式 | 向いている条件 | 注意点 |
|---|---|---|
| Callback | 完了時に通知できる | Task Tokenと通知再送を管理 |
| Wait→状態照会→Choice | 状態DBを中心に作りたい | 照会間隔・業務締切を設定 |
| 同期呼び出し | 短時間で結果が返る | 応答サイズ・時間・形式を検証 |
Callbackでは、例えばlambda:invoke.waitForTaskTokenで起動アダプターにトークンを渡し、完了通知用LambdaからSendTaskSuccessまたはSendTaskFailureを呼びます。これはアプリケーション側で構築する連携です。AWS:Callbackパターン
Task TokenはログやLLMへのプロンプトへ混入させず、必要なコンポーネントだけに渡します。結果保存後に完了通知し、通知の失敗に備えて再送情報を永続化します。タイムアウト後に届いた古い通知は、現在のジョブ世代と照合して扱います。
現在はInvokeAgentRuntimeをStep FunctionsのAWS SDK統合から直接呼び出せます。ただし、非同期ジョブの完了待機が自動で付くわけではありません。Lambdaアダプターが不要なケースもありますが、応答の変換や独自エラー分類が必要ならアダプターを残す価値があります。AWS:対応SDK統合
8. SQS・再実行・監視の実務ポイント
SQSの可視性タイムアウトは、起動Lambdaのタイムアウトの6倍以上にバッチウィンドウを加える設定がAWSの推奨です。maxReceiveCountは5以上を初期基準にし、Partial Batch Responseで失敗メッセージだけを再配送します。AWS:SQSイベントソースの設定
StandardのStartExecutionは、実行中の同じ名前・同じ入力に対して冪等です。実行名をjobIdから決め、入力を固定します。完了済みや入力不一致ではExecutionAlreadyExistsとなるため、既存ジョブとの一致を確認して処理します。完了済みという理由だけで新しい実行名を作ると、業務処理が重複します。AWS:StartExecution
入口のDLQは「開始できなかったジョブ」の隔離先です。開始済みワークフローの業務失敗とは分け、運用上の復旧方法も区別します。
同時実行は、起動Lambdaの数だけで管理できません。短時間で大量の長時間ジョブを開始できるからです。AgentCore・モデル・外部ツールのクォータを基に、実行中ジョブのスロットや投入レートを制御します。MapのMaxConcurrencyはそのMap内の制限なので、複数ワークフローを横断する全体制限が必要なら別途設計します。
監視ではDLQ件数に加え、受付から開始までの待ち時間、429率、UNKNOWN件数、完了時間、実行失敗、通知失敗を追います。jobId・executionArn・runtimeSessionIdを関連付けると原因を追いやすくなります。大きな入力や結果はS3に置き、状態には参照を渡します。
Redriveでは対象状態のRetry回数がリセットされます。ワークフローのRetry回数だけで費用を制御せず、ジョブ単位の総試行数・期限・モデル利用予算も保存してください。AWS:Redrive時のRetry動作
まとめ
導入時に優先するのは、Retryの回数を増やすことより、再実行しても安全な契約を作ることです。
- SQSは受付・配送、Step Functionsは業務工程を担当する。
- 一時障害だけを指数バックオフとジッターで再試行する。
- タイムアウトは結果不明として状態照合する。
- jobIdと操作IDで二重実行・二重更新を抑える。
- AgentCoreの完了はCallbackか状態照会で確認する。
- 回数・総時間・同時実行・費用の上限をジョブ全体で決める。
この設計を先に固めると、障害時にも安全に待ち、再試行し、必要に応じて運用へ引き継げる非同期バックエンドを作れます。