はじめに
watsonx.ai Model Gatewayは、複数のAIモデルを統一的に管理・利用できるサービスです。本記事では、公式SDKを使用してRate Limitを実装する方法を、実践的なコード例とともに解説します。
対象読者
- watsonx.aiを使用している開発者
- AIモデルのAPI利用にコスト管理が必要な方
- Rate Limitの実装方法を学びたい方
Model Gatewayの設定方法については以下の記事を参考にしました。
https://qiita.com/YokeSin/items/3c4a8ce0c9b5b5e9e3ce
Model Gatewayが解決する課題
watsonx.ai Model Gatewayは、以下のような課題を持つ方々に最適です:
- 複数AIプロバイダーの管理: OpenAI、Anthropic、Meta(Llama)など、複数のプロバイダーを統一APIで管理
- コスト管理: Rate Limit機能でリクエスト数・トークン数を制御し、予算超過を防止
- セキュリティ: 中央集権的なアクセス制御とAPIキー管理でセキュリティリスクを低減
特に、開発環境でのコスト制御や部門別のリソース管理に有効です。
watsonx.ai Model Gatewayは2026年3月現在プレビュー版のみでの提供となっており、正式リリース時に機能に変更がある場合がございます。0
環境
本記事で使用した環境は以下の通りです:
システム環境
- OS: macOS Sequoia
- Python: 3.11.11
パッケージバージョン
- ibm-watsonx-ai: 1.5.3
- python-dotenv: 1.0.0
- pandas: 2.1.4
必要なパッケージのインストール
pip install ibm-watsonx-ai python-dotenv
注意: Python 3.8以上が必要です。本記事のコードはPython 3.11.11で動作確認済みです。
バージョン確認方法
# Pythonバージョン確認
python --version
# パッケージバージョン確認
pip show ibm-watsonx-ai
pip show python-dotenv
環境変数の設定
.envファイルを作成し、以下の情報を設定します:
WATSONX_API_KEY=your_api_key_here
WATSONX_URL=https://ca-tor.ml.cloud.ibm.com
WATSONX_PROJECT_ID=your_project_id_here
Rate Limitの基本概念
watsonx.ai Model GatewayのRate Limitはトークンバケットアルゴリズムを使用しています。
主要なパラメータ
- capacity: バケットの最大容量(同時に保持できるトークン数)
- amount: 補充されるトークン数
- duration: トークン補充の間隔(例:1m = 1分)
動作の仕組み
- 初期状態でバケットに
capacity個のトークンが入っている - リクエストごとに1トークンを消費
-
durationごとにamount個のトークンが補充される - トークンが0になるとRate Limitが発動(HTTP 429エラー)
RateLimitsクラスの詳細
RateLimitsクラスは、watsonx.ai Model GatewayのRate Limitを管理するための公式クラスです。
クラスの初期化
from ibm_watsonx_ai import APIClient
from ibm_watsonx_ai.gateway.rate_limits import RateLimits
rate_limits = RateLimits(api_client=client)
主要なメソッド
1. create_for_model()
特定のモデルに対してRate Limitを設定します。
result = rate_limits.create_for_model(
model_id: str,
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
パラメータ:
-
model_id(str): Model GatewayのモデルID -
request(RateLimitSettings, optional): リクエスト数のRate Limit設定 -
token(RateLimitSettings, optional): トークン数のRate Limit設定
戻り値:
- dict: Rate Limitの詳細情報(IDを含む)
使用例:
result = rate_limits.create_for_model(
model_id="your-model-id",
request=RateLimitSettings(amount=5, capacity=5, duration="1m"),
token=RateLimitSettings(amount=50000, capacity=50000, duration="1m")
)
rate_limit_id = result.get('uuid') or result.get('id')
2. create_for_provider()
特定のプロバイダーに対してRate Limitを設定します。
result = rate_limits.create_for_provider(
provider_id: str,
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
3. create_for_tenant()
テナント全体に対してRate Limitを設定します。
result = rate_limits.create_for_tenant(
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
4. list()
登録されているRate Limitの一覧を取得します。
df = rate_limits.list() -> pandas.DataFrame
戻り値:
- pandas.DataFrame: Rate Limitの一覧
使用例:
existing_limits = rate_limits.list()
print(existing_limits)
5. get_details()
Rate Limitの詳細情報を取得します。
details = rate_limits.get_details(
rate_limit_id: str | None = None
) -> dict
パラメータ:
-
rate_limit_id(str, optional): Rate LimitのID。指定しない場合は全てのRate Limitの詳細を取得
6. delete()
Rate Limitを削除します。
status = rate_limits.delete(rate_limit_id: str) -> Literal["SUCCESS"]
パラメータ:
-
rate_limit_id(str): 削除するRate LimitのID
戻り値:
- "SUCCESS": 削除が成功した場合
使用例:
rate_limits.delete("your-rate-limit-id")
7. update_for_model()
モデルのRate Limitを更新します。
result = rate_limits.update_for_model(
rate_limit_id: str,
model_id: str,
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
8. update_for_provider()
プロバイダーのRate Limitを更新します。
result = rate_limits.update_for_provider(
rate_limit_id: str,
provider_id: str,
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
9. update_for_tenant()
テナントのRate Limitを更新します。
result = rate_limits.update_for_tenant(
rate_limit_id: str,
request: RateLimitSettings | None = None,
token: RateLimitSettings | None = None
) -> dict
静的メソッド
get_id()
Rate Limitの詳細情報からIDを取得します。
rate_limit_id = RateLimits.get_id(rate_limit_details: dict) -> str
RateLimitSettingsクラスの詳細
RateLimitSettingsは、Rate Limitの設定を定義するTypedDictクラスです。
パラメータ
from ibm_watsonx_ai.gateway.rate_limits import RateLimitSettings
settings = RateLimitSettings(
amount: int, # 必須
capacity: int, # 必須
duration: str # 必須
)
amount (int)
補充間隔ごとにバケットに追加されるトークンの数。
例:
-
amount=5: 1分ごとに5トークンが補充される(duration="1m"の場合)
capacity (int)
バケットが保持できる最大トークン数。
例:
-
capacity=10: 最大10リクエストまで同時に保持可能
duration (str)
トークン補充の間隔。Go言語のduration形式で指定します。
例:
-
"1m": 1分 -
"30s": 30秒 -
"1h": 1時間 -
"1h30m": 1時間30分
使用例
# 1分あたり5リクエスト
request_limit = RateLimitSettings(
amount=5,
capacity=5,
duration="1m"
)
# 1分あたり50000トークン
token_limit = RateLimitSettings(
amount=50000,
capacity=50000,
duration="1m"
)
# 30秒あたり3リクエスト
custom_limit = RateLimitSettings(
amount=3,
capacity=3,
duration="30s"
)
requestとtokenの違い
- request: リクエスト数の制限(APIコール回数)
- token: トークン数の制限(処理されるトークン数)
通常、両方を設定することで、リクエスト数とトークン数の両方を制御できます。
rate_limits.create_for_model(
model_id=model_id,
request=RateLimitSettings(amount=5, capacity=5, duration="1m"), # 5リクエスト/分
token=RateLimitSettings(amount=50000, capacity=50000, duration="1m") # 50000トークン/分
)
実装例1: シンプルなチャットアプリケーション
Rate Limit付きでLlamaモデルとチャットするアプリケーションです。
スクリプトはIBM Bobを使用して実装しました。
Model GatewayではOpenAIやGoogleなど様々なプロバイダーのAPIを管理することができるのですが、今回はRate Limitの実装にフォーカスするため、あえてwatsonx.aiのモデルを使用しました。
"""
watsonx.ai Model Gateway チャットアプリケーション
Rate Limit付きでLlamaモデルとチャット
"""
import os
import time
from dotenv import load_dotenv
from ibm_watsonx_ai import Credentials, APIClient
from ibm_watsonx_ai.gateway import Gateway
from ibm_watsonx_ai.gateway.rate_limits import RateLimits, RateLimitSettings
load_dotenv()
# 設定
MODEL_NAME = "meta-llama/llama-3-3-70b-instruct"
= 5
def main():
print("\n" + "="*60)
print("watsonx.ai Model Gateway チャットアプリ")
print("="*60)
# 初期化
credentials = Credentials(
api_key=os.getenv("WATSONX_API_KEY"),
url=os.getenv("WATSONX_URL")
)
client = APIClient(credentials=credentials)
client.set.default_project(os.getenv("WATSONX_PROJECT_ID"))
gateway = Gateway(api_client=client)
# モデルIDを取得
models_df = gateway.models.list()
print(f"\n利用可能なモデル:")
print(models_df[["MODEL", "TYPE"]].to_string())
model_row = models_df[models_df['MODEL'] == MODEL_NAME]
if model_row.empty:
print(f"\nエラー: モデル '{MODEL_NAME}' が見つかりません")
return
gateway_model_id = model_row.iloc[0]['ID']
print(f"\n使用モデル: {MODEL_NAME}")
print(f"Rate Limit: {REQUESTS_PER_MINUTE}リクエスト/分")
# 既存のRate Limitを削除
rate_limits = RateLimits(api_client=client)
existing = rate_limits.list()
if not existing.empty:
for _, row in existing.iterrows():
limit_id = row.get('ID') or row.get('id') or row.get('uuid')
if limit_id:
rate_limits.delete(limit_id)
# Rate Limitを設定
result = rate_limits.create_for_model(
model_id=gateway_model_id,
request=RateLimitSettings(
amount=REQUESTS_PER_MINUTE,
capacity=REQUESTS_PER_MINUTE,
duration="1m"
),
token=RateLimitSettings(
amount=50000,
capacity=50000,
duration="1m"
)
)
rate_limit_id = result.get('uuid') or result.get('id')
print(f"✓ Rate Limit設定完了 (ID: {rate_limit_id})")
# Rate Limitが反映されるまで待機
print("\n待機中...")
time.sleep(5)
# チャット実行
print("\n" + "="*60)
print("チャット実行")
print("="*60)
messages = [
{
"role": "system",
"content": "You are a helpful assistant that answers in Japanese."
},
{
"role": "user",
"content": "テストです。短く自己紹介してください。"
}
]
try:
response = gateway.chat.completions.create(
model=MODEL_NAME,
messages=messages
)
answer = response["choices"][0]["message"]["content"]
print(f"\nアシスタント:\n{answer}")
print("\n✓ チャット成功")
except Exception as e:
print(f"\n✗ エラー: {e}")
# クリーンアップ
print("\n" + "="*60)
cleanup = input("Rate Limit設定を削除しますか? (y/N): ")
if cleanup.lower() == 'y':
rate_limits.delete(rate_limit_id)
print("✓ 削除完了")
print("="*60 + "\n")
if __name__ == "__main__":
main()
実行結果
============================================================
watsonx.ai Model Gateway チャットアプリ
============================================================
使用モデル: meta-llama/llama-3-3-70b-instruct
Rate Limit: 5リクエスト/分
✓ Rate Limit設定完了
チャット実行
アシスタント:
こんにちは!私はAIのアシスタントです。質問に答えたり、
情報を提供したりすることができます。
✓ チャット成功
実装例2: Rate Limitテストスクリプト
Rate Limitが正しく動作するかを検証するテストスクリプトです。
こちらもIBM Bobを使って実装しました。
"""
watsonx.ai Model Gateway Rate Limitテストスクリプト
Rate Limit: 2リクエスト/分の設定で、5リクエストを連続送信してテスト
"""
import os
import time
from dotenv import load_dotenv
from ibm_watsonx_ai import Credentials, APIClient
from ibm_watsonx_ai.gateway import Gateway
from ibm_watsonx_ai.gateway.rate_limits import RateLimits, RateLimitSettings
load_dotenv()
# 設定
MODEL_NAME = "meta-llama/llama-3-3-70b-instruct"
REQUESTS_PER_MINUTE = 2
NUM_TEST_REQUESTS = 5
def main():
print("\n" + "="*60)
print("watsonx.ai Model Gateway Rate Limitテスト")
print("="*60)
# 初期化
credentials = Credentials(
api_key=os.getenv("WATSONX_API_KEY"),
url=os.getenv("WATSONX_URL")
)
client = APIClient(credentials=credentials)
client.set.default_project(os.getenv("WATSONX_PROJECT_ID"))
gateway = Gateway(api_client=client)
# モデルIDを取得
models_df = gateway.models.list()
model_row = models_df[models_df['MODEL'] == MODEL_NAME]
if model_row.empty:
print(f"エラー: モデル '{MODEL_NAME}' が見つかりません")
return
gateway_model_id = model_row.iloc[0]['ID']
print(f"\n使用モデル: {MODEL_NAME}")
print(f"Rate Limit: {REQUESTS_PER_MINUTE}リクエスト/分")
# 既存のRate Limitを削除
rate_limits = RateLimits(api_client=client)
existing = rate_limits.list()
if not existing.empty:
for _, row in existing.iterrows():
limit_id = row.get('ID') or row.get('id') or row.get('uuid')
if limit_id:
rate_limits.delete(limit_id)
# Rate Limitを設定
result = rate_limits.create_for_model(
model_id=gateway_model_id,
request=RateLimitSettings(
amount=REQUESTS_PER_MINUTE,
capacity=REQUESTS_PER_MINUTE,
duration="1m"
),
token=RateLimitSettings(
amount=50000,
capacity=50000,
duration="1m"
)
)
rate_limit_id = result.get('uuid') or result.get('id')
print(f"✓ Rate Limit設定完了 (ID: {rate_limit_id})")
# Rate Limitが反映されるまで待機
print("\n待機中...")
time.sleep(5)
# テスト実行
print(f"\n{NUM_TEST_REQUESTS}リクエストを送信:")
print("-" * 60)
success = 0
rate_limited = 0
for i in range(NUM_TEST_REQUESTS):
try:
response = gateway.chat.completions.create(
model=MODEL_NAME,
messages=[{"role": "user", "content": "1+1は?"}],
max_tokens=50
)
answer = response["choices"][0]["message"]["content"]
print(f"{i+1}. ✓ 成功 - {answer[:30]}...")
success += 1
except Exception as e:
if "429" in str(e):
print(f"{i+1}. ⚠ Rate Limit到達")
rate_limited += 1
else:
print(f"{i+1}. ✗ エラー: {str(e)[:50]}")
time.sleep(0.1)
# 結果
print("-" * 60)
print(f"\n結果: 成功 {success}/{NUM_TEST_REQUESTS}, Rate Limit {rate_limited}/{NUM_TEST_REQUESTS}")
if rate_limited > 0:
print("✓ Rate Limitが正常に動作しています")
else:
print("⚠ Rate Limitが検出されませんでした")
# クリーンアップ
cleanup = input("\nRate Limit設定を削除しますか? (y/N): ")
if cleanup.lower() == 'y':
rate_limits.delete(rate_limit_id)
print("✓ 削除完了")
print("="*60 + "\n")
if __name__ == "__main__":
main()
テスト結果
============================================================
watsonx.ai Model Gateway Rate Limitテスト
============================================================
使用モデル: meta-llama/llama-3-3-70b-instruct
Rate Limit: 2リクエスト/分
5リクエストを送信:
------------------------------------------------------------
1. ✓ 成功 - 答えは2です。
2. ✓ 成功 - 答えは2です。
3. ⚠ Rate Limit到達
4. ⚠ Rate Limit到達
5. ⚠ Rate Limit到達
------------------------------------------------------------
結果: 成功 2/5, Rate Limit 3/5
✓ Rate Limitが正常に動作しています
Rate Limitの動作解析
トークン補充のタイミング
テストを長時間実行すると、以下のような結果になることがあります:
1. ✓ 成功
2. ✓ 成功
3. ⚠ Rate Limit到達
...(約30秒後)
11. ✓ 成功 ← トークンが1つ補充された
12. ⚠ Rate Limit到達
これは、duration="1m"の設定により、1分間でamount=2個のトークンが補充されるためです。
ベストプラクティス
1. 適切なRate Limit値の設定
# 開発環境: 緩めの設定
REQUESTS_PER_MINUTE = 10
# 本番環境: 厳しめの設定
REQUESTS_PER_MINUTE = 5
2. エラーハンドリング
try:
response = gateway.chat.completions.create(...)
except Exception as e:
if "429" in str(e):
print("Rate Limit到達。しばらく待ってから再試行してください。")
time.sleep(60) # 1分待機
else:
print(f"エラー: {e}")
3. Rate Limitの監視
# 既存のRate Limitを確認
rate_limits = RateLimits(api_client=client)
existing = rate_limits.list()
print(existing)
4. クリーンアップ
テスト後は必ずRate Limit設定を削除しましょう:
rate_limits.delete(rate_limit_id)
まとめ
本記事では、watsonx.ai Model GatewayでRate Limitを実装する方法を解説しました。
重要なポイント
- トークンバケットアルゴリズムを理解する
- capacityとamountを適切に設定する
- テストで動作を確認する