DEV Community

Cover image for AIモデルが突然ダウンした時に備える:APIフェイルオーバー設計
Akira
Akira

Posted on • Originally published at apidog.com

AIモデルが突然ダウンした時に備える:APIフェイルオーバー設計

2026年6月12日、米国の輸出規制により、AnthropicはClaude Fable 5をほとんど警告なしにオフラインにせざるを得ず、モデルは7月1日に復帰するまで停止していました。モデルIDをコードにハードコーディングしていたチームは19日間混乱しましたが、フェイルオーバーチェーンを用意していたチームは設定を切り替えるだけで運用を継続できました。

今すぐApidogを試す

この教訓は、1つのモデル障害に限りません。LLMを使うアプリケーションでは、モデルの可用性をDNSやストレージのような前提として扱いがちです。しかし、モデルは法規制、容量制限、非推奨化、安全性レビューによって停止・変更される製品です。この記事では、AI APIのフェイルオーバーを実装するための設計、設定例、テスト方法、運用手順を整理します。

モデルが消える理由

LLMモデルは、単純な技術障害以外でも利用不能になります。

  • 規制。 Fable 5の停止は、技術的な障害ではなく輸出規制によるものでした。法規制やコンプライアンス関連のイベントは、非推奨スケジュールや90日前通知に従うとは限りません。障害発生時、外部からはこのように見えていました
  • プロバイダのインシデント。 主要なAIプロバイダでも、数時間規模の障害は発生します。プロバイダ側が復旧中でも、自社アプリのSLAは止まりません。
  • 非推奨化。 プロバイダは公開スケジュールに従ってモデルを廃止します。OpenAIは非推奨化ページを更新しており、Anthropicも古いClaudeバージョンを廃止しています。非推奨化は、期限が分かっている「ゆっくりした障害」です。
  • 容量制限。 リリース週やトラフィックスパイク時には、モデルは存在していても429や529を返すことがあります。
  • 安全性によるロールバック。 プロバイダはリリース後に問題を見つけた場合、モデルアクセスを制限または撤回することがあります。これは静かに、場合によってはアカウント単位で行われます。

原因は違っても、アプリ側の症状は同じです。コードが依存しているモデルIDが応答しなくなります。したがって、フェイルオーバーはインシデント対応ではなく、通常のアーキテクチャ設計として組み込むべきです。

フェイルオーバーの3階層

フェイルオーバーは1つの切り替えではありません。実装レベルを分けると設計しやすくなります。

レベル1:同一プロバイダ内のフォールバック

同じベンダーの別モデルへルーティングします。

例:

Fable 5 -> Opus 4.8 -> Sonnet 4.6
Enter fullscreen mode Exit fullscreen mode

同じSDK、同じ認証、同じ応答形式を使えるため、導入コストが低く、切り替えも速いのが利点です。Anthropicはフォールバックパラメータを通じて、同じAPI呼び出し内で拒否されたリクエストを代替モデルで再試行する仕組みもサポートしています。

障害が起きる前に、Fable 5 vs Opus 4.8の比較のように、実際のプロンプトでフォールバック候補を検証しておきます。

レベル2:プロバイダをまたぐフォールバック

別ベンダーのモデルに切り替えます。これは、プロバイダ全体の障害、アカウント停止、地域制限に備えるための手段です。

ただし、次のコストが発生します。

  • 2つ目のSDKまたはHTTPクライアント実装
  • 別の請求関係
  • 別の認証パス
  • 異なるレスポンス形式
  • モデルごとに調整したプロンプト
  • 異なる拒否挙動やトークナイザーへの対応

まずは同一プロバイダ内フォールバックから始め、ビジネスリスクが大きい場合にプロバイダ横断を追加するのが現実的です。

レベル3:機能低下モード

最先端モデルが使えない場合でも、最低限の機能を提供します。

例:

  • よくある質問にはキャッシュ済み回答を返す
  • 分類タスクは小さなローカルモデルやルールエンジンに切り替える
  • 生成機能を一時停止し、明確なメッセージを表示する
  • 管理画面の一部だけを読み取り専用にする

品質低下は許容できますが、アプリケーション全体の停止は避けるべきです。

