LLMアプリの縮退運転とは?障害時に機能を減らして中核を守る
LLMアプリでは、モデルAPI、検索基盤、外部の道具、認証サービスのどれか一つが不調になるだけで、全体を停止させてしまいがちである。しかし、すべての機能が同じ重要度とは限らない。検索が使えなくても保存済み資料は表示できるかもしれず、高性能モデルが混雑していても定型処理は小さいモデルで続けられるかもしれない。
縮退運転とは、障害や過負荷のときに提供する機能や品質を意図的に減らし、中核となる仕事を守る設計である。単に別のモデルへ切り替えることではない。何を残し、何を止め、利用者へ何を伝えるかを、平常時に決めておく必要がある。
完全停止と通常運転の間を設計する
通常運転か全面停止かの二択にすると、補助機能の障害がサービス全体へ波及する。例えば、回答への関連記事表示が失敗しただけで、本文の生成まで失敗させる必要はない。
縮退運転では、機能を重要度で分ける。
- 必ず守る中核機能
- 品質を落としても継続できる機能
- 後回しにできる機能
- 障害時は止める機能
顧客への確定通知は中核だが、その通知文の言い換え候補は止めてもよい。対話画面への回答は続ける一方、会話の自動要約はキューへ戻せる。サービスごとに、この境界を利用者の仕事から決める。
依存関係を一覧にする
縮退経路を作る前に、一つの利用者操作が何へ依存しているかを可視化する。LLM呼び出しだけでなく、次の要素を含める。
- 認証と権限確認
- 入力検査
- モデルAPI
- RAGの検索と文書取得
- 外部の道具やMCPサーバー
- 会話状態とチェックポイント
- モデレーションや安全検査
- ログ、トレース、評価用記録
- 通知や成果物の保存
各依存関係について、失敗したら処理全体を止めるハード依存か、機能を減らして続けられるソフト依存かを決める。安全検査や権限確認の失敗を、可用性を優先して省略してはいけない。縮退時にも守る制約は、品質上の付加機能と分けておく。
先に削る仕事を決める
過負荷時にすべての処理を少しずつ遅くすると、タイムアウトと再試行が増え、さらに負荷が高くなる。重要度の低い仕事を早めに止め、中核処理へ容量を残す方がよい。
LLMアプリで先に削りやすいのは、夜間集計、埋め込みの再生成、評価用の影実行、複数候補の生成、長い要約、装飾的な画像生成などである。利用者が待っている対話と、待てるバッチを同じキューに入れない。
Google SREは、負荷遮断を過負荷へ近づいたときに一部の処理を落とす方法、graceful degradationを必要な計算量や品質を下げる方法として説明している。LLMアプリでも、受付件数を減らす負荷遮断と、一件当たりの仕事を軽くする縮退を区別する。
モデルの切り替えだけでは不十分である
高性能モデルから小さいモデルへ切り替えると、遅延や費用を抑えられる場合がある。しかし、モデルが変わると、回答品質、対応する入力長、道具の選択、構造化出力の安定性、安全性が変わる。
代替モデルへ切り替えるなら、平常時から同じ評価セットで測り、任せてよい仕事を限定する。自由形式の説明は代替できても、契約判断や外部操作は停止するといった分け方が必要である。
さらに、障害がモデル提供者全体やネットワークにある場合、同じ経路の別モデルへ変えても復旧しない。切り替え先が同じ失敗領域を共有していないかを確認する。
RAGが壊れたときに知識を捏造させない
検索が失敗した状態で、モデルだけに回答を続けさせると、根拠が必要な質問へ推測で答える危険がある。RAGの縮退は、検索なし回答へ無条件に切り替えることではない。
質問を分類し、一般知識で答えてよい範囲だけモデル単独の回答を許可する。社内規程、価格、在庫、最新情報など、取得した根拠が必要な質問は「現在資料を確認できない」と明示して停止する。直近に検証済みのキャッシュを使う場合は、取得日時と有効期限を表示する。
検索件数を減らす、再ランキングを省略する、保存済みの要約を返すといった縮退も考えられる。ただし、閲覧権限の確認や削除済み文書の除外は省略しない。
Tool Callingは読み取りと書き込みを分ける
外部の道具が不調なとき、読み取りはキャッシュで代替できても、書き込みは同じ方法で扱えない。予定作成や送信の結果が不明なまま再試行すると、重複実行が起きる。
結果が不明な書き込みは、成功とも失敗とも決めつけない。冪等性キーや操作IDで状態を照会し、確認できるまで保留にする。代替経路へ切り替える場合も、同じ操作が元の経路で遅れて完了しないことを確かめる。
縮退中は、参照系の道具だけを残し、変更系の道具を停止する方法もある。道具の一覧をモデルへ見せなくするだけでなく、サーバー側の認可でも拒否する。
再試行で障害を増幅させない
一時的なレート制限やサーバーエラーは再試行で成功する場合がある。しかし、画面、アプリサーバー、ワーカー、SDKがそれぞれ再試行すると、一つの利用者操作が大量の下流呼び出しへ膨らむ。
再試行する層を一つに決め、回数と全体の期限を制限する。待ち時間には指数バックオフとランダムな揺らぎを入れ、同時に失敗した処理が同じ瞬間に戻らないようにする。入力不備、権限不足、容量超過など、待っても直らない失敗は再試行しない。
再試行予算を設け、サービス全体で追加呼び出しが一定量を超えたら止める。障害中に成功率を上げようとして繰り返すほど、提供者と自分のキューの両方を圧迫する可能性がある。
サーキットブレーカーで失敗中の依存先を休ませる
同じ依存先への失敗が続いたら、一定時間は呼び出しを止め、すぐに代替処理やエラーを返す。これはサーキットブレーカーと呼ばれる。
呼び出しを止めることで、毎回タイムアウトまで待つ処理と再試行を減らし、依存先が回復する余地を作る。停止後は少量の試行で回復を確認し、いきなり全件を戻さない。
モデルAPI全体を一つの回路として扱うと、一部のモデルや地域だけの障害でも全停止する。一方で細かく分けすぎると状態管理が複雑になる。利用するモデル、接続先、機能、テナントなど、実際の失敗領域に合わせて単位を決める。
縮退開始と解除の条件を分ける
遅延が一度しきい値を超えただけで縮退し、すぐ戻ると、通常運転と縮退運転を行き来する。これを避けるため、開始条件と解除条件に幅を持たせる。
開始判断には、エラー率だけでなく、待ち行列、処理中件数、最初の応答までの時間、下流のレート制限、残りの容量を使う。解除は、一定期間安定したことを確認してから段階的に行う。
自動切り替えだけに頼らず、運用者が強制的に機能を止めたり、縮退を解除したりできる手段も用意する。設定変更自体が全台同時の障害を起こさないよう、段階反映と監査記録が必要である。
利用者へ品質低下を隠さない
縮退中に通常と同じ表示をすると、利用者は古い情報や低い品質を通常の結果だと受け取る。何が利用できず、何を省略し、いつのデータを使ったかを短く示す。
例えば「検索基盤の混雑により、保存済みの2026年9月28日時点の資料から回答」と表示すれば、利用者は最新性を判断できる。「一部機能で問題が発生」だけでは、回答を信頼してよい範囲が分からない。
内部の提供者名や詳細な障害情報をすべて公開する必要はないが、利用者の判断に必要な制約は伝える。状態表示と実際の処理経路が一致していることもテストする。
縮退経路を普段から動かす
障害時だけ使うコードは、依存関係の更新や認証切れによって動かなくなりやすい。小さな割合の処理を定期的に縮退経路へ流し、結果と監視を確認する。
試験では、モデルAPIのタイムアウト、429、5xx、RAGの空結果、検索遅延、道具の結果不明、認証停止、キュー飽和を個別に注入する。確認するのは、代替結果が返ることだけではない。
- 安全検査や権限確認が省略されないか
- 再試行回数が上限内か
- 重複した副作用が起きないか
- 古いデータの日時が表示されるか
- 縮退開始と解除が記録されるか
- 回復時に負荷が一斉に戻らないか
縮退中の成功率だけでなく、通常時との差も測る。遅延、品質、費用、拒否率、利用者の再実行率を別々に記録し、縮退が障害を隠して長期化していないかを見る。
まとめ
LLMアプリの縮退運転は、障害時に別モデルへ切り替えるだけの仕組みではない。依存関係を整理し、中核機能、品質を落として続ける機能、後回しにする仕事、停止する機能を先に決める設計である。
検索なしで答えてよい範囲、書き込みを止める条件、再試行予算、サーキットブレーカー、開始と解除の基準を明確にする。安全検査と認可は縮退させず、品質や最新性の低下を利用者へ示す。平常時から縮退経路を動かしてこそ、障害時に中核を守れる。