LiteLM 實戰最容易踩的坑,不是安裝失敗,而是把一個 Python library 當成完整 LLM Gateway。這個 alpha 專案以「約 2,900 行、2 個依賴、19 個 provider 路由目標」在 Show HN 吸引注意;但我們在 pinned tree 依「所有 litelm/**/*.py 實體行、含空白與註解」重數是 3,121 行,兩個也只指 direct base runtime dependencies。熱度與小巧都不等於 production readiness,真正要問的是:它替你完成哪一段,又把哪些責任留給應用程式?
這篇會先用不需要 API Key 的 mock request 跑通呼叫,再用同一組 contract 思維檢查 streaming、tool calling、錯誤、跨供應商 fallback 與 trace 收據。先把結論說在前面:LiteLM 適合學習 provider 路由、在單一 Python 應用內統一呼叫介面;它本身不是可讓 OpenAI client 連入的 HTTP Proxy。
先記住這條判斷式:LiteLM = provider/model 路由+格式翻譯;完整 Gateway = 這類呼叫核心+HTTP 入口+身分驗證/限流+跨 Provider fallback+端到端觀測。這是責任邊界的心智模型,不代表每個 Gateway 都採用 LiteLM。
LiteLM 實戰先說結論:小而可讀,但不是 Gateway
⚡ 三句話版:
一、截至 v0.5.2,LiteLM 提供 Python 函式,不提供 Proxy server;你不能只啟動它,就讓既有 OpenAI SDK 改一個 base_url 連入。
二、provider/model 可決定單次呼叫去哪裡,但 Router、load balancing 與跨供應商 fallback 不在其宣告範圍。
三、先用 mock 與 contract tests 理解介面,再用你自己的故障注入與 trace 收據驗收;需要多人共用的網路入口時,另選 Gateway 或自行補齊控制面。
LiteLM v0.5.2 README把範圍寫得很直接:保留 model routing、訊息格式轉換、streaming、tool use、embeddings,以及選定上游有 OpenAI-compatible Responses endpoint 時的 SDK passthrough;排除 Router、Proxy、cache、預算與成本追蹤。這也是它和名稱只多一個大寫 L 的 LiteLLM AI Gateway最關鍵的差別。