レベル 切り替えまでの遅延時間 品質の低下 エンジニアリングコスト
同一プロバイダ内フォールバック 数秒から数分; 設定変更または自動再試行 小〜中程度; 同じモデルファミリー、慣れた動作 低; 同じSDK、認証、応答形式
プロバイダをまたぐフォールバック 数分から数時間; ルーティングロジックとテスト済みプロンプトが必要 中程度; 異なるトークナイザー、形式、拒否動作 中〜高; 2番目の統合、請求、モニタリング
機能低下モード 構築後は実質的に即時 大きいが予測可能で正直 中程度; キャッシュ層、ローカルモデル、または機能フラグ

実務上は、今すぐレベル1を導入し、レベル3を最低限の安全装置として用意します。レベル2は、収益リスクやSLA要件が2つ目の統合コストを正当化する場合に追加します。

フェイルオーバー条件を明文化する

フォールバック先だけでなく、切り替え条件も定義します。

例:

状態 推奨アクション
404 model_not_found 即時フェイルオーバー
拒否レスポンス 次のモデルで1回だけ再試行
429 rate_limit バックオフ後、継続失敗ならフェイルオーバー
連続タイムアウト3回 サーキットブレーカーを開く
5xx継続 一定回数後に次モデルへ切り替え
構造化出力の検証失敗 同じモデルで再試行後、次モデルへ

このルールをルーティング層に実装しておくと、障害時に人間がその場で判断する必要がありません。

フェイルオーバーを安価にする設計

1. モデルIDをコードではなく設定に置く

まず、リポジトリでモデルIDを検索してください。

grep -R "claude-" .
Enter fullscreen mode Exit fullscreen mode

モデル文字列がアプリケーションコードの複数箇所に散らばっている場合、デプロイなしの切り替えは困難です。モデルIDは設定ファイルに集約します。

# config/model-routes.yaml
routes:
  chat-assist:
    primary: claude-fable-5
    fallbacks:
      - claude-opus-4-8
      - claude-sonnet-4-6
    degraded_mode: cached_answers
    max_output_tokens: 8192
    timeout_seconds: 120

  ticket-classifier:
    primary: claude-sonnet-4-6
    fallbacks:
      - claude-haiku-4-5
    degraded_mode: rules_engine
    max_output_tokens: 1024
    timeout_seconds: 30
Enter fullscreen mode Exit fullscreen mode

この形式にしておけば、ルート単位でプライマリ、フォールバック、機能低下モードを管理できます。

2. ルーティングを1箇所に集約する

各機能が直接モデルを呼ぶのではなく、1つのルーティングモジュールを経由させます。呼び出し元は「どのモデルか」ではなく「このタスクを完了してほしい」と依頼します。

最小構成の例:

MODEL_CHAIN = ["claude-fable-5", "claude-opus-4-8", "claude-sonnet-4-6"]

def complete(prompt: str) -> str:
    last_error = None

    for model in MODEL_CHAIN:
        try:
            response = client.messages.create(
                model=model,
                max_tokens=8192,
                messages=[{"role": "user", "content": prompt}],
            )

            if response.stop_reason == "refusal":
                last_error = RefusalError(model)
                continue

            return response.content[0].text

        except (
            anthropic.NotFoundError,
            anthropic.RateLimitError,
            anthropic.APIStatusError,
        ) as err:
            last_error = err
            continue

    raise AllModelsUnavailable(MODEL_CHAIN) from last_error
Enter fullscreen mode Exit fullscreen mode

本番では、次も追加します。

  • モデルごとのタイムアウト
  • サーキットブレーカー
  • リトライ回数の上限
  • メトリクス記録
  • 構造化出力の検証
  • 機能低下モードへの切り替え

3. サーキットブレーカーを入れる

障害中のモデルに毎回リクエストを送ると、レイテンシとコストが増えます。一定回数失敗したモデルは短時間スキップします。

from time import time

class CircuitBreaker:
    def __init__(self, failure_threshold=3, cooldown_seconds=60):
        self.failure_threshold = failure_threshold
        self.cooldown_seconds = cooldown_seconds
        self.failures = {}
        self.opened_at = {}

    def allow(self, model: str) -> bool:
        opened = self.opened_at.get(model)
        if opened is None:
            return True

        if time() - opened > self.cooldown_seconds:
            self.failures[model] = 0
            self.opened_at.pop(model, None)
            return True

        return False

    def record_success(self, model: str):
        self.failures[model] = 0
        self.opened_at.pop(model, None)

    def record_failure(self, model: str):
        count = self.failures.get(model, 0) + 1
        self.failures[model] = count

        if count >= self.failure_threshold:
            self.opened_at[model] = time()
Enter fullscreen mode Exit fullscreen mode

