まだLLM API呼び出しで消耗してるの?LiteLLMによるAPI呼び出し共通化のススメ
こんにちはべいえりあです。いきなり煽りタイトルで申し訳ないのですが、今回はLiteLLMについて書いてみようと思います。
LiteLLMとは?
LiteLLMは様々なLLM APIをOpenAIフォーマットで呼び出せるサービスです。
LLM APIを共通のフォーマットで呼び出せるサービスとしては他にもLangChainなどもあると思うのですが、APIの呼び出し共通化が目的だとLangChainをインストールすると不要な機能/ライブラリも併せて大量にインストールされるので、必要最小限の機能があれば良いのであればLiteLLMがオススメです。
LiteLLM自体は(LLMのライブラリとしては)そこそこ古いライブラリで、開発が始まったのは2023年の8月なのですが、最近ではLLM系の有名ライブラリでも使われるようになってきていて、例えばDSPyがLiteLLMに移行したのも話題になりました。
こちらのリリースノートを見てもらえれば分かると思うのですが、すさまじい勢いで開発が進んでいて1日1回以上のペースで何らかの機能が実装されており、何か問題があったとしても次に見た時は大体問題が解決しているような印象です。
上記の通り、LiteLLMはそれなりに古いライブラリなのでもう使いこなしてる方もいらっしゃるとは思うのですが、いろんな人と話してみると意外に知られていない気がするので、今回はLiteLLMの基礎的な機能について紹介したいと思います。
LiteLLMで何ができる?
LLM API呼び出しインターフェースの統合
一番大きいのがこれだと思います。以下がAzure OpenAIを呼び出すためのコードです。
messages = [
{"role": "system", "content": "You are an awesome assistant."},
{"role": "user", "content": "Hi, how are you?"},
]
result = litellm.completion(model="azure/gpt-4o-mini", messages=messages)
続いて、Vertex AIでGeminiを呼び出すためのコード。
messages = [
{"role": "system", "content": "You are an awesome assistant."},
{"role": "user", "content": "Hi, how are you?"},
]
result = litellm.completion(model="gemini-1.5-flash-001", messages=messages)
最後に、Amazon BedrockでClaudeを呼び出すためのコードです。
messages = [
{"role": "system", "content": "You are an awesome assistant."},
{"role": "user", "content": "Hi, how are you?"},
]
result = litellm.completion(
model="bedrock/anthropic.claude-3-haiku-20240307-v1:0",
messages=messages
)
…お分かりいただけたでしょうか?ほぼほぼ同じインターフェースで様々なAPIを呼び出せます。Messagesについても全てOpenAIフォーマットでOKで、ライブラリ内でよしなに各API向けに変換してくれます。
一応、必要情報を環境変数として入れないといけなくはあるのですが、それさえ入っていればOKです。また、LiteLLMの開発者の方々が様々な入力パラメーターに対応してくれているため、モデルに共通のパラメーター(温度や最大トークン数など)も使えますし、各LLMに固有のパラメーター(リージョンなど)も使えます。
litellm.vertex_location = "asia-northeast1" # Vertex AIのリージョン設定
result = litellm.completion(
model="gemini-1.5-flash-001",
messages=messages,
temperature=1.0,
max_tokens=1000,
)
こんな感じなので、様々なAPIを比較してみたい時にめちゃくちゃ便利ですし、本番環境のAPIを切り替えるのも(環境変数は別途必要ですが)一行の変更で出来るため、LLM APIを使った開発がとても捗るかと思います。
フレキシブルなフォールバック設定
LLM API呼び出しインターフェースが共通化されているということは、何も入力や出力だけが共通化されているわけではなく、エラーが発生した時の例外も共通化されています。
例えばタイムアウトした際、OpenAIはopenai.APITimeoutError、Vertex AIはビルトインのTimeoutErrorを吐きますが、openai.APITimeoutErrorはTimeoutErrorを継承したりはしてないので、別途取り扱う必要が出てきます。これがLiteLLMではlitellm.exceptions.Timeoutに共通化されます。
これで何が嬉しいかと言うと、フォールバックなどの設定が共通化できるようになります。ありがたいことに、LiteLLMにはそのフォールバックをよしなに処理してくれるRouterというものがあります。
このRouterを使うとフォールバックを細かく設定できて、例えば、
- まずはChatGPTのAPIを呼び出す
- litellm.exceptions.Timeoutが2回、または他のエラーが1回発生した場合にGeminiのAPIにフォールバックする
みたいな処理が以下のように簡単に書けます。
model_list = [
# ここでモデル名とモデルのパラメーターを定義する
{"model_name": "chatgpt", "litellm_params": {"model": "azure/gpt-4o", ...}},
{"model_name": "gemini", "litellm_params": {"model": "gemini-1.5-flash-001", ...}},
]
fallbacks = [{"chatgpt": ["gemini"]}] # chatgpt -> geminiにフォールバックする
router = litellm.router.Router(
model_list=model_list,
fallbacks=fallbacks,
num_retries=0, # タイムアウト以外は即フォールバックする
retry_policy=litellm.router.RetryPolicy(TimeoutErrorRetries=1),
)
result = router.completion(model="chatgpt", messages=messages)
皆さんご存知の通り(?)、ChatGPTはOpenAI経由でもAzure経由でもたまに落ちるので、失敗が許されないサービスで本番環境に入れるとなると、このようなフォールバックは必須になるかと思います。弊社でも実は最近までフォールバックが入ってなかった(超絶微声)のですが、最近Routerを用いたフォールバックの実装が本番環境に入りました。
注:LiteLLM Proxyは弊社では(まだ)使ってません
LiteLLMではプロキシサーバーを立てることも出来ます。
多分、複数サービスから同じLLMを呼び出すなどがあるとプロキシサーバーがあった方が良いのかもしれませんが、弊社ではサービス毎に別のLLMを使いわけていたり、サービスによってサービスレベルが結構違ったりするので、あまりプロキシサーバーを使うメリットが無く、まだ導入はしていません。ただ、公式ではプロキシサーバーの利用を推奨しているようなので、興味がある人はドキュメントを見てみてください。
あとはキャッシュなども使えるのですが、こちらも弊社のユースケースではそこまでご利益が無いため使ってないです。
LiteLLMを使うデメリット
(APIによっては)呼び出しが遅くなる
LiteLLMは多分いろいろラップしてるからだと思うのですが、呼び出しの際にオーバーヘッドがあります。出力長が短いとレイテンシーが倍くらい違うこともありますが、出力長が長くなると相対的にレイテンシーの差は小さくなってきます。
弊社では比較的短い出力を出す応用が多いのでこのオーバーヘッドは望ましくはないのですが、とは言え応用上そこまでクリティカルではないのと、API切り替えの利便性を考えて多少のオーバーヘッドは許容することにしました。
初回呼び出しのオーバーヘッドが大きい
これもモデルによると思うのですが、最初の呼び出し時に限りgemini-1.5-flash-002だと1.5秒くらいオーバーヘッドが乗ります。ただ、こちらは初回のみなので、弊社ではAPIを一度空呼び出しして実際のサービスには影響が出ないようにしています。
ドキュメンテーションが無い便利機能が結構ある/実際にコードを見ないと分からないことがある
こちらは開発が爆速で進んでいるライブラリの常なような気もするのですが、ドキュメントに無い仕様が結構あります。例えば、Vertex AIのGeminiを使う場合、公式のドキュメントにはvertex_credentialsという引数をcompletion呼び出し時に渡さなければいけない風なことが書いてあるのですが、コードのこの辺を見ると実は環境変数で設定できることが分かったりします。
このように割と重要な機能(引数でcredentialを渡さないといけないのであれば、コードが共通化できない)でもコードを読まないと分からないことがあったりするので、全くコードを読みたくない、という感じだと便利機能を使いこなせないかもしれません。
最後に
こんな感じでLiteLLMは非常に便利なので、是非皆さんも使ってみてください!
IVRyではこのLiteLLMを使いつつ、様々なLLMサービスを本番運用しています。AIエンジニア(Ops系含む)も絶賛募集中なので、LLMを使った本番サービスに興味がある方、是非一緒に働きましょう!
Discussion