LiteLLMで複数のLLMプロバイダーを統一インターフェースで呼び出してみる
はじめに
データ事業本部のkobayashiです。
最近のAI開発では、OpenAI・Claude・Geminiなど複数のLLMプロバイダーを用途に応じて使い分けるケースが増えています。しかしプロバイダーごとにSDKやAPIのインターフェースが異なるため、切り替えのたびにコードを書き換える必要があり、開発・運用の負担になりがちです。
今回はそのような課題を解決するLiteLLMを試してみたのでその内容をまとめます。
LiteLLMとは
LiteLLMは、100以上のLLMプロバイダーをOpenAI互換のインターフェースで統一的に呼び出せるPythonライブラリです。OpenAIのchat.completions.create()と同じ形式で、Claude・Gemini・OpenAIなどのモデルをシームレスに切り替えることができます。
主な特徴としては以下になります。
- 統一インターフェース:
completion()関数にモデル名を渡すだけで、プロバイダーに関係なく同じコードで呼び出し可能 - 100以上のプロバイダー対応: OpenAI、Anthropic、Google(Gemini)、AWS Bedrock、Azure、Cohere、Mistralなど
- ローカルモデル対応: Ollamaで動作するローカルモデルも同じインターフェースで利用可能
- 複数エンドポイント対応: チャット補完だけでなく、Embeddings、画像生成、音声文字起こしなども統一インターフェースで利用可能
- ストリーミング対応: すべてのプロバイダーで統一的にストリーミングレスポンスを利用可能
- 非同期対応:
acompletion()による非同期呼び出しをサポート - フォールバック・リトライ: モデルが失敗した場合に別モデルへ自動フォールバックする機能
- コスト追跡: プロバイダーごとのトークン使用量とコストを追跡可能
どんな時に役立つか
LiteLLMは以下のようなシーンで特に役立ちます。
1. 複数モデルの比較・評価
新しいモデルがリリースされるたびに、既存モデルとの比較評価を行いたいケースがあります。LiteLLMを使えばモデル名を変えるだけで同じプロンプトを異なるモデルに投げられるため、比較が非常に簡単になります。
2. コスト最適化
タスクの難易度に応じてモデルを使い分けることでコストを最適化できます。例えば、単純な分類タスクには安価なモデル、複雑な推論にはハイエンドモデルというように、動的にルーティングすることが可能です。
3. 可用性の向上
特定のプロバイダーがダウンした場合や、レートリミットに達した場合に、自動的に別のプロバイダーへフォールバックさせることで、サービスの可用性を高められます。
4. プロバイダーロックインの回避
特定プロバイダーのSDKに依存したコードを書いてしまうと、将来の切り替えが困難になります。LiteLLMの統一インターフェースを使うことで、プロバイダーの切り替えをモデル名の変更だけで実現でき、ロックインを回避できます。
LiteLLMを使ってみる
環境
今回使用した環境は以下の通りです。
Python 3.13
litellm 1.83.14
インストール
pipでインストールします。
$ uv pip install litellm
APIキーの設定
各プロバイダーのAPIキーを環境変数に設定します。
$ export OPENAI_API_KEY="sk-..."
$ export ANTHROPIC_API_KEY="sk-ant-..."
$ export GEMINI_API_KEY="..."
基本的な使い方:モデル名を変えるだけで切り替え
LiteLLMの最大の魅力は、completion()関数のモデル名を変えるだけでプロバイダーを切り替えられる点です。
from litellm import completion
messages = [{"role": "user", "content": "Pythonでフィボナッチ数列を生成する関数を書いてください"}]
# OpenAI
response_openai = completion(model="openai/gpt-5.5", messages=messages)
print("=== OpenAI (GPT-5.5) ===")
print(response_openai.choices[0].message.content)
# Anthropic Claude
response_claude = completion(model="anthropic/claude-sonnet-4-6", messages=messages)
print("\n=== Anthropic (Claude Sonnet) ===")
print(response_claude.choices[0].message.content)
# Google Gemini
response_gemini = completion(model="gemini/gemini-2.5-flash", messages=messages)
print("\n=== Google (Gemini 2.5 Flash) ===")
print(response_gemini.choices[0].message.content)
上記のように、completion()関数のmodelパラメータを変更するだけで異なるプロバイダーのモデルを呼び出せます。レスポンスの形式もOpenAI互換で統一されているため、後続の処理を変更する必要がありません。
Ollamaでローカルモデルを使う
LiteLLMはクラウドAPIだけでなく、Ollamaで動作するローカルモデルにも対応しています。APIキー不要で完全にローカル環境で実行できるため、データをクラウドに送りたくない場合やオフライン環境での利用に適しています。
Ollamaが起動している状態で、ollama/ プレフィックスにモデル名を指定し、api_baseにOllamaのURLを渡します。
from litellm import completion
messages = [{"role": "user", "content": "Pythonでフィボナッチ数列を生成する関数を書いてください"}]
# Ollama(ローカルモデル)
# 事前に ollama pull qwen3:8b でモデルをダウンロードしておく
response = completion(
model="ollama/qwen3:8b",
messages=messages,
api_base="http://localhost:11434",
)
print("=== Ollama (qwen3:8b) ===")
print(response.choices[0].message.content)
モデル名はollama pullでダウンロード済みのモデル名をそのまま指定します。ローカルで利用可能なモデルはollama listコマンドで確認できます。
クラウドAPIのモデルとまったく同じインターフェースで使えるため、開発時はOllamaでローカルテストし、本番ではクラウドAPIに切り替えるといった運用も容易です。
ストリーミング
ストリーミングも全プロバイダーで統一的に利用できます。
from litellm import completion
messages = [{"role": "user", "content": "日本の四季について簡潔に説明してください"}]
# stream=Trueを渡すだけ
response = completion(model="anthropic/claude-sonnet-4-6", messages=messages, stream=True)
for chunk in response:
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print()
非同期呼び出しで複数モデルに同時リクエスト
acompletion()を使うことで、複数のモデルに非同期で同時にリクエストを送れます。モデルの比較評価を効率的に行う際に便利です。
import asyncio
from litellm import acompletion
models = [
"openai/gpt-5.5",
"anthropic/claude-sonnet-4-6",
"gemini/gemini-2.5-flash",
]
async def call_model(model: str, messages: list[dict]) -> dict:
response = await acompletion(model=model, messages=messages)
return {
"model": model,
"response": response.choices[0].message.content,
"tokens": response.usage.total_tokens,
}
async def main():
messages = [{"role": "user", "content": "量子コンピュータを小学生にもわかるように一文で説明してください"}]
tasks = [call_model(model, messages) for model in models]
results = await asyncio.gather(*tasks)
for result in results:
print(f"\n=== {result['model']} (tokens: {result['tokens']}) ===")
print(result["response"])
asyncio.run(main())
実行すると、3つのモデルに並列でリクエストが送られ、それぞれの回答とトークン数が表示されます。
フォールバック機能
特定のモデルが失敗した場合に、別のモデルへ自動的にフォールバックさせることができます。本番環境でのサービス可用性を高めるのに有用です。
from litellm import completion
# fallbacksにフォールバック先モデルをリストで指定
response = completion(
model="openai/gpt-5.5-fallback_test",
messages=[{"role": "user", "content": "Hello"}],
fallbacks=["anthropic/claude-sonnet-4-6", "gemini/gemini-2.5-flash"],
)
print(response.choices[0].message.content)
print(f"実際に使用されたモデル: {response.model}")
この例では、存在しないモデルgpt-5.5-fallback_testへのリクエストが失敗し、Claude Sonnet → Gemini 2.5 Flashの順にフォールバックが試行されます。
コスト追跡
completion_cost()関数でリクエストごとのコストを確認できます。
from litellm import completion, completion_cost
models = [
"openai/gpt-5.5",
"anthropic/claude-sonnet-4-6",
"gemini/gemini-2.5-flash",
]
messages = [{"role": "user", "content": "Pythonのリスト内包表記について50文字以内で説明してください"}]
for model in models:
response = completion(model=model, messages=messages)
cost = completion_cost(completion_response=response)
print(f"{model}: ${cost:.6f} ({response.usage.prompt_tokens} + {response.usage.completion_tokens} tokens)")
実行結果の例:
openai/gpt-5.5: $0.000412 (21 + 24 tokens)
anthropic/claude-sonnet-4-6: $0.000630 (30 + 42 tokens)
gemini/gemini-2.5-flash: $0.001080 (15 + 430 tokens)
これにより、どのモデルがコストパフォーマンスに優れているかを定量的に比較できます。
Embeddings(テキスト埋め込み)
LiteLLMはcompletion()だけでなく、embedding()関数でテキスト埋め込みベクトルの生成にも対応しています。RAG(検索拡張生成)やセマンティック検索などで活用できます。
from litellm import embedding
texts = ["Pythonは汎用プログラミング言語です", "機械学習はAIの一分野です"]
# OpenAI
response_openai = embedding(model="openai/text-embedding-3-small", input=texts)
print("=== OpenAI (text-embedding-3-small) ===")
print(f"次元数: {len(response_openai.data[0]['embedding'])}")
print(f"ベクトル(先頭5要素): {response_openai.data[0]['embedding'][:5]}")
print(f"トークン数: {response_openai.usage.total_tokens}")
# Gemini
response_gemini = embedding(model="gemini/gemini-embedding-001", input=texts)
print("\n=== Gemini (gemini-embedding-001) ===")
print(f"次元数: {len(response_gemini.data[0]['embedding'])}")
print(f"ベクトル(先頭5要素): {response_gemini.data[0]['embedding'][:5]}")
print(f"トークン数: {response_gemini.usage.total_tokens}")
実行結果の例:
=== OpenAI (text-embedding-3-small) ===
次元数: 1536
ベクトル(先頭5要素): [-0.01030731201171875, 0.01023101806640625, 0.0157470703125, -0.005435943603515625, 0.048553466796875]
トークン数: 33
=== Gemini (gemini-embedding-001) ===
次元数: 3072
ベクトル(先頭5要素): [0.0058305715, -0.0037729947, 0.0031870084, -0.059216924, -0.022574358]
トークン数: 33
チャット補完と同様に、モデル名を変えるだけでプロバイダーを切り替えられます。レスポンス形式もOpenAI互換で統一されているため、埋め込みベクトルの利用側コードを変更する必要がありません。
画像生成(Image Generation)
image_generation()関数で、テキストプロンプトから画像を生成できます。
import base64
from litellm import image_generation
# OpenAI GPT Image 1.5
response_openai = image_generation(
model="openai/gpt-image-1.5",
prompt="富士山と桜の美しい風景、水彩画風",
n=1,
)
print("=== OpenAI (GPT Image 1.5) ===")
image_data = response_openai.data[0]["b64_json"]
with open("generated_image_openai.png", "wb") as f:
f.write(base64.b64decode(image_data))
print("画像を generated_image_openai.png に保存しました")
# Google Imagen 3(Vertex AI)
# Vertex AIを使用するため、以下が必要
# uv pip install 'litellm[google]'
# export VERTEXAI_PROJECT="your-gcp-project-id"
# export VERTEXAI_LOCATION="us-central1"
# サービスアカウントにVertex AI権限の付与も必要
response_gemini = image_generation(
model="vertex_ai/imagen-3.0-generate-002",
prompt="富士山と桜の美しい風景、水彩画風",
n=1,
)
print("\n=== Google (Imagen 3) ===")
image_data = response_gemini.data[0]["b64_json"]
with open("generated_image_gemini.png", "wb") as f:
f.write(base64.b64decode(image_data))
print("画像を generated_image_gemini.png に保存しました")
チャット補完と同様に、モデル名を変えるだけでOpenAIとGeminiの画像生成を切り替えられます。どちらもbase64エンコードされた画像データを返すため、デコードしてファイルに保存します。
音声文字起こし(Transcription)
transcription()関数で、音声ファイルからテキストへの文字起こしができます。
import os
from litellm import transcription
audio_file_path = os.environ.get("AUDIO_FILE_PATH", "sample.mp3")
# OpenAI GPT-4o Transcribe
with open(audio_file_path, "rb") as audio_file:
response = transcription(
model="openai/gpt-4o-transcribe",
file=audio_file,
)
print("=== OpenAI (GPT-4o Transcribe) ===")
print(f"文字起こし結果: {response.text}")
OpenAIのGPT-4o Transcribeモデルを使った音声文字起こしをLiteLLM経由で実行できます。こちらもレスポンス形式がOpenAI互換で統一されているため、プロバイダーの切り替えが容易です。
タスク難易度に応じたモデルルーティング
LiteLLMのRouter機能を使えば、タスクの特性に応じてモデルを動的にルーティングすることも可能です。
from litellm import Router
router = Router(
model_list=[
{
"model_name": "high-quality",
"litellm_params": {
"model": "anthropic/claude-sonnet-4-6",
},
},
{
"model_name": "cost-effective",
"litellm_params": {
"model": "gemini/gemini-2.5-flash",
},
},
]
)
# 複雑なタスクにはハイエンドモデル
response = router.completion(
model="high-quality",
messages=[{"role": "user", "content": "マイクロサービスアーキテクチャのメリット・デメリットを詳細に分析してください"}],
)
print("=== High Quality Model ===")
print(response.choices[0].message.content[:200])
# 簡単なタスクにはコスト効率の良いモデル
response = router.completion(
model="cost-effective",
messages=[{"role": "user", "content": "Pythonでリストをソートするコードを書いてください"}],
)
print("\n=== Cost Effective Model ===")
print(response.choices[0].message.content[:200])
Routerにモデルのエイリアスを定義しておくことで、アプリケーション側ではタスクの性質に応じてhigh-qualityやcost-effectiveを指定するだけでよくなります。モデルの変更はRouter側の設定を変えるだけで済むため、運用が楽になります。
まとめ
複数のLLMプロバイダーを統一インターフェースで扱えるLiteLLMを試してみました。
LiteLLMの大きな魅力は、completion()関数のモデル名を変えるだけでOpenAI・Claude・Geminiを切り替えられるシンプルさにあります。プロバイダーごとにSDKやレスポンス形式の違いを意識する必要がなくなるため、開発者はプロンプトやアプリケーションロジックに集中できます。
さらに、チャット補完だけでなく、embedding()によるテキスト埋め込み、image_generation()による画像生成、transcription()による音声文字起こしなど、複数のエンドポイントが統一インターフェースで提供されています。これにより、LLMアプリケーションで必要となる多様な機能をプロバイダーに依存しない形で実装できます。
また、フォールバック・コスト追跡・ルーティングといった本番運用に必要な機能も備わっており、複数モデルを活用する際の運用負荷を大幅に軽減できます。
LLMを使ったアプリケーション開発で複数モデルの使い分けやコスト最適化を検討している方は、ぜひ試してみてください。
最後まで読んでいただきありがとうございました。









