TT Lab
はじめる
学ぶ 学習パス コース

LLMサービング

レートリミットとトークン会計のゲートウェイを作る

TT Labで続きを見る

目標

LLMゲートウェイを自分で作って、リクエスト単位ではなく、トークン単位の制限と会計を実装し、テナントごとのバジェット制御まで完成させます。

なぜ重要なのか

毎分60リクエストの制限は、LLMの前では、ほとんど無意味です。あるテナントが、毎回のリクエストに200,000トークンを入れると、リクエスト数は制限の範囲内ですが、コストとGPUの占有は、他のテナントの数百倍で、そのリクエストがKVキャッシュを独占して、他のリクエストをプリエンプションさせます。そのため、LLMゲートウェイの第1の設計原則は、トークンを数えることです。このラボで特に厄介なのが、ステップ4とステップ8です。出力トークンは、生成が終わって初めてわかるので、バジェットの検査を完了後にだけ行うと、同時に入ってきたリクエストが、すべて予算の範囲内だと判断して通過したあと、いっしょに超過します。max_tokensを上限としてあらかじめ予約し、完了後に精算する方式が、この問題を解きます。実際のサービスで、コストの事故が起きる地点が、まさにここです。

ステップ

  1. /root/gw/gateway.pyを127.0.0.1:8171で起動して、POST /v1/generateを8170のバックエンドに転送してください。応答がバックエンドと同一である必要があります。テナントt1のAPIキーを/root/gw/key.txtに、t2のキーを/root/gw/key2.txtに、それぞれ1行で保存しておいてください。以降のステップの採点が、この2つのファイルを使います。
  2. X-API-Keyヘッダーで、テナントを識別してください。キーとテナントのマッピングは、/opt/fixtures/llms/apikeys.jsonにあります。キーがないか、未知のキーなら、401です。
  3. テナントごとの毎分のリクエスト制限10を適用してください。11番目のリクエストが429である必要があります。/root/gw/rpm.txtに、allowed=10 rejected=1を書いてください。
  4. GET /v1/usageが、{"tenant":"...","prompt_tokens":<n>,"completion_tokens":<n>,"requests":<n>}を返すようにしてください。2つのトークンの値が、どちらも0より大きい必要があります。
  5. 毎分のトークン制限2000を、トークンバケットで適用してください。上限を超えるリクエストが、429を受け取る必要があります。/root/gw/tpm.txtに、limit=2000 consumed=<n> rejected=<n>を書き、rejectedは1以上である必要があります。
  6. すべての429のレスポンスに、Retry-Afterヘッダーが、1以上60以下の整数で付く必要があります。/root/gw/retry.txtに、retry_after=<정수>(プレースホルダーは整数です)を書いてください。
  7. /opt/fixtures/llms/pricing.jsonのモデルごとの単価でコストを計算して、/root/gw/cost.csvに、tenant,model,prompt_tokens,completion_tokens,cost_usdのヘッダーと2行以上を書いてください。
  8. テナントt1の日次バジェットを0.01ドルにして、超過したら402を返してください。予約はmax_tokensを基準にあらかじめ確保し、完了後に実際の使用量で精算します。/root/gw/budget.txtに、budget_usd=0.01 spent_usd=<수> blocked=true(プレースホルダーは数値です)を書いてください。

参考

ゲートウェイでバックエンドをプロキシする

/root/gw/gateway.pyを127.0.0.1:8171で起動して、POST /v1/generateを8170のバックエンドに転送してください。応答がバックエンドと同一である必要があります。テナントt1のAPIキーを/root/gw/key.txtに、t2のキーを/root/gw/key2.txtに、それぞれ1行で保存しておいてください。以降のステップの採点が、この2つのファイルを使います。

まず、そのまま通すプロキシを作って、機能を1つずつ載せていきます。

APIキーでテナントを識別する

X-API-Keyヘッダーで、テナントを識別してください。キーとテナントのマッピングは、/opt/fixtures/llms/apikeys.jsonにあります。キーがないか、未知のキーなら、401です。

キーがないか、未知のキーなら、401です。キーをテナントにマッピングしておいてください。

毎分のリクエスト制限をかける

テナントごとの毎分のリクエスト制限10を適用してください。11番目のリクエストが429である必要があります。/root/gw/rpm.txtに、allowed=10 rejected=1を書いてください。

固定ウィンドウが、最も単純です。超過したら、どのステータスコードになるかを考えてみてください。

入力・出力のトークンを会計する

GET /v1/usageが、{"tenant":"...","prompt_tokens":<n>,"completion_tokens":<n>,"requests":<n>}を返すようにしてください。2つのトークンの値が、どちらも0より大きい必要があります。

2つを別々に数える必要があります。単価が違うからです。

毎分のトークン制限をかける

毎分のトークン制限2000を、トークンバケットで適用してください。上限を超えるリクエストが、429を受け取る必要があります。/root/gw/tpm.txtに、limit=2000 consumed=<n> rejected=<n>を書き、rejectedは1以上である必要があります。

トークンバケットがよく合います。静かだったテナントのバーストを許容しながら、長期の平均を守ります。

429に再試行の時点を載せる

すべての429のレスポンスに、Retry-Afterヘッダーが、1以上60以下の整数で付く必要があります。/root/gw/retry.txtに、retry_after=<정수>(プレースホルダーは整数です)を書いてください。

残り時間を計算して、整数の秒で渡します。これがないと、クライアントたちが波を作ります。

モデルごとの単価でコストを計算する

/opt/fixtures/llms/pricing.jsonのモデルごとの単価でコストを計算して、/root/gw/cost.csvに、tenant,model,prompt_tokens,completion_tokens,cost_usdのヘッダーと2行以上を書いてください。

出力のほうが入力より高いのが普通です。単価表は、フィクスチャにあります。

バジェット超過を遮断して使用量を報告する

テナントt1の日次バジェットを0.01ドルにして、超過したら402を返してください。予約はmax_tokensを基準にあらかじめ確保し、完了後に実際の使用量で精算します。/root/gw/budget.txtに、budget_usd=0.01 spent_usd=<수> blocked=true(プレースホルダーは数値です)を書いてください。

遮断の代わりに、より安いモデルにダウングレードする選択肢もあります。ここでは、遮断で実装します。