これをルーティング層に組み込み、開いているモデルは一時的にチェーンから除外します。

4. 能力階層型プロンプトを作る

特定モデルの癖に依存したプロンプトは、フェイルオーバー時に壊れます。

推奨パターン:

base_prompt/
  summarize.md
overlays/
  claude-fable-5.md
  claude-opus-4-8.md
  claude-sonnet-4-6.md
Enter fullscreen mode Exit fullscreen mode
  • base_prompt:すべてのモデルで成立する最低限の指示
  • overlays:モデル固有の最適化、出力スタイル、パラメータ補足

ベースプロンプトは、最強のプライマリモデルではなく、最も弱いフォールバックモデルで検証します。小さいモデルは、より明示的な構造や出力例を必要とすることが多いためです。

5. リクエストパラメータもモデルごとに管理する

プロンプトだけでなく、パラメータもモデル依存です。プライマリモデルで有効な設定が、フォールバックモデルでは400エラーになることがあります。

例:

models:
  claude-fable-5:
    max_output_tokens: 8192
    timeout_seconds: 120
    temperature: 0.2

  claude-opus-4-8:
    max_output_tokens: 8192
    timeout_seconds: 120
    temperature: 0.2

  claude-sonnet-4-6:
    max_output_tokens: 4096
    timeout_seconds: 90
    temperature: 0.1
Enter fullscreen mode Exit fullscreen mode

ディスパッチ時に、ルーティング層がモデルIDに対応するパラメータを適用します。無効なパラメータで停止するフェイルオーバーは、実質的にはフェイルオーバーではありません。

6. 応答を内部形式に正規化する

アプリケーション内部でプロバイダ固有のレスポンスオブジェクトに依存すると、切り替え時に壊れます。ルーティング境界で内部形式に変換します。

例:

from dataclasses import dataclass
from enum import Enum

class CompletionStatus(Enum):
    OK = "ok"
    REFUSAL = "refusal"
    TRUNCATED = "truncated"
    ERROR = "error"

@dataclass
class CompletionResult:
    text: str
    model: str
    status: CompletionStatus
    latency_ms: int
    raw_stop_reason: str | None = None
Enter fullscreen mode Exit fullscreen mode

以降のアプリケーションコードは、Anthropicや他プロバイダの生レスポンスではなく、この内部型だけを扱います。

7. コストと制限を設定に含める

フォールバックモデルは、トークン単価、コンテキストウィンドウ、最大出力が異なります。Fable 5からOpus 4.8へのフォールバックではトークンあたりのコストが約半分になり、Sonnet 4.6はさらに安価ですが、出力上限が低くなります。

記憶に頼らず、現在のモデル概要を確認してください。

ルーティング層では、少なくとも次をモデルごとに持たせます。

  • max_output_tokens
  • context_window
  • タイムアウト
  • 切り捨て時の挙動
  • 想定コスト
  • 構造化出力対応の有無

フォールバックセット全体をコントラクトテストする

一度も実行されていないフェイルオーバーパスは、必要なときに高確率で失敗します。フォールバックチェーンをAPIコントラクトとして扱い、定期的にテストします。

実装パターンとして、Apidogで、フォールバックチェーン内のすべてのモデルに対して重要なプロンプトを実行するテストシナリオを作成します。

各モデルで確認する項目は3つです。

1. スキーマ

構造化出力が期待どおりに解析できるか確認します。

  • JSONとしてパースできる
  • 必須フィールドが存在する
  • JSON Schemaに合格する
  • enum値が想定範囲内
  • 空文字やnullが混入していない

フォールバックモデルがJSONを別の形でエスケープしたり、パーサーが前提にしているフィールドを省略したりする問題を検出できます。

2. レイテンシ

モデルごとにp95の予算を設定します。

例:

chat-assist:
  fable-5: p95 < 12s
  opus-4.8: p95 < 15s
  sonnet-4.6: p95 < 10s
Enter fullscreen mode Exit fullscreen mode

技術的に成功しても、40秒かかるフォールバックは別種の障害です。

3. 品質シグナル

CIでは表現力を厳密に評価する必要はありません。壊れたモデルを検出できれば十分です。

低コストなチェック例:

  • 出力が空ではない
  • 指定した言語で返っている
  • 必須セクションを含む
  • JSONフィールド数が想定どおり
  • 拒否率がベースラインから大きく外れていない
  • 明らかなエラーメッセージを含まない

