私のAgentシステムがしばらく稼働している場合、問題が起きてから気づくのではなく、既存のリトライロジックがひそかにお金を燃やしていないか遡って確認するにはどうすればいいですか?
最も直接的な方法は「リトライコスト監査」を行うことです。具体的な手順は次の通りです。まずログや課金記録から過去30日間にリトライをトリガーしたすべてのタスクを抽出し、各タスクの「リトライ回数」と「そのタスクの総コスト」を計算し、両者の散布図を描きます。正常であれば、大半のタスクは「リトライ0〜1回、コストは典型的なタスクコストに近い」領域に収まるはずです。「リトライ回数が高め(5回以上)で毎回のリトライコストも高め」の領域に固まっているタスク群が見えたら、そのタスク群は循環的失敗または構造的失敗が一時的失敗と誤判定され、不適切なリトライ戦略が使われている可能性が高いです。
次に、この「高リトライ+高コスト」タスク群について、実際の実行ログをサンプル調査し、リトライ間のリクエスト内容とエラーメッセージが高度に類似しているか(循環のシグナル)、または各リトライのエラー原因が同じか(構造的失敗のシグナル)を確認します。これらのいずれかが確認されれば、既存システムがリトライすべきでない失敗をリトライすべき失敗として扱っていることを意味し、これが最も直接的に修正すべきギャップです。
最後にこのタスク群の総コスト比率を計算します。全タスクコストの1〜2%にすぎなければ、問題は存在しますが影響は限定的で優先順位に応じて開発スケジュールに組み込めます。10%を超えれば、すでに実質的に利益を侵食している問題であり、スケジュールが空くのを待たずに即座に修正すべきです。多くのチームが初めてこの監査を行う際、「循環的失敗が占めるコスト比率」に驚きます。この種の失敗は単一タスクレベルではしばしば目立たない(数セント余分にかかるだけかもしれない)ものの、累積規模はよく過小評価されるためです。
指数バックオフ(exponential backoff)リトライ戦略は標準的に聞こえますが、Agentシナリオで特別に調整が必要な点はありますか?
従来システムでの指数バックオフの設計目標は「一時的な混雑が解消する時間を与える」ことで、このロジック自体に問題はありませんが、Agentシナリオに直接適用する際は2点調整が必要です。
第一に、バックオフ時間はユーザーの体感遅延を考慮すべきで、システム負荷だけを考慮すべきではありません。従来システムのリトライはバックエンドで発生し、ユーザーは通常気づきません。しかし多くのAgentシナリオは結果を同期的に待つもので(ユーザーが指示を送信してAgentの返答を待つ)、バックオフ時間を標準的な1秒・2秒・4秒・8秒(4回のリトライで合計15秒待つ)に設計すると、ユーザーはシステムの反応が鈍いと感じます。同期待機シナリオでは、リトライ上限を下げること(例:2回のみ、総待機時間を5秒以内に)を推奨し、それを超えたら「処理中、後で結果を確認してください」と直接報告し、長時間の待機を非同期通知に転換し、ユーザーにずっとくるくる回るアイコンを見つめさせないようにします。
第二に、バックオフロジックは構造的失敗と循環的失敗を除外しなければならず、真に一時的な失敗にのみ適用されるべきです。これは最も見落とされやすい点です——多くのフレームワークのデフォルト実装は「どんなエラーにも同じ指数バックオフリトライを適用する」もので、そのエラーが一時的かどうかを先に判断していません。正しい方法は、リトライロジックの前にエラー分類層を追加することです。まずこの失敗が一時的・構造的・循環的のいずれかを判断し、一時的失敗と判定された場合のみ指数バックオフリトライフローに入り、構造的失敗は直接「リクエスト戦略を変更する」経路へ、循環的失敗は直接タスクを中断します。
この分類層がなければ、指数バックオフリトライはすべての失敗タイプを同じように扱い、構造的失敗と循環的失敗にバックオフ待機時間とリトライ回数を無駄に費やしながら、実際の問題の根本原因を解決できません。
タスクがユーザーにとって重要な場合(例:金額の大きい取引決定に関わる)、必ず成功させるためにリトライ上限とコスト上限を緩めるべきですか?
直感的には「重要なタスクは何としても成功させるべき」というのは合理的に聞こえますが、この考えは「タスクの重要性」と「リトライ上限をどれだけ広く設定すべきか」という2つのことを混同しており、実務上むしろ逆効果になりがちです。
重要な洞察は、タスクが重要であるほど、早期の失敗時に人間の介入に切り替えるべきであり、Agentにより多くの回数、より高いコストで「無理に解こう」とさせるべきではないということです。理由は、高重要度のタスクが合理的なリトライ回数(例:3回)以内で成功完了できない場合、これは通常そのタスク自体がAgentの現在の能力範囲を超えているか、人間の判断を要する境界事例に遭遇していることを意味します。この時点でさらにリトライを重ねても成功率は上がらず、より高いコストで同じ「失敗」または「品質に疑問符がつく」結果を得るだけです。
より良い設計は、重要なタスクのリトライ上限を一般タスクより広く設定すべきではなく、むしろより保守的に設定すべきです(例:5回ではなく2回)。ただし縮退経路のトリガー閾値は「縮退して簡略版で自動完了」ではなく「直接人間に切り替える」よう設計すべきです。つまり重要なタスクのリソース配分ロジックは「早めに自動化の試みを諦め、リソース(とユーザーの忍耐)を人間の処理のために残す」べきであり、「予算を使い切るまでAgentに試行を続けさせる」べきではありません。
この設計はむしろ直感が達成したい目標に合致しています。重要なタスクが適切に処理されることを保証することです。ただ「適切に処理される」とは多くの場合、機械にコストを度外視して繰り返し試させることではなく、早めに人間の判断に委ねることを意味します。後者はお金を燃やすだけでなく、本当に人間の介入が必要なタイミングを遅らせ、本当に重要なタスクにとってはむしろ害になります。
マルチAgentシステムで、Orchestratorが呼び出すあるSub-agentが失敗し続ける場合、リトライロジックはOrchestrator層で処理すべきか、Sub-agent層で処理すべきか?
階層的に処理すべきであり、どの層が何を担当するかを明確に区別することが「リトライ回数が意図せず増幅される」という一般的な落とし穴を避ける鍵です。
Sub-agent内部のリトライ:そのSub-agent自身のタスク範囲内の一時的失敗のみを処理します(呼び出した外部ツールのタイムアウトなど)。この層のリトライロジックは「局所的」であり、Sub-agentは自身の内部リトライ回数とコストに責任を持ち、リトライが上限に達したら「いつか成功する」と無限にリトライするのではなく、明確な失敗ステータス(前述のstatus: failed)をOrchestratorに返すべきです。
Orchestrator層のリトライ:単純に「同じSub-agentをもう一度呼び出す」のではなく、「このSub-agentが失敗ステータスを返してきた、どうすべきか」というより高次の決定に直面します。OrchestratorがSub-agentの失敗ステータスに対してもSub-agent内部と同じ「リトライ」ロジックを適用すると、見落とされやすい落とし穴が生じます。Sub-agent内部ではすでに3回リトライしてから失敗を報告し、Orchestratorは失敗を受け取ってから再度Sub-agentを呼び出して3回リトライし、Orchestrator自体がさらに上位のループでリトライされている場合、実際に発生する総リトライ回数は3層の回数の合計ではなく積になります——これはマルチAgentシステムで「リトライ回数が意図せず増幅される」最も一般的な原因であり、コスト監査で最も深刻に過小評価されやすいリスクです。
正しい方法は、各層が「これはすでにリトライ済みの失敗か」という情報を把握できるようにすることで、下層の失敗を全く新しい、まだ試みられていないリクエストとして扱わないことです。具体的な実装としては、trace contextに「消費済みリトライ回数」フィールドを持たせ層を通じて伝達し、Orchestratorがsub-agentの失敗ステータスを受け取った際、まずこのフィールドを確認し、すでにグローバル上限(単一層の上限だけでなく)に達していれば、リトライ回数をゼロから数え直すのではなく、タスクチェーン全体を即座に中止します。
ほぼすべてのAgentフレームワークのデフォルト動作は「ツール呼び出しが失敗したらリトライする」です。このロジックは従来のプログラミングでは合理的です——ネットワークリクエストが時々タイムアウトしても、1回リトライすれば通常解決します。しかしこのロジックをそのままAgentシステムに持ち込むと、開発者が最も見落としやすく、結果が最も深刻になりうるリスクを生み出します。暴走したリトライループです。
違いは失敗の性質にあります。従来システムの失敗は通常一時的(ネットワークの揺らぎ、瞬間的なサーバー過負荷)で、リトライにはほとんど意味あるコストがかかりません。Agentシステムの「失敗」は多くの場合一時的ではなく構造的です——ツールが返すデータ形式をモデルが正しくパースできない、タスク自体がモデルの能力を超えている、あるいは論理ループに陥る(モデルが毎回同じ誤った判断をする)。この種の失敗を10回リトライしても、1回リトライしたのとほぼ同じ結果しか得られませんが、コストは10倍です。
リトライロジックを設計する前に、まず「失敗」を異なるタイプに分解する必要があります。それぞれ必要な戦略が全く異なるためです。
一時的な失敗:ネットワークタイムアウト、APIレート制限、サードパーティサービスの瞬間的な利用不可。この失敗タイプの特徴は「同じリクエストを少し後で再試行すれば通常成功する」ことで、指数バックオフ(exponential backoff)でのリトライに適しています。これは「単純なリトライ」戦略が本当に適用できる唯一の失敗タイプです。
構造的な失敗:ツールが返すデータ形式が期待と一致しない、モデルのタスクの理解自体に偏りがある。この失敗タイプの特徴は「同じリクエストをリトライしても、大部分は再び失敗する」ことです。問題が「今回運が悪かった」のではなく「このリクエスト自体に問題がある」ためです。構造的な失敗に単純なリトライを使うと、成功率を上げることなく繰り返しお金を燃やすだけです。正しい対処法は、この種の失敗を検知したらリクエストの内容や戦略を変更することです(タスク説明を簡略化する、プロンプトの枠組みを変える、または直接人間の介入が必要とマークする)。
循環的な失敗:モデルが「Aを試して失敗→Bを試して失敗→またAに戻る」という推論ループに陥り、各ラウンドがトークンとツール呼び出しを消費しながら実質的な進展がない状態。これは最も危険な失敗タイプです。監視ダッシュボード上では「システムが忙しく懸命に動いている」ように見えますが、実際にはその場で回転してお金を燃やしているだけだからです。循環的な失敗を検知するにはAgentの状態履歴を追跡する必要があり、連続する数ラウンドの状態が高度に類似している場合(同じツール呼び出し、同じパラメータ、同じエラーメッセージ)、これは明確な循環のシグナルであり、「もう一度試してみる」のではなく即座に中断しなければなりません。
多くの開発者はリトライ上限を設定すべきと知っています(例:最大3回まで)が、回数上限だけを設定してもコストが制御不能になる可能性があります。理由は、回数上限が「各リトライのコストが異なりうる」ことを考慮していないためです。最初の試行が500トークンを消費したとしても、モデルがリトライ時により多くのコンテキストを追加すれば(前回の失敗エラーメッセージをプロンプトに含めるなど)、3回目のリトライは3000トークンを消費するかもしれません。単に「3回リトライ」を制限するだけで「このタスクに合計いくら使ったか」を制限しなければ、タスクの総コストが予想をはるかに超える可能性は依然として残ります。
より完全な設計は2つの上限を同時に設定すべきです。リトライ回数の上限(無限ループを防ぐ)と、単一タスクの総コスト上限(回数は少なくても毎回高額で合計が爆発するのを防ぐ)です。どちらかの上限に達したら、タスクは即座に終了し失敗としてマークされるべきで、「まだ回数の余裕があるからもう一度試そう」であってはなりません。
多くのAgentシステムは「失敗したら成功するまで何とかすべき」とデフォルトで想定していますが、この仮定自体に問題があります。すべてのタスクが「成功」のために無制限のリトライコストを払う価値があるわけではなく、特にタスク自体の価値が限定的な場合はそうです。
より実務的な設計は段階的な縮退です。第一段階の失敗では元の戦略でリトライを試みる(一時的な失敗に適用)。第二段階の失敗では簡略化されたバージョンのタスクに切り替える(例:本来複雑な多段階分析を要求していたものを、最も基本的な結論だけを返すよう縮退させ、深さを犠牲にして成功率を得る)。第三段階の失敗では自動化を直接放棄し、コストを燃やして無理に押し通すのではなく「自動完了不可、人間の処理が必要」という明確な状態を返します。この縮退チェーンの設計により、システムは困難なタスクに遭遇した際「優雅に失敗する」ことができ、「高額に失敗する」ことを避けられます。
Agentプロダクトを開発・運営しているなら、リトライロジックのコスト制御を適切に設計していないことは、最も見落とされやすく、最も大きな財務的な穴になりうる技術的負債の一つです——循環的な失敗に陥ったAgentは、あなたが気づかないうちに1日で通常タスク千件分に相当するコストを燃やす可能性があります。Agent機能を公開する前に3つのことを確認してください。リトライロジックが一時的・構造的・循環的の3種類の失敗を区別し、それぞれ異なる戦略で処理しているか。リトライ回数の上限と単一タスクの総コスト上限を同時に設定しているか。システムが問題を解決できない場合に無限に頑張り続けるのではなく「優雅に失敗する」明確な縮退チェーンがあるか。この3つができていなければ、あなたのAgentのコスト構造には常に見えないが、いつでも爆発しうるリスクが潜んでいます。