LLMフェイルオーバー実装法|GitHub Actionsで5分自動切替

「Claudeが急に重くなって、業務が止まったらどうしよう」と不安な人に向けて書きます。この記事では、GitHub Actionsを制御面にして5分周期でLLMの生存確認を回し、Claude/OpenAI/Geminiを自動で切り替えるRunbookを、今日から動かせる粒度でまとめます。読了後は、障害時の初動を人力判断に頼らず、復旧時間と機会損失を最小化できる運用フローまで作れます。僕は福祉事業のIT全般を担当しつつ、AI×SaaS開発を複業で回しているので、止めない運用の現場目線で話します。

公開前確認(2026年5月時点):Vercel AI Gatewayは単一エンドポイントで複数モデル、予算、利用量監視、ロードバランス、フォールバックを扱えます。GitHub Actionsで切替を自動化する場合も、429・5xx・タイムアウトを分けて記録し、手動復旧できる環境変数を必ず残してください。

こんな方におすすめ

  • Claude中心で運用していて、障害時の代替手順が曖昧な方
  • GitHub Actions ヘルスチェックを最短5分で回したい方
  • Claude OpenAI Gemini 切り替えを自動化したい方
  • 夜間対応を減らしながら品質を落としたくない方

この記事でわかること

  • Anthropic停止リスクを前提にしたRunbook設計の考え方
  • GitHub Actionsのtimezonedeployment: falseを使う実装方法
  • 429/503/529を跨いだリトライとフェイルオーバー基準
  • 通知・監査・GameDayまで含む運用の固定化手順
僕は福祉事業の利用者管理システムや業務改善を担当しながら、AI×SaaS開発を継続しています。24時間運用の現場で「止まった瞬間に詰む」場面を見てきたので、個人開発でもチーム開発でも再利用できるRunbookに落として共有しています。

なぜ今Anthropic停止リスクをRunbook化するのか

まず前提として、Anthropicだけを責めたい話ではないです。どのLLMも障害や制限はあります。ただ、依存先が1つだと、停止時の選択肢がゼロになります。イメージとしては、通勤ルートが1本しかない状態です。事故が起きた瞬間、その日全部が崩れますよね。LLM運用も同じです。

公開ステータスを見ると、OpenAIのAPI稼働率は2026年1月〜4月で99.98%、一方でClaude APIは過去90日で98.98%でした。どちらも高水準ですが、「1%未満の揺れでも業務クリティカルな時間帯に当たると被害が大きい」のが実務です。さらにClaudeは2026年4月6日〜7日にエラー増加インシデントも出ています。数字だけでなく、直近の揺れをRunbookに反映するのがポイントです。

観点 Claude OpenAI Gemini
直近の運用論点 529/429の吸収 5軸レート管理 429/500/503/504対処
公開稼働率の参考 90日で98.98% 2026/1-4で99.98% 障害時の別モデル切替を公式推奨
Runbookへの反映 retry-after優先 x-ratelimit系ヘッダ監視 一時切替を標準フロー化

参考: Claude Status / OpenAI Status / Gemini Troubleshooting

政策面でも外部要因が増えています。連邦機関向けのフェーズアウト報道と、同時にClaude利用急増の報道が並行していました。つまり、需要増による混雑リスクと外部要因リスクが同時に来る可能性があるということです。だからこそ、障害発生後に考えるのではなく、平時にRunbook化しておく価値があります。

5分で作る全体像|GitHub Actions ヘルスチェックを制御面にする

ここは設計の芯です。結論から言うと、GitHub Actionsは「判定専用の司令塔」にすると扱いやすいです。最短5分間隔のon.scheduleで監視し、判定結果だけを各アプリへ配る構成です。アプリ側は「今どのプロバイダを使うか」だけ読むようにして、複雑な判定ロジックを持たせません。

制御面と実行面を分離すると、障害時の変更範囲が小さくなります。イメージとしては、信号機(制御)と車(実行)を分ける感じです。車が増えても信号機のルールだけ直せば回せます。これをLLM運用に当てはめると、複数サービスを同時に守りやすいです。

name: llm-healthcheck
on:
  schedule:
    - cron: '*/5 * * * *'
      timezone: 'Asia/Tokyo'
  workflow_dispatch:

jobs:
  check-and-switch:
    runs-on: ubuntu-latest
    environment:
      name: llm-prod
      deployment: false
    steps:
      - uses: actions/checkout@v4
      - run: pnpm tsx scripts/healthcheck.ts
      - run: pnpm tsx scripts/decide-provider.ts
      - run: pnpm tsx scripts/notify-slack.ts

このdeployment: falseは地味に重要です。Environment secrets/variablesは使いたいけど、毎回Deploymentオブジェクトは要らない運用で効率化できます。参考: GitHub Actions workflow syntax

設計時の注意

  • 5分監視は検知用:即時復旧が必要な系はWebhookや手動起動(workflow_dispatch)も併用します
  • 判定ログを残す:provider名、HTTP、遅延、request-idを必ず保存します
  • 秘密情報の分離:providerごとに環境変数を分けて誤配線を防ぎます

運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。

実装ステップ1|Claude/OpenAI/Geminiの共通判定を作る

ここで大事なのは、APIごとの差を「吸収レイヤー」で統一することです。各APIを直接比較すると条件がバラバラで混乱します。なのでstatuslatencyMsretryablerequestIdの4項目にそろえます。つまり、翻訳してから評価するということです。

判定ルールの最初の型は次で十分です。

  • Healthy:HTTP 2xx、遅延しきい値未満
  • Degraded:2xxだが遅延超過、または429/503/529が連続1回
  • Unhealthy:連続失敗、retry-after超過後も復帰しない

