LLM APIを本番運用する場合、最も怖いのが「重複リクエストによる二重請求」です。特に決済処理や длительный会話の途中でネットワークエラーが発生すると、同じリクエストが何度も送信される可能性があります。

私は実際にHolySheep AIを使用して每天500ドル以上のAPIリクエストを処理していますが、過去に重複請求で痛い目に遭った経験を活かし、本稿では堅牢な去重・幂等性設計について解説します。

なぜ去重・幂等性設計が重要か

LLM APIのコストは馬鹿になりません。私のプロジェクトではGPT-4.1を頻繁に使用していますが、8ドル/1Mトークンの料金を考えると、1回の重複リクエストでも馬鹿になりません。

HolySheep AIでの実装

まず基本的なAPI呼び出しを確認しましょう。HolySheep AIのレートは1ドル=1ドル相当(公式¥7.3=$1比85%節約)で、コスト効率が非常に優れています。

# 基本設定
import requests
import hashlib
import time
import redis
from datetime import datetime, timedelta

BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY"

ヘッダー設定

HEADERS = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } def create_chat_completion(messages, idempotency_key=None): """ HolySheep AI APIへの幂等性付きリクエスト """ url = f"{BASE_URL}/chat/completions" payload = { "model": "gpt-4.1", "messages": messages, "max_tokens": 2000, "temperature": 0.7 } # 幂等性キーをヘッダーに追加 headers = HEADERS.copy() if idempotency_key: headers["Idempotency-Key"] = idempotency_key response = requests.post(url, json=payload, headers=headers, timeout=30) if response.status_code == 200: return response.json() elif response.status_code == 409: # 重複リクエスト検出 return {"error": "duplicate_request", "cached": True} else: raise Exception(f"API Error: {response.status_code} - {response.text}")

使用例

messages = [ {"role": "system", "content": "あなたは有帮助なアシスタントです。"}, {"role": "user", "content": "API設計について教えてください。"} ]

同じキーで呼び出すと結果がキャッシュされる

result1 = create_chat_completion(messages, idempotency_key="req-12345") result2 = create_chat_completion(messages, idempotency_key="req-12345") # キャッシュから返る

Redisを活用したクライアントサイド去重

API側で幂等性キーをサポートしていない場合、またはよりきめ細やかな制御が必要な場合は、Redisを使用した去重パターンが効果的です。私の環境ではレイテンシ50ms未満を記録しており、Redisのオーバーヘッドも最小限です。

import redis
import json
import hashlib
from typing import Any, Optional
import logging

logger = logging.getLogger(__name__)

class IdempotentAPIClient:
    """
    Redisを活用したAPI去重クライアント
    HolySheep AI推奨
    """
    
    def __init__(self, redis_host='localhost', redis_port=6379, ttl=3600):
        self.redis_client = redis.Redis(
            host=redis_host,
            port=redis_port,
            db=0,
            decode_responses=True
        )
        self.ttl = ttl  # キャッシュ有効期限(秒)
        self.base_url = "https://api.holysheep.ai/v1"
        self.api_key = "YOUR_HOLYSHEEP_API_KEY"
    
    def _generate_request_hash(self, payload: dict) -> str:
        """
        リクエストボディから一意のハッシュを生成
        """
        normalized = json.dumps(payload, sort_keys=True, ensure_ascii=False)
        return hashlib.sha256(normalized.encode()).hexdigest()[:32]
    
    def _is_duplicate(self, request_hash: str) -> Optional[dict]:
        """
        同一リクエストが既に処理済みかチェック
        """
        cache_key = f"api:dedup:{request_hash}"
        cached = self.redis_client.get(cache_key)
        
        if cached:
            logger.info(f"重複リクエスト検出: {request_hash}")
            return json.loads(cached)
        
        return None
    
    def _set_cache(self, request_hash: str, response: dict):
        """
        レスポンスをキャッシュ
        """
        cache_key = f"api:dedup:{request_hash}"
        self.redis_client.setex(
            cache_key,
            self.ttl,
            json.dumps(response)
        )
        logger.info(f"レスポンスをキャッシュ: {request_hash}")
    
    def _acquire_lock(self, request_hash: str, timeout=30) -> bool:
        """
        分散ロックを取得(同時リクエスト防止)
        """
        lock_key = f"api:lock:{request_hash}"
        # NX: キーが存在しない場合のみ設定、EX: 有効期限
        return self.redis_client.set(lock_key, "1", nx=True, ex=timeout)
    
    def _release_lock(self, request_hash: str):
        """
        分散ロックを解放
        """
        lock_key = f"api:lock:{request_hash}"
        self.redis_client.delete(lock_key)
    
    def request(self, payload: dict, model: str = "gpt-4.1") -> dict:
        """
        去重機能付きAPIリクエスト
        """
        request_hash = self._generate_request_hash(payload)
        
        # ステップ1: キャッシュチェック
        cached_response = self._is_duplicate(request_hash)
        if cached_response:
            return {
                "data": cached_response,
                "cached": True,
                "request_hash": request_hash
            }
        
        # ステップ2: ロック取得(処理中チェック)
        if not self._acquire_lock(request_hash):
            # 他のプロセスが処理中 - 少し待ってから再試行
            time.sleep(0.1)
            cached_response = self._is_duplicate(request_hash)
            if cached_response:
                return {"data": cached_response, "cached": True}
            raise Exception("リクエスト処理中のため待機してください")
        
        try:
            # ステップ3: API呼び出し
            start_time = time.time()
            
            response = requests.post(
                f"{self.base_url}/chat/completions",
                headers={
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json",
                    "Idempotency-Key": request_hash  # HolySheep推奨
                },
                json={
                    "model": model,
                    **payload
                },
                timeout=30
            )
            
            latency_ms = (time.time() - start_time) * 1000
            logger.info(f"API応答時間: {latency_ms:.2f}ms")
            
            if response.status_code == 200:
                result = response.json()
                # 成功レスポンスをキャッシュ
                self._set_cache(request_hash, result)
                return {
                    "data": result,
                    "cached": False,
                    "latency_ms": latency_ms,
                    "request_hash": request_hash
                }
            else:
                raise Exception(f