複数LLM運用における混乱の実態
前四半期、私たちのインフラは混沌を極めていました。FastAPIで構築されたドキュメントパーサー、Node.jsの通知エンジン、社内向けSlackボットという3つのコアサービスに加え、6人のエンジニアがプロンプトパイプラインの検証を行っていました。すべてのシステムが個別の.envファイルにOpenAIやAnthropicの生のAPIキーを直接保持しており、誰がどれだけのコストを消費しているのか誰も把握できていない状況でした。
そしてトラブルが発生しました。あるエンジニアが実行した評価スクリプトがOpenAIに対して毎分3,200リクエストを送信。これによりTier 3のレートリミット(1,000 RPM)を大幅に超過し、本番環境のトラフィックが20分間にわたって枯渇・停止してしまいました。さらにその3週間後、監視されていなかったテストスクリプトが週末の間にClaude OpusのAPIコールで1,400ドルを消費。月間のLLM支出は、原因の特定もままならないまま45%も急増してしまったのです。
APIキーの散在とトラフィックガバナンスの欠如は、システム停止を招く直接的な原因になります。また、複数のリポジトリにプロバイダーのシークレットをハードコードすることは、セキュリティ面でも大きな懸念材料です。この問題を解決するため、私たちはアプリケーションと上流のAIプロバイダーの間にリバースプロキシを配置しました。それが「一元管理AIゲートウェイ」です。
なぜLiteLLMを採用したのか?
独自のLLMプロキシをゼロから開発・保守するには数週間以上の工数がかかります。LiteLLMなら、導入後すぐにこの課題を解決できます。軽量なOpenAI互換プロキシサーバーとして動作するため、OpenAI SDK向けに記述されたPythonスクリプトやLangChainエージェント、cURLリクエストは、ベースURL(base URL)を差し替えるだけですぐにそのまま機能します。
この構成により、以下のメリットが得られます:
- 統一されたAPIインターフェース:Gemini 1.5 Pro、Claude 3.5 Sonnet、あるいはセルフホストのvLLMインスタンスへのリクエストも、すべて標準の
/v1/chat/completionsペイロードで受け付けることができます。 - 仮想テナントキー:各サービスにはマスターのプロバイダーシークレットではなく、スコープを制限した仮想トークン(
sk-プレフィックス付き)を払い出します。トークンごとに特定のモデル、毎分リクエスト数(RPM)制限、利用可能予算(ドル上限)を紐付け可能です。 - スマートなロードバランシングとフォールバック:複数の組織キーやリージョン間でトラフィックを均等に分散します。もしGPT-4oが429や503エラーを返した場合でも、プロキシがミリ秒単位で透過的にClaude 3.5 Sonnetへとペイロードを再ルーティングします。
- 自動コスト上限設定:PostgreSQLがトークン消費量をリアルタイムでトラッキングします。例えばインターンの仮想キーが月間50ドルの上限に達した時点で、LiteLLMがそれ以降のリクエストを自動的に拒否します。
ステップバイステップ:DockerによるLiteLLMのデプロイ
ここでは、ユーザーキーの追跡、支出ログの記録、ヘルスチェックを行うためのPostgreSQLを組み合わせた、本番運用可能なLiteLLMプロキシを構築します。
ステップ 1:プロジェクトディレクトリとシークレットの準備
ホストマシン上に設定ファイル用の専用ディレクトリを作成します:
mkdir -p /opt/ai-gateway && cd /opt/ai-gateway
touch docker-compose.yml litellm_config.yaml .env
chmod 600 .env
データベースのパスワードと各プロバイダーのマスターAPIキーを.envに追加します:
# .env
POSTGRES_USER=litellm
POSTGRES_PASSWORD=Secr3t_Postgres_P@ss!2024
POSTGRES_DB=litellm_proxy
LITELLM_MASTER_KEY=sk-admin-master-prod-9988
OPENAI_API_KEY_PRIMARY=sk-proj-primaryKey-12345
OPENAI_API_KEY_SECONDARY=sk-proj-backupKey-67890
ANTHROPIC_API_KEY=sk-ant-liveKey-abcdef
ステップ 2:モデルルーティングと自動フェイルオーバーの設定
litellm_config.yaml内にルーティングルールを定義します。以下の設定では、GPT-4oのリクエストを2つのOpenAIアカウント(それぞれ1,000 RPM)へ均等に分散し、両方でエラーが発生した場合はAnthropic Claude 3.5 Sonnetへとフォールバックします。
model_list:
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY_PRIMARY
rpm: 1000
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY_SECONDARY
rpm: 1000
- model_name: gpt-4o-fallback
litellm_params:
model: anthropic/claude-3-5-sonnet-20241022
api_key: os.environ/ANTHROPIC_API_KEY
router_settings:
routing_strategy: usage-based-routing-v2
fallbacks:
- gpt-4o: ["gpt-4o-fallback"]
num_retries: 2
retry_after: 1
timeout: 30
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
ステップ 3:Docker Composeでサービスを定義する
PostgreSQL 16とLiteLLMプロキシのコンテナをdocker-compose.ymlに記述します。ヘルスチェックにより、データベースソケットが接続を受け入れ可能になるまでLiteLLMが起動しないように制御します。
version: '3.8'
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 5
litellm:
image: ghcr.io/berriai/litellm:main-latest
restart: unless-stopped
ports:
- "4000:4000"
environment:
DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
OPENAI_API_KEY_PRIMARY: ${OPENAI_API_KEY_PRIMARY}
OPENAI_API_KEY_SECONDARY: ${OPENAI_API_KEY_SECONDARY}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
volumes:
- ./litellm_config.yaml:/app/config.yaml
command: ["--config", "/app/config.yaml", "--port", "4000", "--detailed_debug"]
depends_on:
db:
condition: service_healthy
volumes:
pgdata:
コンテナをバックグラウンド(デタッチモード)で起動します:
docker compose up -d
docker compose logs -f litellm
ログにLiteLLM: Proxy running on http://0.0.0.0:4000と表示されていることを確認し、データベースのマイグレーションが正常に完了したことを確かめます。
ステップ 4:スコープと予算上限付きの仮想キーを発行する
マスタープロバイダーキーを直接共有する必要はもうありません。LiteLLMの管理APIを使用して、チームや環境に応じたスコープ付き仮想トークンを発行します。以下は、月間50ドルの厳格な上限を設定してステージングチーム用のキーを作成する例です:
curl -s -X POST http://localhost:4000/key/generate \
-H "Authorization: Bearer sk-admin-master-prod-9988" \
-H "Content-Type: application/json" \
-d '{
"models": ["gpt-4o"],
"max_budget": 50.0,
"budget_duration": "30d",
"user_id": "team-analytics-staging",
"metadata": {"environment": "staging", "cost_center": "cc-402"}
}' | jq
LiteLLMはsk-litellm-1234...という形式の仮想キーを返します。プロキシはレスポンスごとにトークン消費量を記録します。このキーの利用額が50.00ドルに達した瞬間に、LiteLLMはHTTP 400 BudgetExceededErrorを返してアクセスを遮断します。本番環境用のキーには一切影響を与えません。
ステップ 5:クライアントコードをゲートウェイ経由に切り替える
既存のアプリケーションへの導入は2分もかかりません。base_urlをゲートウェイのエンドポイントに変更し、発行した仮想トークンを指定するだけです:
from openai import OpenAI
# クライアントの接続先をLiteLLMリバースプロキシに向ける
client = OpenAI(
base_url="http://localhost:4000/v1",
api_key="sk-litellm-analytics-key-here"
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "あなたはサイト信頼性エンジニア(SRE)です。"},
{"role": "user", "content": "TCPコネクションプールがスレッドの枯渇(thread starvation)をどのように防ぐかを説明してください。"}
]
)
print(response.choices[0].message.content)
私たちのマイクロサービス群はこのゲートウェイ構成のもとで半年間トラブルなく稼働しています。11月に発生したOpenAIの大規模なレイテンシ急増時(p99レイテンシが14秒を超過)にも、LiteLLMが自動的に800件のアクティブなリクエストをClaude 3.5 Sonnetへと切り替えました。その結果、エンドユーザー側でコネクション切断が発生することは一切ありませんでした。
本番運用に向けた次のステップ
AIゲートウェイを導入することで、シークレットの散在と制御不能なコスト増加に終止符を打つことができます。ベンダーのプライマリAPIキーはプロキシホスト内に安全に隔離され、ロードバランシングは本来あるべきネットワーク層で処理されます。
本番環境へ移行する前に、TLS終端やレートリミットを担うTraefikやCaddyなどのリバースプロキシをポート4000の前段に配置してください。最後に、LiteLLMに標準搭載されている/metricsエンドポイントを有効化し、レイテンシのヒストグラムやトークン消費速度、エラー率をPrometheusやGrafanaのダッシュボードへ直接パイプライン送信できるように設定しましょう。