Apidogでは、エンドポイントURL、APIキー、モデルIDを環境変数として管理します。モデルごと、またはプロバイダごとに環境を分けると、同じテストシナリオを環境切り替えだけで実行できます。

例:

Environment: claude-fable-5
  API_BASE_URL
  API_KEY
  MODEL_ID=claude-fable-5

Environment: claude-opus-4-8
  API_BASE_URL
  API_KEY
  MODEL_ID=claude-opus-4-8

Environment: claude-sonnet-4-6
  API_BASE_URL
  API_KEY
  MODEL_ID=claude-sonnet-4-6
Enter fullscreen mode Exit fullscreen mode

4つ目のモデルを追加する場合も、新しいテストを書くのではなく、新しい環境を追加するだけで済みます。

テストプロンプトの選び方

何百件も必要ありません。実運用を代表する10〜20件で十分です。

含めるべきケース:

  • 最も長いコンテキスト
  • 最も厳密な構造化出力
  • 過去にパーサーを壊した入力
  • ドメイン境界に近い入力
  • 拒否率の変化を検出しやすい入力
  • 多言語または日本語固有の入力
  • 低温度でも高温度でも崩れやすいタスク

プロンプトセットはコードと一緒にバージョン管理します。本番で新しいエッジケースが見つかったら、そのケースをテストスイートに追加します。

モックサーバーも用意する

障害発生時には、1つの環境を記録済みレスポンスを返すモックサーバーに向けます。プロバイダが停止していても、下流のパーサー、UI、保存処理、ワークフローはCIで検証できます。

Apidogは、テストで使っているAPI仕様からモックを生成できます。これにより、モデル障害がリリースパイプライン全体を止める事態を避けられます。

6月12日に冷静だったチームと混乱したチームの差はここにありました。前者は、Opus 4.8パスが主要プロンプトに対して有効な出力を返すことを夜間テストで確認していました。後者は、希望的観測しか持っていませんでした。

運用準備チェックリスト

アーキテクチャはフェイルオーバーを可能にします。運用準備は、素早く安全な切り替えを可能にします。

すべてのモデルをプローブする

プライマリだけでなく、チェーン内の各モデルに対して定期的に合成プロンプトを送ります。

status.anthropic.comのようなステータスページは有用ですが、次は分かりません。

  • 自社アカウント固有の制限
  • リージョン固有の問題
  • レート制限ティアの問題
  • 特定モデルIDだけの問題
  • 自社プロンプトでのみ発生する拒否率変化

最初に失敗を検出するのは、自社のプローブであるべきです。

HTTP 5xx以外にもアラートする

モデル障害は、HTTP 5xxだけでは見えません。

監視すべき指標:

  • model_not_foundの発生数
  • 429の増加
  • 拒否率の急上昇
  • タイムアウト率
  • 構造化出力の検証失敗率
  • 平均出力長の急変
  • モデル別レイテンシp95/p99
  • フォールバック発生率

ダッシュボードは「全体」だけでなく、モデル別・ルート別に分けます。

カットオーバー用ランブックを作る

障害時に決めるのではなく、事前に決めておきます。

ランブックに含める項目:

  • 誰がフェイルオーバーを判断するか
  • どの設定値を変更するか
  • 自動切り替えと手動切り替えの境界
  • 切り替え後に見るダッシュボード
  • サポートチームへの通知文
  • 顧客向けメッセージ
  • ロールバック条件
  • 復帰時のカナリア手順

Fable 5の障害時、ランブックがなかったチームは、実行よりも意思決定に時間を使いました。

復帰は段階的に行う

プライマリモデルが戻っても、即座に100%戻してはいけません。

推奨手順:

  1. コントラクトテストを全件実行
  2. 拒否率と品質指標を停止前ベースラインと比較
  3. 1〜5%のトラフィックでカナリア
  4. エラー率、拒否率、出力品質を監視
  5. 25%、50%、100%へ段階的に増やす
  6. フォールバックモデルへの即時ロールバック手段を残す

Fable 5 APIへの切り戻し方では、この復帰プロセスの考え方が説明されています。このパターンは、任意のプライマリモデル復帰に適用できます。

四半期ごとにリハーサルする

ステージング環境で、意図的にプライマリモデルを無効化します。リスク許容度がある場合は、本番の小さなトラフィックセグメントで実施します。

ドリルで見つかる典型的な問題:

  • フォールバック用APIキーの期限切れ
  • 請求設定の未完了
  • 誰も見ていないダッシュボード
  • 名前変更された設定値
  • 使われていないランブック
  • 本番だけで失敗するプロンプト
  • フォールバックモデルの出力上限不足