Anthropicは529 overloaded_errorが明示され、429はレート・加速制限を含みます。OpenAIはRPM/RPD/TPM/TPD/IPMの5軸で、失敗リクエストも制限を消費します。Geminiは429/500/503/504時の対処を公開し、500/503で別モデル切替を推奨しています。この違いを知らずに同じリトライを書くと、復旧が遅くなります

type Health = {
  provider: 'claude' | 'openai' | 'gemini'
  status: 'healthy' | 'degraded' | 'unhealthy'
  latencyMs: number
  retryable: boolean
  requestId?: string
}

// providerごとの差をここで吸収
function normalize(provider: string, http: number, latencyMs: number, headers: Record<string,string>): Health {
  const retryable = [429, 500, 503, 504, 529].includes(http)
  const degraded = http  200 && latencyMs > 2500
  return {
    provider: provider as Health['provider'],
    status: degraded ? 'degraded' : http >= 200 && http < 300 ? 'healthy' : 'unhealthy',
    latencyMs,
    retryable,
    requestId: headers['x-request-id'] || headers['request-id']
  }
}
実運用では「1回失敗したら即切替」より、「短い再試行→失敗継続で切替」のほうが安定します。瞬間的なネットワーク揺れを拾いすぎないからです。

参考: Anthropic Errors / OpenAI Rate Limits / Gemini Troubleshooting

実装ステップ2|自動切替ロジックを固定化する

次は「どう切り替えるか」です。僕が使っている基本ルールは、優先順位 + 指数バックオフ + クールダウン + 復帰判定の4点です。これを明文化しておくと、夜中の障害でも判断がブレません。運用担当が誰でも同じ動きを取れます。

  1. 優先順位:例: Claude → OpenAI → Gemini。通常時は品質とコストで決めます
  2. 指数バックオフ:1秒、2秒、4秒で再試行。retry-afterがあればそれを優先します
  3. クールダウン:失敗プロバイダは15分再選択しません
  4. 復帰条件:連続3回Healthyを確認したら優先候補へ戻します

「戻し条件」を決めないと、永遠に代替先へ張り付く事故が起きます。これが地味に多いです。なのでフェイルオーバーだけでなく、フェイルバックまでRunbookに書いておくのが大事です。

ゲートウェイを使う場合は、Vercel AI Gatewayのモデル順指定やCloudflare/Portkeyのステータスコード条件切替が使えます。アプリ改修を最小化しやすいので、複数チームで運用するなら有力です。参考: Vercel AI Gateway / Cloudflare AI Gateway / Portkey

コスト急増を防ぐ実務ルール

  • 上限設定:日次予算を超えたら高単価モデルを停止します
  • 用途分離:要約・分類は軽量モデル、重要生成だけ高性能モデルに寄せます
  • 監査:切替回数と1回あたり単価を週次で可視化します

運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。

実装ステップ3|通知・監査・再現テストまで回す

最後は運用の定着です。Runbookは作るだけだと、いざ本番で開かれません。通知、証跡、練習の3点をセットにすると機能します。イメージとしては、防災マニュアルと避難訓練を一緒に回す感じです。

  • 通知:Slackへ「切替発生」「復帰」「連続失敗」を分けて送ります
  • 証跡:provider、HTTP、latency、request-id、判定理由を1行JSONで保存します
  • 練習:月1回GameDayを実施し、意図的にClaudeを落として復旧導線を確認します

通知メッセージには「次の一手」を必ず書くのがおすすめです。例えば「15分後に自動再判定」「手動切替コマンドはこれ」の2行があるだけで、心理的負荷が一気に下がります。

僕の現場では、Mac miniを24時間稼働してtmuxで複数エージェントを回しています。だから、1台や1社APIへの依存は常に警戒しています。以前、結婚式Web招待状サービスを継続改善したときも、障害時の導線設計を詰めたことで90日以上ログイン継続率を維持できました。安定運用は派手じゃないですが、継続率と信頼に直結します

関連記事: Mac mini AIエージェント構成の実践記事 / AIコーディングエージェント比較

Anthropic依存を放置したまま1年過ごすと

ここは脅しではなく現実の話です。単一依存のままだと、障害時に「誰が判断するか」「どこを切り替えるか」が毎回ゼロからになります。その結果、復旧までの時間が伸び、チームの集中力が削られます。さらに、障害のたびに高単価モデルへ場当たり的に逃げると、コストも読めなくなります。

Runbookを知らないままだと、障害よりも“判断の遅さ”で損失が広がる可能性があります。だから、完璧な仕組みを待つより、まず5分監視と切替条件だけ先に固定するのがおすすめです。

この記事を書いている理由

僕自身、24時間運用の仕組みを回す中で「止まったあとに考える運用」の限界を何度も感じました。2026年3月にはMac mini 2台でClaude Code/Codexを自動切替する構成も検証して、単一ベンダー依存を避ける効果を実感しています。さらに、福祉現場のITでは小さな停止でも利用者対応に連鎖しやすいです。

550名以上の面談や非IT職種との対話を通じて、技術の価値は「難しい実装」より「現場で再現できる運用」に宿ると強く感じました。僕がこのテーマを伝えたいのは、同じつまずきを減らして、みんなの時間を守りたいからです

次のアクション

まずは今週中に、GitHub Actionsで5分監視のワークフローだけ作ってみてください。動き始めると改善点が一気に見えます。実装で詰まったら、気軽に相談してくれたら助かります。

今日からできるアクション

  • 監視ジョブをcron: */5 * * * *で作成し、3プロバイダの疎通を記録する
  • 429/503/529時の再試行回数とクールダウン時間を決める
  • 運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。