ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-04-23上級

Antigravity Python API を本番で落とさない — リトライ・タイムアウト・サーキットブレーカー実装ガイド

Antigravity で開発する Google Gen AI SDK アプリケーションを本番環境で安定稼働させるための、リトライ・タイムアウト・サーキットブレーカー・バックプレッシャー設計を、動作する Python コードと運用経験から体系的に解説します。

python26antigravity435production59resilience7retry8circuit-breaker3rate-limit4

プレミアム記事

深夜 2 時、Slack の通知音で目が覚めて、アラートを確認すると本番の AI エンドポイントが 15 分間ほぼ全滅している——こういう経験、一度はしてみたくないのではないでしょうか。原因は大抵、Google Gen AI API 側の一時的な 503、もしくはこちら側のリトライ暴走による RPM 上限の自滅です。

Antigravity で Python のエージェントや API サーバーを開発していると、最初は「とりあえず動くコード」を書いてしまいがちです。try: ... except: ... でエラーを握りつぶし、失敗したらそのままクライアントにエラーを返す。ローカルでは問題なく動きますが、本番のトラフィックに晒すと、ほんの小さなきっかけで連鎖的に落ちていきます。

本番で生き残るコードに求められるのは、派手な工夫ではなく、地味な設計の積み重ねです。ここではGoogle Gen AI SDK を使った Python サービスを、Antigravity 環境で設計・実装・デプロイしてきた私の運用経験をもとに、「どこでどう壊れるか」「それをどう受け止めるか」を、実際に動くコードと一緒に体系化していきます。公式ドキュメントには書かれていない、本番でだけ露呈する罠にも踏み込みますので、一通り読み終えたころには、自分のプロジェクトに足りない「堅さ」がはっきり見えてくるはずです。

本番で AI API が落ちるパターンを正確に分類する

リトライを書く前にやるべきことは、「自分のコードはどのタイプの失敗で倒れているのか」を把握することです。失敗を一括りに扱うと、本来リトライすべきでないエラーまで無限に叩き続け、コストと信頼性の両方を失います。

Google Gen AI API を叩くときに遭遇する失敗は、大まかに次の 5 種類に整理できます。

  • 一時的なサービス障害: HTTP 500 / 502 / 503 / 504。数秒から数分で回復します。リトライの対象。
  • レート制限: HTTP 429。RPM(Requests Per Minute)もしくは TPM(Tokens Per Minute)上限に到達。Retry-After ヘッダに従ってリトライ。
  • クライアント側エラー: HTTP 400 / 401 / 403 / 404。リクエストそのものに問題があり、リトライしても直らありません。即時失敗させる。
  • タイムアウト: 接続・読み取り・総処理時間のいずれかが上限超過。ネットワーク問題か、モデル側の遅延か、切り分けが必要。
  • コンテンツフィルタ由来の失敗: FinishReason.SAFETY / FinishReason.RECITATION 等。プロンプトを変えない限り何度叩いても同じ結果なので、リトライ禁止。

このうち、盲目的にリトライしてよいのは最初の 2 つだけです。ところが、現場で見るコードの多くは「except Exception で全部受けて 3 回リトライ」という雑な実装になっています。これだと 400 系のエラーまでリトライしてしまい、ユーザーには遅延と同じエラーが連続で返る、という最悪の体験になります。

失敗を分類するための Exception 階層を先に作る

Antigravity の Agent に「Gen AI SDK のエラーをリトライ可否別に分類する Exception 階層を書いて」と依頼すると、こんな叩き台が得られます。これをプロジェクトの起点にして、拡張していくのが実用的です。

# ai_errors.py — リトライ可否で分岐できる Exception 階層
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
 
class AIError(Exception):
    """AI API 呼び出し全般の基底クラス。"""
 
class RetryableAIError(AIError):
    """一時的な失敗。指数バックオフで再試行してよい。"""
 
class RateLimitedError(RetryableAIError):
    """429。retry_after 秒だけ待つこと。"""
 
    def __init__(self, message: str, retry_after: Optional[float] = None):
        super().__init__(message)
        self.retry_after = retry_after
 
class PermanentAIError(AIError):
    """リクエストに問題があり、リトライしても直らない。"""
 
@dataclass
class SafetyBlocked(PermanentAIError):
    """コンテンツフィルタで拒否。プロンプトを見直す必要がある。"""
    reason: str
 
    def __str__(self) -> str:
        return f"Blocked by safety filter: {self.reason}"

リトライロジックはこの階層を前提にして組み立てます。RetryableAIError とそのサブクラスだけを再試行し、PermanentAIError は即座にクライアントへ返す——これだけで、暴走リトライの 7 割は消えます。私は新しいプロジェクトを始めるとき、ほぼ必ずこの階層から書き始めます。

exponential backoff + jitter をサボらずに実装する

リトライ間隔を固定値(たとえば毎回 1 秒)にしてはいけません。複数のプロセスが同時にリトライすると、すべて同じタイミングでサーバーに波状攻撃を仕掛けてしまい、サーバーが起き上がる瞬間をつぶし続けることになります。これが有名な「thundering herd 問題」です。

解決策は、指数的に伸びる待機時間ランダムなジッター(ゆらぎ) を組み合わせることです。AWS の SDK でも Google の公式サンプルでも、この組み合わせが標準になっています。

tenacity を使った堅い実装

tenacity ライブラリは Python でリトライを書くときの事実上のデファクトです。デコレータで宣言的にポリシーを記述できるため、ロジック本体がリトライコードに埋もれずに済みます。

# resilient_client.py — tenacity による exponential backoff + jitter
import asyncio
import random
from typing import Any
 