これらは、障害中ではなく平常時に見つける方が安く済みます。

Fable 5の事例から学べること

7月1日の復帰には、重要な教訓がありました。AnthropicはFable 5を再デプロイしましたが、それは再訓練された安全分類器を備えたものでした。

つまり、同じモデルID、同じAPIサーフェスであっても、停止前と完全に同一のモデルではありませんでした。拒否の境界線が移動した可能性があります。6月上旬には問題なかったプロンプトが、7月には異なる結果を返す可能性があります。逆に、以前は拒否されていた入力が通ることもあります。

ここから導けるルールは明確です。

復帰時は、再有効化ではなく再テストを行う。

停止、ロールバック、長期非推奨から戻ったモデルは、新しいモデルバージョンとして扱います。

実施すべきこと:

  • 完全なコントラクトスイートを実行する
  • 拒否率を停止前ベースラインと比較する
  • 品質指標をフォールバック時ではなく停止前と比較する
  • 段階的にカナリア展開する
  • ユーザー影響をプロダクト変更として扱う

19日間は、フォールバックモデルが実質的なベースラインになるのに十分な期間です。ユーザーはOpus 4.8の挙動に慣れ、社内チームもそれに合わせてプロンプトを調整していた可能性があります。復帰は単なる技術イベントではなく、プロダクト変更でもあります。

実装時の最小チェックリスト

まずは次の順番で進めると、短期間でレベル1のフェイルオーバーを導入できます。

[ ] モデルIDをgrepし、設定ファイルへ移動する
[ ] ルートごとの primary / fallbacks / degraded_mode を定義する
[ ] すべてのLLM呼び出しを1つのルーティング層に集約する
[ ] 404、429、5xx、拒否、タイムアウトの扱いを決める
[ ] モデルごとの timeout / max_output_tokens を設定する
[ ] 応答を内部形式に正規化する
[ ] 主要プロンプト10〜20件を選ぶ
[ ] Apidogでモデル別環境を作る
[ ] CIまたは夜間ジョブでフォールバックチェーンをテストする
[ ] カットオーバーと復帰のランブックを作る
[ ] 四半期ごとにフェイルオーバードリルを実施する
Enter fullscreen mode Exit fullscreen mode

よくある質問

同一プロバイダのフォールバックチェーンで十分ですか?

まずは同一プロバイダから始めるのが現実的です。非推奨化、容量制限、安全性ロールバック、モデル固有の停止に、低い実装コストで対応できます。Anthropicのサーバーサイドフォールバックのような機能を使えば、導入コストをさらに下げられます。

プロバイダ全体の障害やアカウントレベルのリスクが大きい場合は、プロバイダ横断のフォールバックを追加します。機能低下モードはいずれの場合でも用意すべきです。

小さなモデルへフェイルオーバーしたらユーザーは気づきますか?

タスクによります。推測せず、測定してください。

情報抽出や分類では、適切にプロンプトされた小さなモデルでも差が出にくいことがあります。一方、長文推論や複雑な計画タスクでは差が出やすくなります。

Fable 5 vs Opus 4.8の比較のようなベンチマークは初期判断に役立ちます。能力階層型プロンプトと、正直なUI文言(例:「現在、応答が通常より簡潔になる場合があります」)も有効です。

フォールバックパスはどのくらいの頻度でテストすべきですか?

少なくとも次のタイミングで実行します。

  • 毎日スケジュール実行
  • プロンプト変更時のCI
  • ルーティング設定変更時のCI
  • モデル追加・削除時
  • プロバイダから非推奨化や障害の告知があった直後

主要な20プロンプトを3モデルに対して実行するコストは、障害時に壊れたフォールバックを発見するコストに比べれば小さいです。


モデルの可用性は、今後さらに予測しづらくなります。規制は厳格になり、リリースと非推奨化のサイクルは速くなり、容量はローンチごとに変動します。

次のFable 5のような事態を乗り切るチームは、モデルを恒久的な設備ではなく、テスト済みの代替先を持つ交換可能なコンポーネントとして扱っているチームです。

必要な作業は大きくありません。設定ファイル、ルーティングラッパー、毎晩実行されるコントラクトスイートから始められます。Apidogをダウンロードし、今日からフォールバックチェーンをスケジュールテストに組み込みましょう。次の障害は、いつ起きてもおかしくありません。

Top comments (0)