LiteLM 到底做什麼?先拆開一個 request
- 解析目標:
openai/model-name、anthropic/model-name這類前綴決定 provider;沒有前綴時預設走 OpenAI。 - 選擇呼叫路徑:Anthropic、Cloudflare、Mistral 等有客製 handler;許多其他服務走 OpenAI-compatible endpoint。
- 轉換輸入:把 messages、tools 與部分 provider 特有欄位整理成對方接受的格式。
- 送出上游呼叫:在同一個 Python process 內使用 SDK 或 HTTP client;這一步不是接收外部 client 的 Proxy。
- 標準化輸出:整理文字、串流 chunk、tool call,並映射部分起始呼叫的常見錯誤;多數 route 在串流開始後拋出的例外仍可能保留原 SDK 型別。
原始碼雖有 Bedrock handler,但 AlphaLab 在 v0.5.2 與相同 package code 的 main 快照重現:未顯式傳入 api_key 的一般 bedrock/... 路徑,會先在 provider parser 把 key_env=None 傳入 os.environ.get(),觸發 TypeError,尚未走到 handler。這就是「路由表有一列」不能等同「目前可用」的具體反例。
這個設計和AI Agent Harness的想法相同:薄薄的模型呼叫層只是系統的一部分;外面還要有 secrets、policy、eval、觀測與部署邊界。如果你想看完整 Gateway 的 OpenTelemetry、cache 與 failover 驗收,可搭配AI Gateway 故障實驗閱讀。
LiteLM 實戰步驟 1:鎖版安裝,先跑零成本 mock
本文以 2026 年 9 月 11 日發布的 v0.5.2 為基準;PyPI 要求 Python 3.10 以上。先建獨立環境並鎖版,不要讓明天的新 release 偷換掉今天的結果。
python3 -m venv .venv
. .venv/bin/activate
python -m pip install "litelm==0.5.2"
python - <<'PY'
import litelm
r = litelm.completion(
"openai/demo-model",
messages=[{"role": "user", "content": "ping"}],
mock_response="pong",
)
print(litelm.__version__, r.choices[0].message.content)
PY
預期輸出是 0.5.2 pong。它只證明套件可載入、completion contract 與標準化回應可走通,不證明 OpenAI 或任何真實 provider 已連線。這種把證據拆小的做法,也適合放進從零建立 AI Agent Harness的 smoke gate。
步驟 2:用環境變數替換 provider,不把模型名寫死
真正呼叫時,讓部署環境決定 target 與金鑰。下方 LLM_TARGET 必須換成帳戶目前可用的真實模型;範例刻意不硬編一個可能退役的 model ID。
export LLM_TARGET='openai/<your-model>'
export OPENAI_API_KEY='<your-key>'
python - <<'PY'
import os, litelm
response = litelm.completion(
os.environ["LLM_TARGET"],
messages=[{"role": "user", "content": "只回答 OK"}],
timeout=20,
)
print(response.choices[0].message.content)
PY
要換到 OpenRouter、Groq 或自架 OpenAI-compatible endpoint,可改 target、對應金鑰或 api_base;但「語法能路由」仍不等於「每個模型的參數與回應完全等價」。api_base 會收到解析後的 bearer key,只能由受信任部署設定控制,不要讓終端使用者任意指定。需要固定上游、避免 provider 漂移時,可延伸使用OpenRouter Provider Pinning的驗收方法。
步驟 3:先寫 streaming 與 tool calling 的驗收條件
Streaming 至少要驗證三件事:首段文字是否在門檻內到達、空 choices/空 delta 不會弄壞 UI、中途斷線能被記錄。Tool calling 則要檢查 tool name、arguments 是否為合法 JSON、未知工具是否拒絕。LiteLM 只回傳 tool call;真正執行函式、驗證參數與權限仍是你的應用責任。
python - <<'PY'
import os, time, litelm
started, first_text_ms, pieces = time.monotonic(), None, []
for chunk in litelm.completion(
os.environ["LLM_TARGET"],
messages=[{"role": "user", "content": "用三個字打招呼"}],
stream=True, timeout=20,
):
if not chunk.choices:
continue
text = chunk.choices[0].delta.content or ""
if text and first_text_ms is None:
first_text_ms = (time.monotonic() - started) * 1000
pieces.append(text)
assert first_text_ms is not None and "".join(pieces)
print({"first_text_ms": round(first_text_ms, 1), "text": "".join(pieces)})
PY
python - <<'PY'
import json, os, litelm
tools = [{"type": "function", "function": {
"name": "get_weather",
"parameters": {"type": "object", "properties": {
"city": {"type": "string"}}, "required": ["city"]},
}}]
r = litelm.completion(os.environ["LLM_TARGET"],
messages=[{"role": "user", "content": "查台北天氣"}],
tools=tools, tool_choice="required", timeout=20)
call = r.choices[0].message.tool_calls[0]
args = json.loads(call.function.arguments)
assert call.function.name == "get_weather" and isinstance(args["city"], str)
print({"tool": call.function.name, "args": args})
PY
這兩段是需要真實模型能力的 success-path probes,不是完成的 failure-path contract,也不是 AlphaLab 已替所有 route 跑過的成績。請先替 first_text_ms 寫下自己的 SLO 門檻,再用可控制的 OpenAI-compatible stub 注入首段後斷線。把同一批合法、未知工具、壞 JSON 與故意中斷的 requests 分別送到每個候選 target,保存 request ID、首段延遲、完成原因、tool arguments、usage 與實際錯誤型別;不要把 capability helper 當成即時保證。這才是OpenRouter 六組 Contract Tests所強調的「同一把尺」。
步驟 4:重試和 fallback 要分開,並保留 trace 收據
Retry 是同一 target 再試一次;fallback 是改投另一個 target。兩者混在一起,事故後就無法回答「到底呼叫了誰」。截至 v0.5.2,README 明列 Router/fallbacks 不在 LiteLM 範圍;目前原始碼也會移除傳入的 fallbacks。另一方面,OpenAI-compatible 路徑可把 max_retries/num_retries 交給 SDK,但客製 handler 並未一致接收,retry_strategy 也會被移除;所以不能宣稱有跨 provider 一致的 retry policy。
最小可驗收做法,是在應用層維護有序 target 清單,只有 rate limit、timeout、連線或 5xx 類型才允許轉移;authentication、權限、格式錯誤應直接失敗。每次應用層 retry/fallback 決策至少留下 target、attempt、decision、error type 與 latency;這不會揭露 SDK 內部每個 HTTP attempt。正式版本再補 status code、upstream request ID、總 deadline、Retry-After、指數 backoff 與 jitter,但不要把 prompt 或金鑰寫入收據。
import time, litelm
from litelm import APIConnectionError, InternalServerError, RateLimitError, Timeout
RETRYABLE = (RateLimitError, Timeout, APIConnectionError, InternalServerError)
def call_with_fallback(targets, messages, *, invoke=litelm.completion,
retryable_errors=RETRYABLE, retries_per_target=1):
receipt = []
for target_index, target in enumerate(targets):
for attempt in range(1, retries_per_target + 2):
started = time.monotonic()
try:
result = invoke(target, messages=messages, timeout=20)
except retryable_errors as exc:
can_retry = attempt <= retries_per_target
has_fallback = target_index < len(targets) - 1
decision = "retry" if can_retry else "fallback" if has_fallback else "exhausted"
receipt.append({"target": target, "attempt": attempt, "ok": False,
"decision": decision, "error_type": type(exc).__name__,
"latency_ms": round((time.monotonic() - started) * 1000, 3)})
if can_retry:
continue
break
receipt.append({"target": target, "attempt": attempt, "ok": True,
"decision": "return", "error_type": None,
"latency_ms": round((time.monotonic() - started) * 1000, 3)})
return result, receipt
raise RuntimeError(f"all targets failed: {receipt}")
這是可測的 state machine,不是完整 production retry library。AlphaLab 對同一份函式注入可重試例外,重現決策順序 [retry, fallback, return];另注入不在 allowlist 的 authentication 類錯誤,確認它直接拋出且不會轉投。接著仍要測「兩個都失敗」與「stream 已輸出部分內容後不得重播」。跨 provider 可能重複計費,也可能把資料送往不同司法轄區;target 必須先經 allowlist,而不是見錯就全送一輪。
步驟 5:跑兩層測試,別把 maintainer claim 當你的結果
AlphaLab 在 LiteLM main commit 4a260c7、Python 3.14.7 的隔離環境跑了兩層不需供應商金鑰的檢查:專案非 live suite 為 262 passed、55 skipped;同步到當時 LiteLLM upstream commit 9276317 後,專案挑選的快速 ported contract 為 49 passed。這個 main 只比 v0.5.2 tag 多一個文件/測試/腳本 commit,package source 相同;官方 CI 範圍是 Python 3.10–3.13,並不包含我們的 3.14.7 環境。
Mock completion 另回傳版本 0.5.2 與內容 pong,故障注入收據也通過上述三段決策。這些結果只支持「本機 contract 在該快照可重現」;快速 ported gate 是 49 個 allowlisted nodes,官方 CI 也不跑這道 gate 或 live provider tests,不能外推成完整 LiteLLM parity。維護者的表宣告 19 個 canonical routing targets,其中 7 個標成 verified、12 個仍是 No;專案狀態也明寫 Alpha。AlphaLab 沒有執行付費 live provider 測試。

