AI APIを本番運用していると、必ず遭遇するのが一時的なエラーです。私のプロジェクトでは、夜間のバッチ処理で突如 ConnectionError: timeout が頻発し、サービスの可用性が大きく低下した経験があります。本稿では、HolySheep AI を含むAI API呼び出しにおいて可用性を最大化するリトライ戦略を、実体験に基づき詳細に解説します。

リトライ戦略の基礎:なぜbackoffが必要か

AI API呼び出しでエラーが発生した際、即座に再リクエストを送ると以下のような悪影響が生じます:

Backoff(待機)戦略は、指数関数的または線形的に再試行の間隔を広げることで、サーバーへの負荷を最小限に抑えつつ、成功概率を高めます。

Linear Backoff:均等間隔リトライ

Linear Backoffは、每次リトライ時に固定の時間間隔を加算する方式です。

import time
import requests

def linear_backoff_request(
    url: str,
    headers: dict,
    data: dict,
    max_retries: int = 5,
    base_delay: float = 1.0,
    delay_increment: float = 2.0
) -> dict:
    """
    Linear Backoff 実装
    リトライ間隔: base_delay, base_delay+increment, base_delay+2*increment...
    """
    for attempt in range(max_retries):
        try:
            response = requests.post(
                f"https://api.holysheep.ai/v1/chat/completions",
                headers=headers,
                json=data,
                timeout=30
            )
            
            if response.status_code == 200:
                return response.json()
            
            # 4xxエラーはリトライしない(客户端エラー)
            if 400 <= response.status_code < 500:
                print(f"クライアントエラー {response.status_code}: リトライ中止")
                return None
                
        except requests.exceptions.Timeout:
            print(f"試行 {attempt + 1}/{max_retries}: タイムアウト")
        except requests.exceptions.ConnectionError as e:
            print(f"試行 {attempt + 1}/{max_retries}: 接続エラー - {e}")
        
        if attempt < max_retries - 1:
            # Linear: base_delay + (attempt * increment)
            wait_time = base_delay + (attempt * delay_increment)
            print(f"{wait_time}秒後にリトライ...")
            time.sleep(wait_time)
    
    print("最大リトライ回数に達しました")
    return None

使用例

headers = { "Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY", "Content-Type": "application/json" } payload = { "model": "gpt-4.1", "messages": [{"role": "user", "content": "Hello!"}], "max_tokens": 100 } result = linear_backoff_request( url="https://api.holysheep.ai/v1/chat/completions", headers=headers, data=payload ) print(result)

Linear Backoffの特徴:

Exponential Backoff:指数関数的リトライ

Exponential Backoffは、失敗の度に待機時間を指数関数的に増加させる方式です。

import time
import random
import requests
from typing import Optional

def exponential_backoff_request(
    prompt: str,
    model: str = "gpt-4.1",
    max_retries: int = 6,
    base_delay: float = 1.0,
    max_delay: float = 64.0,
    jitter: bool = True
) -> Optional[dict]:
    """
    Exponential Backoff with Jitter 実装
    
    待機時間計算: min(max_delay, base_delay * (2 ** attempt)) + random_jitter
    間隔パターン例: 1-2秒, 2-4秒, 4-8秒, 8-16秒, 16-32秒, 32-64秒
    """
    headers = {
        "Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
        "Content-Type": "application/json"
    }
    
    payload = {
        "model": model,
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": 500
    }
    
    for attempt in range(max_retries):
        try:
            response = requests.post(
                "https://api.holysheep.ai/v1/chat/completions",
                headers=headers,
                json=payload,
                timeout=30
            )
            
            if response.status_code == 200:
                return response.json()
            
            # 429 Too Many Requests
            if response.status_code == 429:
                retry_after = response.headers.get('Retry-After')
                if retry_after:
                    wait_time = float(retry_after)
                else:
                    wait_time = min(max_delay, base_delay * (2 ** attempt))
                    if jitter:
                        wait_time += random.uniform(0, 1)  # フルジッター
                    print(f"レートリミット到達。{wait_time:.2f}秒後にリトライ...")
                    time.sleep(wait_time)
                    continue
            
            # 401 Unauthorized
            if response.status_code == 401:
                print("認証エラー: APIキーを確認してください")
                return None
            
            # 500番台サーバーエラーはリトライ対象
            if 500 <= response.status_code < 600:
                wait_time = min(max_delay, base_delay * (2 ** attempt))
                if jitter:
                    wait_time += random.uniform(0, wait_time * 0.1)  #  частичнаяジッター
                print(f"サーバーエラー {response.status_code}。{wait_time:.2f}秒後にリトライ...")
                time.sleep(wait_time)
            else:
                print(f"予期しないエラー {response.status_code}")
                return None
                
        except requests.exceptions.Timeout:
            wait_time = min(max_delay, base_delay * (2 ** attempt))
            if jitter:
                wait_time += random.uniform(0, 1)
            print(f"試行 {attempt + 1}/{max_retries}: タイムアウト。{wait_time:.2f}秒後にリトライ...")
            time.sleep(wait_time)
            
        except requests.exceptions.ConnectionError as e:
            print(f"試行 {attempt + 1}/{max_retries}: 接続エラー - {str(e)[:50]}")
            if attempt < max_retries - 1:
                wait_time = min(max_delay, base_delay * (2 ** attempt))
                time.sleep(wait_time)
    
    print("最大