AI APIを本番環境に統合する際、最も頭を悩ませるのが「なぜこのリクエストは失敗したのか」「応答時間が気になる」「料金請求が予想と違う」といった問題です。私は3年以上にわたり複数のAI APIを運用していますが、特にHolySheheep AIへの移行後は、API呼び出しの構造が統一されたことでデバッグ効率が劇的に向上しました。本稿では、実際のエラーシナリオから始まり、リクエスト・レスポンスのログ取得方法、そして私の実践で培われた分析ツールの活用법을詳しく解説します。
実際のエラーシナリオから始める
まず、私が実際に遭遇した典型的な3つのエラーを見てみましょう。これらのエラーは、どんなAPIクライアントでも發生しうる普遍的な問題です。
シナリオ1:ConnectionErrorとタイムアウト
# よくあるタイムアウトエラー
import requests
try:
response = requests.post(
"https://api.holysheep.ai/v1/chat/completions",
headers={
"Authorization": f"Bearer {YOUR_HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
},
json={
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 100
},
timeout=5 # 5秒でタイムアウト
)
except requests.exceptions.Timeout as e:
print(f"TimeoutError: サーバー応答が5秒以内に返りませんでした")
print(f"Error details: {e}")
except requests.exceptions.ConnectionError as e:
print(f"ConnectionError: サーバーへの接続に失敗しました")
print(f"Network経路またはDNS解決の問題可能性があります")
シナリオ2:401 Unauthorized - APIキー認証エラー
# APIキー設定ミス导致的401エラー
import os
import requests
環境変数からAPIキーを取得(推奨パターン)
api_key = os.environ.get("HOLYSHEEP_API_KEY")
❌ よくある間違い:スペース混入
headers = {
"Authorization": f"Bearer {api_key}", # "Bearer "の後に余分なスペース
"Content-Type": "application/json"
}
✅ 正しいパターン
headers_correct = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
認証テスト
response = requests.post(
"https://api.holysheep.ai/v1/models",
headers=headers_correct
)
if response.status_code == 401:
print("認証エラー: APIキーが無効または期限切れです")
print(f"Response: {response.json()}")
シナリオ3:QuotaExceededError - 利用制限超過
# 料金制限超過エラーの例
{
"error": {
"type": "insufficient_quota",
"code": "monthly_limit_exceeded",
"message": "Your monthly usage limit has been reached.
Please upgrade your plan or wait until the next billing cycle.",
"param": null,
"code": 429
}
}
対策:使用量モニターの実装
import time
from datetime import datetime, timedelta
class UsageMonitor:
def __init__(self, api_key):
self.api_key = api_key
self.request_count = 0
self.total_tokens = 0
self.cost_tracker = []
def check_and_log(self, response, model):
"""レスポンスから使用量を取得してログ"""
if response.status_code == 200:
data = response.json()
usage = data.get("usage", {})
prompt_tokens = usage.get("prompt_tokens", 0)
completion_tokens = usage.get("completion_tokens", 0)
# HolySheepの2026年価格表に基づくコスト計算
prices = {
"gpt-4o": 0.000015, # $15/MTok
"claude-sonnet-4": 0.000015,
"gemini-2.0-flash": 0.0000025,
"deepseek-v3.2": 0.00000042
}
cost = (prompt_tokens + completion_tokens) * prices.get(model, 0.000015)
self.request_count += 1
self.total_tokens += prompt_tokens + completion_tokens
self.cost_tracker.append({
"timestamp": datetime.now().isoformat(),
"model": model,
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"cost_usd": cost
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] "
f"Request #{self.request_count} | "
f"Model: {model} | "
f"Cost: ${cost:.6f} | "
f"Total: ${sum(c['cost_usd'] for c in self.cost_tracker):.4f}")
リクエスト・レスポンス詳細ログ取得システム
デバッグにおいて最も重要なのは、リクエストとレスポンスの詳細を正確に記録することです。私の経験では、 проблемの80%はログ分析だけで解决できます。
包括的ログクラス
import json
import logging
import time
from datetime import datetime
from typing import Optional, Dict, Any
from pathlib import Path
class APIDebugLogger:
"""
AI APIリクエスト・レスポンスの詳細ログ記録クラス
HolySheep API対応
"""
def __init__(self, log_dir: str = "./api_logs"):
self.log_dir = Path(log_dir)
self.log_dir.mkdir(exist_ok=True)
# 構造化ログ設定
self.logger = logging.getLogger("APIDebugger")
self.logger.setLevel(logging.DEBUG)
# ファイルハンドラー
fh = logging.FileHandler(
self.log_dir / f"api_debug_{datetime.now().strftime('%Y%m%d')}.log"
)
fh.setLevel(logging.DEBUG)
# フォーマッター
formatter = logging.Formatter(
'%(asctime)s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
fh.setFormatter(formatter)
self.logger.addHandler(fh)
# メモリ内ログ保持
self.memory_logs = []
def log_request(self, method: str, url: str, headers: Dict, body: Any,
start_time: float):
"""リクエスト詳細を記録"""
elapsed_ms = (time.time() - start_time) * 1000
log_entry = {
"type": "request",
"timestamp": datetime.now().isoformat(),
"method": method,
"url": url,
"headers": self._sanitize_headers(headers),
"body": body,
"latency_ms": round(elapsed_ms, 2)
}
self.memory_logs.append(log_entry)
self.logger.debug(f"REQUEST | {method} {url} | Latency: {elapsed_ms:.2f}ms")
return log_entry
def log_response(self, response, end_time: float, start_time: float):
"""レスポンス詳細を記録"""
elapsed_ms = (time.time() - start_time) * 1000
try:
response_body = response.json()
except:
response_body = response.text
log_entry = {
"type": "response",
"timestamp": datetime.now().isoformat(),
"status_code": response.status_code,
"headers": dict(response.headers),
"body": response_body,
"latency_ms": round(elapsed_ms, 2),
"size_bytes": len(response.content)
}
self.memory_logs.append(log_entry)
# 成功/失敗に応じたログレベル
if response.status_code < 400:
self.logger.info(
f"RESPONSE | {response.status_code} | "
f"Size: {log_entry['size_bytes']} bytes | "
f"Latency: {elapsed_ms:.2f}ms"
)
else:
self