😊

OpenAI API レート制限とエラー処理ガイド

2025/01/18に公開

OpenAI API レート制限とエラー処理ガイド

4oのビジョン機能を使ってOCRでどこまでできるか試していたら、急に使えなくなってしまった。どうやらAPI利用制限に引っかかったようです。
APIの利用制限について、全然確認していなかったのでこれを機会に確認してみようと思います。

Rate limitsを読んでみよう。

OpenAI APIのレート制限について説明する。
レート制限は、悪用防止、公平なアクセス確保、インフラストラクチャの負荷管理のために設けられており、リクエスト数(RPM, RPD)、トークン数(TPM, TPD)、画像数(IPM)で計測される。

レート制限とは

APIが、指定された時間内にユーザーまたはクライアントがサービスにアクセスできる回数を制限するもの

レート制限の目的

  • APIの悪用や不正利用から保護する 悪意ある者がAPIに大量のリクエストを送るなどをして、システムに過負荷をかけたり、サービスを中断をさせようとする攻撃を防ぐ
  • 全員がAPIに公平にアクセスできるようにする 特定の人や組織が過剰なリクエストを行うと、APIに遅延が生じる可能性がある。
  • インフラストラクチャへの負荷を管理する APIへのリクエストが増加すると、サーバーに負荷がかかり、パフォーマンスが低下する可能性がある。レート制限により、すべてのユーザーに対して、スムーズで一貫した体験を提供する。

レート制限は5つの方法で測定される

  • RPM: 1日当たりのリクエスト数
  • TPM: 1分当たりのトークン数
  • TPD: 1日当たりのトークン数
  • IPM: 1分当たりの画像数

この5つのいずれかの基準に達すると制限が課される。

その他に重要な点として、

  • レート制限は、ユーザーレベルではなく、組織レベルおよびプロジェクトレベルで定義される。
  • レート制限は、使用されるモデルによって異なる
  • 組織がAPIに1ヶ月で費やすことができる合計金額にも制限があり、これは「利用制限」とも呼ばれる。
  • 一部のモデルファミリでは、レート制限を共有します。特定の「共有制限」にリストされているモデルは、その共有制限内でレート制限を共有している。

ユーザーティア

OpenAI APIの使用量とAPIへの支出が増えるにつれて、自動的に次の利用階層に移行し、ほとんどのモデルでレート制限が増加する。

  • 無料: 許可された地域にいるユーザーが対象、月額100ドル
  • ティア1: 5ドル支払い済み、月額100ドル
  • ティア2: 50ドル支払い済み、最初の支払いが成功してから7日以上経過、月額500ドル
  • ティア3: 100ドル支払い済み、最初の支払いが成功してから7日以上経過、月額1,000ドル
  • ティア4: 250ドル支払い済み、最初の支払いが成功してから14日以上経過、月額5,000ドル
  • ティア5: 1,000ドル支払い済み、最初の支払いが成功してから30日以上経過、月額200,000ドル

それぞれのモデルのリミットについては、公式HPのtier rate limitsで確認できる。

HTTPレスポンスヘッダ

Rate Limits in headers

HTTPレスポンスのヘッダーで、レート制限に関する重要な情報 (残りリクエスト数、トークン数など) を確認することもできる。 ヘッダーフィールドの例は以下の通り:

  • x-ratelimit-limit-requests: レート制限に達するまでに許可される最大リクエスト数
  • x-ratelimit-limit-tokens: レート制限に達するまでに許可される最大トークン数
  • x-ratelimit-remaining-requests: レート制限に達するまでに許可される残りリクエスト数
  • x-ratelimit-remaining-tokens: レート制限に達するまでに許可される残りトークン数
  • x-ratelimit-reset-requests: リクエストに基づくレート制限が初期状態にリセットされるまでの時間
  • x-ratelimit-reset-tokens: トークンに基づくレート制限が初期状態にリセットされるまでの時間

レート制限エラーの軽減

Error Mitigation

レート制限エラーの回避方法

  • 指数バックオフを伴う自動再試行: レート制限エラーが発生した場合、短いスリープを実行し、失敗したリクエストを再試行する。それでも失敗する場合は、スリープ時間を増やし、リクエストが成功するか、最大再試行回数に達するまで繰り返す。これにより、クラッシュやデータ欠落なしにエラーから回復できる。

  • Tenacityライブラリ:
    Tenacityはリトライ処理のためのライブラリ。 tenacity.retryデコレータを使用し、ランダムな指数バックオフをリクエストに追加できる。

from openai import OpenAI
client = OpenAI()

from tenacity import (
  retry,
  stop_after_attempt,
  wait_random_exponential,
)  # for exponential backoff

@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
def completion_with_backoff(**kwargs):
  return client.completions.create(**kwargs)

completion_with_backoff(model="gpt-4o-mini", prompt="Once upon a time,")
  • backoffライブラリ:
    その他のライブラリとして、backoffがあります。backoff.on_exceptionデコレータを使用して、指数バックオフと再試行を追加できる。
import backoff 
import openai
from openai import OpenAI
client = OpenAI()

@backoff.on_exception(backoff.expo, openai.RateLimitError)
def completions_with_backoff(**kwargs):
  return client.completions.create(**kwargs)

completions_with_backoff(model="gpt-4o-mini", prompt="Once upon a time,")
  • 手動バックオフ: サードパーティのライブラリを使用しない場合は、独自にバックオフロジックを実装できる。例:
# imports
import random
import time

import openai
from openai import OpenAI
client = OpenAI()

# define a retry decorator
def retry_with_exponential_backoff(
  func,
  initial_delay: float = 1,
  exponential_base: float = 2,
  jitter: bool = True,
  max_retries: int = 10,
  errors: tuple = (openai.RateLimitError,),
):
  """Retry a function with exponential backoff."""

  def wrapper(*args, **kwargs):
      # Initialize variables
      num_retries = 0
      delay = initial_delay

      # Loop until a successful response or max_retries is hit or an exception is raised
      while True:
          try:
              return func(*args, **kwargs)

          # Retry on specific errors
          except errors as e:
              # Increment retries
              num_retries += 1

              # Check if max retries has been reached
              if num_retries > max_retries:
                  raise Exception(
                      f"Maximum number of retries ({max_retries}) exceeded."
                  )

              # Increment the delay
              delay *= exponential_base * (1 + jitter * random.random())

              # Sleep for the delay
              time.sleep(delay)

          # Raise exceptions for any errors not specified
          except Exception as e:
              raise e

  return wrapper
  
@retry_with_exponential_backoff
def completions_with_backoff(**kwargs):
  return client.completions.create(**kwargs)

このコードのセキュリティや効率性についてはOpenAIが保証しているわけではない。

その他のエラー軽減策

  • max_tokensの調整: max_tokensの値は、予想されるレスポンスサイズにできるだけ近づけて設定する。

  • リクエストのバッチ処理: 即時応答が必要ない場合は、バッチAPIを使用して、多数のリクエストを効率的に実行できる。また、同期応答が必要な場合でも、トークン/分あたりの制限に余裕がある場合は、複数のタスクを1つのリクエストにまとめることで、スループットを向上させることができる。

Discussion