何時適合 LiteLM?何時直接選完整 Gateway?
- 適合學習:想讀懂 provider 路由、訊息翻譯、stream chunk 與錯誤標準化,而且願意接受 alpha 變動。
- 可評估內網單一應用:只有一個受控 Python service,自行管理金鑰、重試、fallback、log 與部署,並有鎖版和 rollback。
- 不該只靠 LiteLM:多個團隊或 client 要共用 HTTP endpoint,需要 API key 管理、rate limit、預算、集中稽核、負載平衡或跨 provider failover。
若需求落在第三種,請比較有明確 Gateway contract 的產品。以 LiteLLM 官方文件為例,它另有 Proxy 與跨供應商 fallback/可靠性設定;選擇標準不該是程式碼行數最少,而是事故時能否證明 policy 真有執行。想從系統層建立完整清單,可再讀AI Agent Harness 實作指南。
常見問題 FAQ
LiteLM 是 LiteLLM 的官方精簡版嗎?
不是。兩者是不同套件與 repository;LiteLM 的目標是相容部分 Python 呼叫面,不能從名稱推定官方從屬關係。
LiteLM 可以直接當 OpenAI-compatible Proxy 嗎?
不可以單獨做到。v0.5.2 不提供 Proxy server;若要讓外部 OpenAI client 透過 base_url 連線,必須自建 HTTP 層或選擇現成 Gateway。
「19 個 routing targets」代表全部實測通過嗎?
不代表。截至本文版本,19 個是 canonical 路由目標,不全是公司;registry 另含別名。維護者只把其中 7 個標成 verified,AlphaLab 也沒有執行付費 live provider 測試。
LiteLM 會自動跨供應商 fallback 嗎?
不會。README 明列 Router/fallbacks 不在範圍;需要時應由應用層明確實作,或交給有此 contract 的 Gateway。
Tool calling 會替我執行函式嗎?
不會。它標準化模型回傳的 tool call;白名單、參數驗證、真正執行與結果回傳仍屬應用責任。
內建 callback 足以做 production tracing 嗎?
目前不夠完整。v0.5.2 相同 package source只對成功、非 streaming 的 completion/acompletion chat calls 提供 success callback;失敗、stream、embedding、Responses、text completion 與跨 target attempt 應在外層補收據。
2 個 direct runtime dependencies 就代表風險更低嗎?
不能只看數量下結論。兩個只是 base requirements;它們的 transitive packages、optional SDK、上游服務、版本更新與你補上的 Proxy/觀測層仍要一起盤點。
新手第一個 production gate 應該是什麼?
先驗證失敗路徑。固定版本與模型,注入 timeout、429、5xx、authentication error 和串流中斷,確認每次應用層 retry/fallback 決策都可由 trace 收據還原。
給新手的 5 個重點
- LiteLM 是程序內的多供應商呼叫 library,不是可直接對外服務的 Gateway。
provider/model解決路由語法,不自動帶來負載平衡或跨供應商 fallback;retry 行為也不是所有 handler 一致。- 先用 mock 和 non-live tests 驗證 contract,再用自己的 provider 金鑰做 live acceptance。
- Streaming、tool calling、錯誤與 fallback 都要用故障情境驗收,並保留逐次 attempt 收據。
- 選型看責任邊界與可證明的 SLO,不看最少行數或一次熱門討論。
接著閱讀
左右滑動查看更多推薦
結語:LiteLM 的價值,在於讓邊界看得見
LiteLM 最值得學的不是「行數少就能取代所有 Gateway」,而是把 provider routing 與格式翻譯縮到可以讀懂的尺度。先跑 mock、再跑 contract、最後故意讓 timeout 與上游失敗發生;如果你能從收據還原每次應用層路由決策,它就可能是合適的內部呼叫層。若還需要共享 HTTP 入口、中央政策與營運控制,就把那些責任明確交給 Gateway。想把這套驗收延伸成完整工作流,也可從 AlphaLab 的AI 與投資實戰課程繼續學。
