
AI APIタイムアウト:原因、リトライパターン、フォールバック設計

AI APIリクエストがタイムアウトした。しかし「タイムアウト」は一つの問題ではなく、同じエラーメッセージの裏に少なくとも4つの異なる問題が潜んでいる。
テキストモデルが30秒後にタイムアウトするのと、動画生成ジョブが5分後にタイムアウトするのはまったく別の問題だ。間違ったタイプに対処すればエンジニアリング時間を無駄にし、問題を悪化させる可能性すらある。
このガイドでは、どのタイプのタイムアウトに直面しているかを診断し、適切な対処パターンを選ぶ方法を解説する。
まとめ
- AI APIタイムアウトには異なる根本原因がある:モデルのレイテンシ、プロバイダーのキュー、大きな入力データ、ネットワークの問題。
- テキストモデルのタイムアウトと動画/画像生成のタイムアウトでは、対処戦略が異なる。
- むやみにリトライしない — タイムアウトの中にはリクエストがまだ処理中であることを意味するものもある。
- 長時間タスク(動画、画像)には、同期レスポンスを待つのではなく非同期パターンを使う。
- フォールバックは必要になる前に設計する:短いタイムアウト+フォールバックモデルの方が、長いタイムアウト+フォールバックなしよりも多くの場合優れている。
タイムアウト診断テーブル
修正方法を選ぶ前に、このテーブルでタイムアウトのタイプを特定しよう:
| タイムアウトタイプ | 典型的な時間 | 根本原因 | 確認方法 | 適切な対処 |
|---|---|---|---|---|
| テキストモデル — レスポンスが遅い | 15〜60秒 | 大きな入力、複雑な推論、または大量の出力トークン | 入力サイズとmax_tokensを確認 | 入力を削減、max_tokensを下げる、またはより高速なモデルに切り替える |
| テキストモデル — プロバイダー過負荷 | 30〜120秒 | プロバイダーが高負荷状態;リクエストがキューに入っている | オフピーク時に同じリクエストを試す | バックオフ付きリトライ、または別のプロバイダーにルーティング |
| 動画/画像生成 — 通常の処理 | 60〜300秒以上 | 生成には本来時間がかかる(特に動画) | プロバイダーのドキュメントで想定生成時間を確認 | 非同期ポーリングを使用、同期待ちはしない |
| 動画/画像生成 — キューの滞留 | 300秒以上 | プロバイダーのキューに大量のジョブが滞留 | プロバイダーのキュー状態または位置を確認 | キュー管理を追加、ユーザーの期待値を設定、または別のプロバイダーを使用 |
| ネットワークタイムアウト | 不定 | DNS、ファイアウォール、プロキシ、接続の問題 | シンプルなヘルスチェックリクエストでテスト | ネットワーク設定を修正、API呼び出しではない |
| クライアント側タイムアウトが短すぎる | 設定依存 | HTTPクライアントのタイムアウトがモデルの必要時間より短い | タイムアウト設定を延長して再テスト | クライアントのタイムアウトを想定レスポンス時間に合わせる |
パターン1:テキストモデルのタイムアウト対処
テキストモデルのタイムアウトは通常、以下の3つのいずれかが原因だ:
1.1 大きな入力またはmax_tokensが大きい
max_tokensの値が大きいと、より長い生成が可能になり、その分時間がかかる。# 問題:大きな入力 + 高いmax_tokens = 遅いレスポンス
response = client.chat.completions.create(
model="gpt-4o",
messages=very_long_messages, # 100K+ tokens
max_tokens=4096 # 長い出力を要求
)
# 解決策:入力を減らすか出力を制限
response = client.chat.completions.create(
model="gpt-4o",
messages=trimmed_messages, # 50Kトークンに削減
max_tokens=1024 # 短い出力
)1.2 プロバイダーが高負荷
ピーク時には、プロバイダーがリクエストをキューに入れることがある。これは明示的なキューメッセージではなく、タイムアウトとして表れる。
- 同じリクエストがオフピーク時には問題なく動作する
- エラーが断続的で、一貫しない
- 同時に他のユーザーも同様の問題を報告している
- ジッター付きバックオフでリトライ
- 代替プロバイダーまたはモデルにルーティング
- ストリーミングを使って部分的な結果をより早く取得
1.3 タイムアウト緩和としてのストリーミング
ストリーミングは生成を高速化するわけではないが、トークンの返却がより早く始まる。これにより、クライアント側のタイムアウトの発動を防ぐことができる:
# 同期 — クライアントが完全なレスポンスを待つ間にタイムアウトする可能性
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=2048
)
# ストリーミング — 最初のトークンがより早く到着し、接続を維持
stream = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=2048,
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")パターン2:動画/画像生成のタイムアウト対処
動画と画像の生成はテキストとは根本的に異なる。30秒から数分の生成時間は正常であり、エラーではない。
2.1 同期生成 vs 非同期生成
| アプローチ | 仕組み | 使い所 |
|---|---|---|
| 同期 | 1回のHTTP呼び出しで完全なレスポンスを待つ | 高速な生成(30秒未満)、シンプルな統合 |
| 非同期ポーリング | ジョブ送信 → ジョブID取得 → ステータス確認 → 結果取得 | 動画生成、バッチ処理、30秒以上のタスク |
| Webhooks | ジョブ送信 → 完了時にプロバイダーがエンドポイントを呼び出す | 大量パイプライン、イベント駆動アーキテクチャ |
2.2 非同期ポーリングパターン
import asyncio
import httpx
async def generate_video_async(client, prompt: str, timeout: int = 600):
"""動画生成ジョブを送信し、完了までポーリングする。"""
# ステップ1:ジョブを送信
submit_response = await client.post(
"/v1/video/generations",
json={"model": "veo-3-fast", "prompt": prompt}
)
job_id = submit_response.json()["id"]
# ステップ2:完了までポーリング
for _ in range(timeout // 5): # 5秒ごとに確認
status_response = await client.get(f"/v1/video/generations/{job_id}")
status = status_response.json()
if status["status"] == "completed":
return status["result"]
elif status["status"] == "failed":
raise RuntimeError(f"Generation failed: {status.get('error')}")
await asyncio.sleep(5)
raise TimeoutError(f"Video generation did not complete within {timeout}s")2.3 キュー位置の把握
動画生成プロバイダーにバックログがある場合、ジョブは処理開始前にキューで待機する。一部のプロバイダーはキュー位置を公開している:
Status: queued → position 42
Status: queued → position 15
Status: processing → estimated 90s remaining
Status: completed → download URL availableプロバイダーがキュー位置を公開していない場合は、過去の待ち時間から推定し、それに応じてユーザーの期待値を設定する。
パターン3:タイムアウトのためのスマートリトライロジック
すべてのタイムアウトを同じ方法でリトライすべきではない:
import asyncio
import random
async def retry_with_timeout_awareness(
make_request,
max_retries: int = 3,
base_timeout: float = 30.0
):
"""タイムアウトタイプを意識したリトライ。"""
for attempt in range(max_retries):
try:
return await asyncio.wait_for(
make_request(),
timeout=base_timeout * (1.5 ** attempt) # リトライごとにタイムアウトを延長
)
except asyncio.TimeoutError:
if attempt == max_retries - 1:
raise
# サンダリングハード効果を防ぐためにジッターを追加
delay = min(30, (2 ** attempt) + random.uniform(0, 1))
await asyncio.sleep(delay)
except Exception as e:
# タイムアウト以外のエラー:自動リトライしない
if "429" in str(e):
delay = min(60, (2 ** attempt) + random.uniform(0, 2))
await asyncio.sleep(delay)
else:
raiseタイムアウトタイプ別のリトライルール
| タイムアウトタイプ | リトライすべきか | 方法 |
|---|---|---|
| テキストモデルが遅い | はい | バックオフ付きリトライ;リトライ時により高速なモデルを検討 |
| プロバイダー過負荷 | はい(慎重に) | より長いバックオフでリトライ;別のプロバイダーを検討 |
| 動画生成がまだ処理中 | いいえ — ジョブがまだ実行中の可能性 | 再送信ではなくステータスをポーリング |
| ネットワークタイムアウト | はい | まずネットワークを修正;接続確認後にリトライ |
| クライアントタイムアウトが短すぎる | いいえ — 代わりにタイムアウトを延長 | 設定を調整、リトライはしない |
最も危険な間違いは、まだ処理中の動画生成ジョブをリトライすることだ。これはジョブの重複を生み、コストを無駄にし、プロバイダーのキューを過負荷にする可能性がある。
パターン4:本番環境のためのフォールバック設計
タイムアウトトリガーのモデルフォールバック
async def call_with_fallback(messages, client, primary_model, fallback_model):
"""プライマリモデルを試し、タイムアウト時にはより高速なモデルにフォールバック。"""
try:
return await asyncio.wait_for(
client.chat.completions.create(
model=primary_model,
messages=messages
),
timeout=30.0
)
except asyncio.TimeoutError:
# より高速な(場合によってはより小さな)モデルにフォールバック
return await client.chat.completions.create(
model=fallback_model,
messages=messages
)ルーティングゲートウェイによるタイムアウト耐性
すべてのサービスにフォールバックロジックを実装する代わりに、ルーティングゲートウェイでインフラレベルのタイムアウト処理が可能だ:
- プライマリルートが遅い場合、より高速なプロバイダーにルーティング
- 異なるアップストリームパスで自動リトライ
- 実際に使用されたモデルを返し、アプリケーションに何が起きたかを通知
from openai import OpenAI
client = OpenAI(
api_key="your-evolink-key",
base_url="https://api.evolink.ai/v1"
)
# Smart Routerがプロバイダー選択とフォールバックを処理
response = client.chat.completions.create(
model="evolink/auto",
messages=messages
)タイムアウト設定リファレンス
| 設定項目 | 推奨値 | 理由 |
|---|---|---|
| HTTPクライアントタイムアウト(テキスト) | 60〜120秒 | 大きな入力と複雑な推論に対応 |
| HTTPクライアントタイムアウト(画像) | 120〜300秒 | 画像生成はモデルと解像度により異なる |
| HTTPクライアントタイムアウト(動画) | 非同期ポーリングを使用 | 同期タイムアウトは動画には不適切 |
| リトライ回数 | テキストは2〜3回、処理中の動画は0回 | 動画/画像ジョブの重複を防止 |
| バックオフの基本遅延 | ジッター付き2秒 | プロバイダー復旧時のサンダリングハード効果を防止 |
| フォールバックモデル切り替えタイムアウト | 15〜30秒 | ユーザーがフラストレーションを感じる前により高速なモデルに切り替え |
関連記事
- Fix OpenRouter 429 "Provider Returned Error" — エラーがタイムアウトではなくレート制限の場合
- Context Length Exceeded in LLM API Calls — 大きな入力がタイムアウトではなく拒否を引き起こす場合
- How to Reduce 429 Errors in Agent Workloads — タイムアウトにつながるバーストトラフィックの管理
- Best AI API Platform for Production Reliability — 組み込みフェイルオーバーを備えたプラットフォームの選択
FAQ
AI APIリクエストがモデルは動作しているのにタイムアウトするのはなぜ?
タイムアウトは通常、以下のいずれかが原因だ:(1) 大きな入力データの処理に時間がかかっている、(2) プロバイダーが高負荷、(3) クライアント側のタイムアウト設定が短すぎる、(4) ネットワークの問題。モデル自体は正常に動作している可能性がある。
タイムアウトを延長すべきか、それとも別のアプローチを取るべきか?
場合による。テキストモデルでは、タイムアウト延長は時折遅いレスポンスに対して有効だ。動画/画像生成では、同期タイムアウトを延長するのではなく非同期ポーリングに切り替えるべきだ。継続的なタイムアウトの場合は、制限を引き上げる前に根本原因を調査しよう。
タイムアウトとレート制限エラーは同じもの?
いいえ。タイムアウトはサーバーが制限時間内に応答しなかったことを意味する。レート制限(429)はサーバーがリクエストを能動的に拒否したことを意味する。タイムアウトは処理の遅延を示すことが多く、429はリクエスト過多を示す。
動画生成でのタイムアウトはどう対処する?
動画生成を同期的に待つことは絶対にしない。ポーリングまたはWebhooksを使った非同期ジョブ送信を使用する。ポーリング中に動画ジョブがタイムアウトした場合、再送信前にステータスを確認すること — ジョブがまだ処理中の可能性がある。
ストリーミングでタイムアウトを防げる?
ストリーミングは最初のトークンが素早く到着して接続を維持するため、クライアント側のタイムアウトを防ぐことができる。ただし、ストリーミングが全体の生成速度を上げるわけではない — 配信パターンが変わるだけだ。
タイムアウト時にフォールバックモデルに切り替えるタイミングは?
閾値を設定し(例:テキストなら15〜30秒)、プライマリモデルがタイムアウトしたらより高速なモデルに切り替える。これにより、エラーではなくレスポンスをユーザーに返すことができる。フォールバックモデルは性能が劣るかもしれないが、やや劣る回答でも回答がないよりはましだ。

