- 概要
- 1. インストールと最初の呼び出し
- 2. 複数プロバイダーを同じコードで呼ぶ
- 3. LiteLLM で OpenRouter を使う
- 4. ストリーミングと非同期
- 5. Router — ロードバランシングとフォールバック
- 6. Proxy Server (AI ゲートウェイ)
- 7. SDK と Proxy の使い分け、そして実践のヒント
- 参考資料
概要
LLM アプリを作り続けると、すぐ同じ壁に当たる。プロバイダーごとに SDK が違い、リクエストのパラメータが違い、レスポンスの形が違う。OpenAI 向けのコードで始めて Anthropic に移そうとすると、クライアントの初期化からメッセージのパースまで全部を書き直す羽目になる。LiteLLM はまさにこの摩擦を取り除く。
LiteLLM(BerriAI 製)は二つの部品からなる。一つは Python SDK、もう一つは Proxy Server(AI ゲートウェイ)だ。両者には共通の目標がある。100 以上の LLM API を OpenAI のリクエスト/レスポンス形式 で呼び出すことだ。その均一な表面の上に、コスト追跡、リトライ、ロードバランシング、フォールバック、ガードレール、ロギングといった運用機能が乗る。
この記事は、すばやく生産性を得るための 実践クイックスタート だ。インストールして最初の呼び出しを投げ、プロバイダーを差し替え、ストリーミングと Router、Proxy までを最短経路でたどる。LiteLLM の内部構造や本番運用の詳細設定を深く扱った LiteLLM 完全ガイド がすでにこのブログにあるので、仕組みや本番の細部が必要ならそちらを併せて読むとよい。この記事は意図的に軽く保つ。
まず一つの見方を先に固めておこう。LiteLLM は「もう一つのプロバイダー」ではなく、複数のプロバイダーの上に置く 単一の統合レイヤー だ。そしてその下には、OpenRouter のようにさらに数百のモデルを束ねるルーターを入れることもできる。つまり一つの LiteLLM 構成で、直接つないだプロバイダーと OpenRouter 経由のプロバイダーの両方を、同じコードで呼べる。
1. インストールと最初の呼び出し
インストールは一行だ。pip でも uv でも違いはない。
pip install litellm
# uv を使う場合
uv add litellm
最小の呼び出しは次のとおり。completion 関数一つに model と messages を渡すだけで、レスポンスは OpenAI の形そのままで返る。
from litellm import completion
import os
os.environ["OPENAI_API_KEY"] = "sk-..."
resp = completion(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
注目したいのはアクセスの仕方だ。resp.choices[0].message.content は、OpenAI SDK を使ったことがあれば見慣れたあの構造だ。どのプロバイダーを呼んでもこの形が保たれるので、レスポンスを扱うコードは一度だけ書けばよい。
SDK の段階でも、簡単な安定性オプションをそのまま渡せる。num_retries でリトライ回数を、timeout で応答の待ち時間を指定すれば、一時的なエラーに少し強くなる。これらのオプションは Router なしの単一呼び出しでも動く。
from litellm import completion
resp = completion(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
num_retries=2,
timeout=30,
)
print(resp.choices[0].message.content)
2. 複数プロバイダーを同じコードで呼ぶ
プロバイダーを切り替える方法は二段階だけだ。model 文字列の 接頭辞(prefix) を変え、そのプロバイダーの 環境変数 を設定する。それで全部だ。メッセージの構造も、レスポンスのパースもそのままでよい。
| プロバイダー | model 接頭辞の例 | 環境変数 |
|---|---|---|
| OpenAI | openai/gpt-4o | OPENAI_API_KEY |
| Anthropic | anthropic/claude-3-5-sonnet-20241022 | ANTHROPIC_API_KEY |
| Gemini (AI Studio) | gemini/gemini-1.5-pro | GEMINI_API_KEY |
| Vertex AI | vertex_ai/gemini-1.5-pro | GCP 認証情報 |
| AWS Bedrock | bedrock/... | AWS 認証情報 |
| Azure OpenAI | azure/... | Azure 設定 |
| Ollama (ローカル) | ollama/llama3 | なし(ローカル実行) |
たとえば Anthropic に移すとこうなる。
from litellm import completion
import os
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..."
resp = completion(
model="anthropic/claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
ローカルモデルで試したいなら、Ollama を立ち上げて接頭辞を ollama/ に変えるだけだ。API キーもいらない。
from litellm import completion
resp = completion(
model="ollama/llama3",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
この構造のおかげで、「開発はローカル Ollama、ステージングは Gemini、本番は Bedrock」といった切り替えを、コード変更なしに環境変数とモデル文字列だけで扱える。
3. LiteLLM で OpenRouter を使う
ここがこの記事でとくに強調したい組み合わせだ。OpenRouter は、それ自体で 300 以上のモデルを一つの API に束ねるサービスだ。LiteLLM をその上に重ねると、OpenRouter の広いモデルカバレッジに、LiteLLM の均一なインターフェースと運用機能(リトライ、フォールバック、コスト追跡)を合わせて得られる。
方法はこれまでと同じパターンだ。OPENROUTER_API_KEY を設定し、モデル文字列を openrouter/ で始めて、その後ろに OpenRouter のモデル id を付ける。
from litellm import completion
import os
os.environ["OPENROUTER_API_KEY"] = "sk-or-..."
resp = completion(
model="openrouter/openai/gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
接頭辞を分解すると分かりやすい。先頭の openrouter/ は LiteLLM に「OpenRouter 経由で送れ」と指示し、後ろの openai/gpt-4o は OpenRouter が認識するモデル id だ。つまり openrouter/ の後ろには、OpenRouter が対応するどのモデル id でもそのまま置ける。
この組み合わせの実用的な利点は明快だ。直接契約したいくつかのプロバイダーは LiteLLM に直結し、それ以外の実験的で多様なモデルは OpenRouter 経由でつなぎつつ、アプリのコードは全部同じ completion 呼び出しに保てる。モデルを増やす際の統合コストが実質ゼロに近づく。
4. ストリーミングと非同期
トークンをリアルタイムで流すには、stream=True を足して戻り値を反復するだけだ。デルタ(delta)から断片を取り出してつなげる形になる。
from litellm import completion
resp = completion(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Write a haiku about the sea"}],
stream=True,
)
for chunk in resp:
print(chunk.choices[0].delta.content or "", end="")
delta.content が None で届くチャンクもあるので、or "" で守るのが慣例だ。こうすれば空の断片で例外が起きない。
非同期コードでは acompletion を使う。シグネチャは completion と同じで、await を付けるだけでよい。
import asyncio
from litellm import acompletion
async def main():
resp = await acompletion(
model="anthropic/claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
asyncio.run(main())
ストリーミングと非同期は組み合わせて使える。FastAPI のような非同期 Web フレームワークでトークンを Server-Sent Events として流すとき、この二つを一緒に使うことになる。
5. Router — ロードバランシングとフォールバック
呼び出し量が増えて安定性が重要になると、SDK の Router クラスが登場する。Router は複数のデプロイ(deployment)を一つのエイリアス(alias)にまとめ、リクエストを分散し、失敗を自動で吸収する。
まず model_list を定義する。各エントリは外向きの名前 model_name と、実際の呼び出しパラメータ litellm_params を持つ。同じ model_name を複数のデプロイに付けると、Router はそのエイリアス宛のリクエストを複数のデプロイに振り分ける。
from litellm import Router
model_list = [
{
"model_name": "my-gpt",
"litellm_params": {
"model": "openai/gpt-4o",
"api_key": "sk-...",
},
},
{
"model_name": "my-gpt",
"litellm_params": {
"model": "azure/gpt-4o-deployment",
"api_key": "...",
"api_base": "https://your-resource.openai.azure.com",
},
},
]
router = Router(model_list=model_list)
resp = router.completion(
model="my-gpt",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
Router の本当の価値はフォールバック(fallback)にある。失敗の種類に応じて三つのフォールバックを分けて設定できる。
| フォールバックの種類 | トリガー | 備考 |
|---|---|---|
fallbacks | 429/500 系のエラー | 別のモデルへ回す |
context_window_fallbacks | コンテキスト超過 | enable_pre_call_checks=True が必要 |
content_policy_fallbacks | コンテンツポリシー拒否 | 別のモデルへ迂回 |
三つをまとめて設定した例は次のとおり。コンテキストのフォールバックを使うには事前チェックを有効にする必要がある、という点だけ注意すればよい。
from litellm import Router
router = Router(
model_list=model_list,
fallbacks=[{"my-gpt": ["claude"]}],
context_window_fallbacks=[{"my-gpt": ["claude-long"]}],
content_policy_fallbacks=[{"my-gpt": ["safe-model"]}],
enable_pre_call_checks=True,
)
このほかにも Router はリトライ(retries)、クールダウン(cooldowns)、タイムアウト(timeouts)を合わせて管理する。あるデプロイが失敗し続けると、しばらくクールダウンのリストに入れてトラフィックを送らず、一定時間後に復帰させる、という具合だ。アプリのコードは相変わらず router.completion(model="my-gpt", ...) の一行でよい。
6. Proxy Server (AI ゲートウェイ)
複数のチームやアプリが一つのゲートウェイを共有する必要があるなら、Proxy Server が答えだ。Proxy は別プロセスとして立ち上がり、どんな OpenAI 互換クライアントも受け付けるゲートウェイとして働く。
まず config.yaml に model_list を書く。API キーは os.environ/ で環境変数を参照させるのが安全だ。
model_list:
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
- model_name: claude
litellm_params:
model: anthropic/claude-3-5-sonnet-20241022
api_key: os.environ/ANTHROPIC_API_KEY
次に設定ファイルを指定して proxy を起動する。デフォルトでは http://0.0.0.0:4000 で待ち受ける。
litellm --config config.yaml
あとは、どんな OpenAI 互換クライアントでも base URL を proxy に向け、ダミー/ベースのキーを渡せばそのまま動く。実際のプロバイダーキーは proxy が config から保持しているので、クライアントには露出しない。
from openai import OpenAI
client = OpenAI(
base_url="http://0.0.0.0:4000",
api_key="anything", # proxy が管理する仮想キー/ダミーキー
)
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
Proxy レイヤーが加えるものは、単なるプロキシを超える。チームごとの仮想キー(virtual keys)の発行、予算(budgets)の上限、コスト追跡のダッシュボード、リクエストのロギングが、すべてゲートウェイで中央管理される。アプリは実際のプロバイダーキーを知らなくてよく、運用チームは誰がどれだけ使ったかを一箇所で見られる。
7. SDK と Proxy の使い分け、そして実践のヒント
どちらを使うかは規模で分かれる。
- SDK を使うとき: 単一のアプリやスクリプト。ライブラリを import してコードの中で直接呼べばよく、別のインフラは要らない。個人プロジェクト、バッチ処理、単一サービスに向く。
- Proxy を使うとき: 複数のチームやアプリが一つのゲートウェイを共有する必要があるとき。中央集約のキー管理、予算、コストダッシュボードが要るなら Proxy が正解だ。組織規模の LLM プラットフォームを作るときは自然とこの方向に向かう。
実践で役立つ点をいくつかまとめる。
- レスポンスコードは一度だけ: どのプロバイダーを呼んでも
resp.choices[0].message.contentの形が保たれるので、レスポンスのパースをプロバイダーごとに書き直す必要はない。 - キーは環境変数で: SDK でも proxy の config でも、キーをコードに埋め込まず環境変数から注入する。proxy では
os.environ/参照を使う。 - モデル文字列がスイッチ: プロバイダーの切り替えは接頭辞の差し替えと環境変数の設定、この二つで全部だ。デプロイ環境ごとにモデル文字列だけを変える戦略がよく効く。
- OpenRouter を隣に置く: よく使うプロバイダーは直結し、実験的で多様なモデルは
openrouter/接頭辞で付ければ、統合コストなしに選択肢を広げられる。 - 小さく始めて育てる: 最初は SDK で始め、チームが増えて中央管理が必要になった時点で、同じ
model_listの考え方をそのまま Proxy の config に移せばよい。
まとめると、LiteLLM はプロバイダーの分断をコードの外へ押し出す。最初の呼び出しは数行で済み、必要になった瞬間に Router で安定性を、Proxy で組織レベルの統制を足せる。より深い内部動作と本番の詳細設定は、LiteLLM 完全ガイド で続けて読むとよい。
参考資料
현재 단락 (1/164)
LLM アプリを作り続けると、すぐ同じ壁に当たる。プロバイダーごとに SDK が違い、リクエストのパラメータが違い、レスポンスの形が違う。OpenAI 向けのコードで始めて Anthropic に移そ...