import httpx
from tenacity import (
    AsyncRetrying,
    RetryError,
    retry_if_exception_type,
    stop_after_attempt,
    stop_after_delay,
    wait_exponential_jitter,
    before_sleep_log,
)
import logging
 
from ai_errors import (
    AIError,
    PermanentAIError,
    RateLimitedError,
    RetryableAIError,
)
 
logger = logging.getLogger(__name__)
 
def _classify_httpx_error(exc: httpx.HTTPStatusError) -> AIError:
    """httpx の HTTPStatusError を自前 Exception にマップする。"""
    status = exc.response.status_code
    if status == 429:
        # Retry-After は RFC 7231 に従い「秒数」または「HTTP-date」だが、
        # Google API は秒数しか返さないため int で受ける前提でよい。
        retry_after = exc.response.headers.get("retry-after")
        return RateLimitedError(
            f"Rate limited: {exc.response.text[:200]}",
            retry_after=float(retry_after) if retry_after else None,
        )
    if status in (500, 502, 503, 504):
        return RetryableAIError(f"Transient {status}: {exc.response.text[:200]}")
    return PermanentAIError(f"{status}: {exc.response.text[:200]}")
 
async def call_with_retry(
    client: httpx.AsyncClient,
    request: httpx.Request,
    *,
    max_attempts: int = 5,
    total_budget_sec: float = 20.0,
) -> httpx.Response:
    """指数バックオフ付きで Gen AI API を呼ぶ。
 
    - max_attempts: 最大試行回数(初回を含む)
    - total_budget_sec: 全試行を合計した上限。超えた時点で打ち切る
    - RateLimitedError は Retry-After に従う
    - PermanentAIError は即座に投げ直して終わる
    """
    async for attempt in AsyncRetrying(
        reraise=True,
        stop=stop_after_attempt(max_attempts) | stop_after_delay(total_budget_sec),
        wait=wait_exponential_jitter(initial=1, max=8, jitter=1.5),
        retry=retry_if_exception_type(RetryableAIError),
        before_sleep=before_sleep_log(logger, logging.WARNING),
    ):
        with attempt:
            try:
                response = await client.send(request)
                response.raise_for_status()
                return response
            except httpx.HTTPStatusError as exc:
                err = _classify_httpx_error(exc)
                if isinstance(err, RateLimitedError) and err.retry_after is not None:
                    # サーバー指定の待機時間に従う(ジッターは加えない)
                    logger.warning(
                        "429 received; sleeping %.2fs per Retry-After",
                        err.retry_after,
                    )
                    await asyncio.sleep(err.retry_after + random.uniform(0, 0.5))
                    raise RetryableAIError("Retryable after 429") from exc
                raise err from exc
            except (httpx.ConnectError, httpx.ReadTimeout) as exc:
                # ネットワーク層の一時障害もリトライ対象
                raise RetryableAIError(f"Network error: {exc}") from exc

このコードの重要なポイントは 4 つあります。

まず wait_exponential_jitter(initial=1, max=8, jitter=1.5) で、1 秒・2 秒・4 秒・8 秒と指数的に待機時間が伸び、そこに 0〜1.5 秒のランダムなずれが加わります。複数プロセスが同時に起動していても、リトライのタイミングが自然にばらけます。

次に stop_after_attempt(max_attempts) | stop_after_delay(total_budget_sec) で、「試行回数」と「総時間予算」の両方で打ち切るようにしています。stop_after_delay がないと、1 回のリトライが極端に遅れたときに全体が破綻します。

3 つ目は retry_if_exception_type(RetryableAIError) の使い方です。前節で作った Exception 階層が、ここで効いてきます。PermanentAIError は tenacity が再試行対象から外すため、自然に即時失敗します。

4 つ目は 429 の扱いです。Retry-After ヘッダがあるときは、exponential backoff ではなくサーバーが指定した値に従います。そのあとに自前でちょっとしたジッターを足しているのは、Retry-After が秒単位で丸められているため、同じ瞬間に大量のクライアントが復帰しないようにするためです。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
「深夜の 503 エラーで本番サービスが止まった」「リトライが暴走して請求が跳ねた」といった、Python × Gen AI SDK の本番運用で誰もが通る失敗パターンを、具体的な対策コードで今日から回避できるようになります
exponential backoff + jitter・サーキットブレーカー・トークン予算付きレートリミッタを tenacity / httpx / asyncio で実装する完全なパターン集を習得し、自分の Antigravity プロジェクトにそのまま持ち込めます
ストリーミング応答の中断リカバリ・コスト上限つきリトライ・OpenTelemetry との接続まで、本番の AI API 運用で必要になる設計判断を一通り身につけ、障害に強いサービスを自力で組めるようになります
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

Agents & Manager2026-04-13
Antigravity AIエージェント耐障害性設計 — リトライ・サーキットブレーカー・フォールバックで本番を守る
Antigravityで構築するAIエージェントに耐障害性を組み込む実践ガイド。リトライ戦略、サーキットブレーカー、モデルフォールバック、チェックポイント設計まで本番運用に必要な全パターンを網羅する。
連携・プラグイン2026-04-22
Antigravity API を Python で本番運用する前に必ず入れる3つの保護装置
リトライ・タイムアウト・サーキットブレーカー — Antigravity API を本番運用する前に必ず入れておきたい3つの保護装置を、実装コード付きで解説します。
アプリ開発2026-06-28
ストリーミング表示がローカルでは動くのに本番だと一括表示に戻るとき — 中間プロキシのバッファリングと沈黙する停止を計測する運用メモ
Antigravity 経由の Gemini ストリーミングを SSE で配信すると、ローカルでは1文字ずつ出るのに本番では数秒固まってから一括で出ます。中間プロキシのバッファリング・アイドル切断・再接続による二重生成を、計測してから潰す運用メモです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →