AI開発のエラーハンドリング - Retry戦略と特有の落とし穴

AI APIを使った開発では、通常のWeb APIとは異なる信頼性の問題に直面します。Rate LimitやTimeoutといった一般的なエラーに加え、AI特有の「見えないエラー」への対策が必要です。これらを適切に処理しないと、表面上は動作しているように見えても、実際には誤ったデータを処理し続けることになります。

結論

通常のRetry戦略に加え、AI特有のエラーパターンへの対策が必須です。レスポンス検証とFallback設計を組み合わせることで、実用レベルの信頼性を確保できます。

AI APIではどんなエラーが発生するのか?

AI APIでは以下のようなエラーが発生します。

  • Rate Limit(429): API呼び出し回数の上限超過
  • Timeout(504): レスポンスまでの時間切れ
  • JSON Parse失敗: 不正なJSON形式での応答

これらは通常のWeb APIでも発生するエラーです。エラーハンドリングとは、try-catchなどで例外を捕捉し、適切に処理する仕組みを指します。

AI特有の「見えないエラー」とは何か?

AI APIには、HTTPステータスコードでは検知できない厄介な問題があります。

プロンプトで指定した構造を無視する

JSON構造を明確に指定しても、フォーマット違反で返すケースがあります。必須フィールドが欠落したり、データ型が異なったりすることも珍しくありません。

エラー時にダミーデータを返す

エラーが発生した際、AIが親切心で「N/A」「不明」「データなし」といったダミーデータを埋め込むことがあります。空文字やnullで埋める場合もあります。HTTPステータスは200で返ってくるため、一見成功したように見えます。

失敗を成功と誤認させる出力

「データが見つかりませんでした」という文章を、正常なレスポンスとして返すケースです。エラーメッセージを通常の出力フィールドに混入させるため、後続処理で問題が発生します。

Retry戦略の基本

指数バックオフ

Retryの待機時間を1秒→2秒→4秒と指数的に増やす手法です。Rate Limit対策として有効で、サーバー側の負荷が回復するまで適切に待機できます。

最大試行回数の設定

無限ループを防ぐため、最大試行回数を設定します。試行回数3〜5回が現実的なラインです。これはAPI料金とのバランスを考慮した数字ですね。

Retryすべきエラーの判別

すべてのエラーをRetryするのは非効率です。

  • Retry推奨: 429(Rate Limit)、503(Service Unavailable)、504(Gateway Timeout)
  • 即座に失敗: 400(Bad Request)、401(Unauthorized)、403(Forbidden)

認証エラーや不正なリクエストは、何度試しても成功しません。

AIのハードコーディング問題をどう防ぐか?

レスポンス検証の徹底

AIの出力を信頼せず、必ず検証を入れます。

def validate_response(response):
    # 必須フィールドの存在確認
    if not response.get("data"):
        raise ValueError("Missing required field: data")

    # ダミーデータの検出
    if response["data"] in ["N/A", "不明", "unknown", ""]:
        raise ValueError("Invalid placeholder value detected")

    # データ型の検証
    if not isinstance(response["data"], dict):
        raise ValueError("Invalid data type")

    return response

この検証を通過したレスポンスのみを正常とみなします。

プロンプト設計での対策

プロンプトに以下を明記します。

「エラー時は決してダミーデータを返さず、処理を失敗させること。N/A、不明、データなし等のプレースホルダーは使用禁止。」

出力例には正常ケースのみを示します。エラーパターンを例示すると、AIがそれを模倣する可能性があります。

Fallback設計

Primary API→Fallback API→デフォルト値という3段構えで設計します。

async function callWithFallback() {
  try {
    return await primaryAPI();
  } catch (error) {
    console.warn("Primary API failed, trying fallback");
    try {
      return await fallbackAPI();
    } catch (fallbackError) {
      console.error("All APIs failed, using default");
      return getDefaultValue();
    }
  }
}

どの段階で成功したかをログに記録しておくと、障害分析に役立ちます。

構造化出力の活用

OpenAIのStructured OutputsやAnthropicのPrompt Caching等を使うことで、JSON Schemaによる厳密な制約を課せます。これにより、フォーマット違反を大幅に減らせます。詳しくは各APIの公式ドキュメントを参照してください。

実装例

PythonとTypeScriptそれぞれでシンプルな実装例を示します。

import time

def call_ai_with_retry(prompt, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = call_ai_api(prompt)
            validated = validate_response(response)
            return validated
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            wait_time = 2 ** attempt  # 指数バックオフ
            time.sleep(wait_time)
async function callAIWithRetry(prompt: string, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await callAIAPI(prompt);
      const validated = validateResponse(response);
      return validated;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      const waitTime = Math.pow(2, attempt) * 1000; // 指数バックオフ
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }
  }
}

コストと信頼性のバランス

Retry回数はAPI料金との兼ね合いで決めます。試行回数3〜5回が現実的なラインです。それ以上増やしても成功率はあまり上がらず、コストだけが増大します。

タイムアウト設定も重要です。1リクエストあたり30〜60秒程度に設定し、長時間待ち続けないようにします。

まとめ

AI APIの信頼性を上げるには、通常のRetry戦略だけでは不十分です。レスポンス検証を徹底し、プロンプト設計で問題を未然に防ぎ、Fallback設計で冗長性を持たせることが重要ですね。

コスト意識を持ちつつ、適切な設計を行えば、実用レベルの信頼性に到達できます。導入コストと得られる利益が見合うのであれば、これらの対策は必須です。