與接口測(cè)試排錯(cuò)記錄)
LiteLLM 啟動(dòng)與接口測(cè)試排錯(cuò)記錄本文記錄my-litellm-service第一次在本地啟動(dòng) LiteLLM Proxy并通過(guò) OpenAI 兼容接口調(diào)用 Gemini 時(shí)遇到的問(wèn)題、排查過(guò)程和最終解決方式。這次排錯(cuò)涉及的內(nèi)容比較多Python 依賴、uv環(huán)境、FastAPI 版本、Redis 網(wǎng)絡(luò)路徑、Tailscale、LiteLLM 網(wǎng)關(guān)認(rèn)證、健康檢查、模型輸出 Token以及 Gemini 的 thinking 和 429 限流。1. LiteLLM Proxy 啟動(dòng)方式當(dāng)前項(xiàng)目不是通過(guò)python main.py啟動(dòng) LiteLLM。LiteLLM Proxy 是第三方包提供的命令行程序入口來(lái)自虛擬環(huán)境中的.venv/bin/litellm推薦啟動(dòng)命令cd/home/gateman/projects/github/my-litellm-service uv run --env-file .env\litellm\--configconfig.yaml\21|tee-a/var/log/my-litellm-service/litellm.log這里的--env-file .env只負(fù)責(zé)把環(huán)境變量注入 LiteLLM 進(jìn)程例如OPENAI_API_KEY_FREE_1... LITELLM_MASTER_KEY... REDIS_HOST... REDIS_PASSWORD...它不會(huì)修改當(dāng)前 shell 的環(huán)境變量。之后使用curl的終端仍需要單獨(dú)執(zhí)行set-asource.envseta否則下面的變量可能為空或仍然是舊值$LITELLM_MASTER_KEY這是本次排錯(cuò)中非常關(guān)鍵的一點(diǎn)uv --env-file .env → LiteLLM 進(jìn)程 source .env → 當(dāng)前 shell 和 curl2. 第一個(gè)問(wèn)題缺少 LiteLLM Proxy 依賴最初的依賴聲明是litellm1.74.0,2.0.0啟動(dòng) Proxy 時(shí)出現(xiàn)ModuleNotFoundError: No module named backoffLiteLLM 的基礎(chǔ)包和 Proxy 所需依賴不是完全相同的集合?;A(chǔ)包可以用于 SDK 調(diào)用但啟動(dòng)完整 Proxy 還需要額外依賴。因此將依賴修改為litellm[proxy]1.74.0,2.0.0然后重新解析和同步環(huán)境uv lock uvsync--devlitellm[proxy]會(huì)額外安裝 Proxy 所需的依賴?yán)鏱ackoff、Proxy 運(yùn)行組件、Redis 相關(guān)組件和 Web 服務(wù)組件。3. 第二個(gè)問(wèn)題LiteLLM 與 FastAPI 版本不兼容安裝 Proxy extra 后LiteLLM 可以繼續(xù)啟動(dòng)但出現(xiàn)了ImportError: cannot import name get_flat_dependant from fastapi.dependencies.utils檢查實(shí)際版本LiteLLM 1.97.0 FastAPI 0.141.1LiteLLM Proxy 代碼仍然導(dǎo)入get_flat_dependant而較新的 FastAPI 已經(jīng)移除了這個(gè)接口。問(wèn)題不是缺少 Python 文件而是兩個(gè)包的版本接口不兼容。最后將 FastAPI 固定到仍然提供該接口的版本fastapi0.136.3,0.137.0然后重新執(zhí)行uv lock uvsync--dev驗(yàn)證.venv/bin/python-c\from fastapi.dependencies.utils import get_flat_dependant; print(compatible)LiteLLM 隨后可以正常進(jìn)入Application startup complete. Uvicorn running on http://0.0.0.0:4000這里得到的經(jīng)驗(yàn)是使用 LiteLLM Proxy 時(shí)不能只看 LiteLLM 自己的版本還要檢查它的 Proxy extra 對(duì) FastAPI、Starlette 和 Uvicorn 的兼容約束。4. 日志輸出到指定文件直接啟動(dòng) LiteLLM 時(shí)日志默認(rèn)輸出到終端。為了保存日志使用21|tee-a/var/log/my-litellm-service/litellm.log第一次執(zhí)行時(shí)出現(xiàn)/var/log/my-litellm-service/litellm.log: No such file or directory原因是目標(biāo)目錄還不存在。先創(chuàng)建并授權(quán)sudomkdir-p/var/log/my-litellm-servicesudochowngateman:gateman /var/log/my-litellm-servicesudochmod750/var/log/my-litellm-service之后重新啟動(dòng)即可uv run --env-file .env\litellm--configconfig.yaml\21|tee-a/var/log/my-litellm-service/litellm.logLiteLLM 是前臺(tái)服務(wù)啟動(dòng)命令不返回 shell 是正?,F(xiàn)象不是卡死。看到下面的日志就說(shuō)明服務(wù)已經(jīng)啟動(dòng)Application startup complete. Uvicorn running on http://0.0.0.0:40005. Redis 緩存配置與連接問(wèn)題當(dāng)前config.yaml啟用了 LiteLLM 原生 Redis Response Cachelitellm_settings:cache:truecache_params:type:redishost:os.environ/REDIS_HOSTport:os.environ/REDIS_PORTpassword:os.environ/REDIS_PASSWORDsupported_call_types:[chat_completion]ttl:3600LiteLLM 會(huì)自動(dòng)創(chuàng)建 Redis 客戶端、查詢緩存、寫(xiě)入響應(yīng)和處理 TTL不需要我們?cè)倬帉?xiě)一套緩存讀寫(xiě)代碼。5.1 Redis 的部署位置Redis 實(shí)際部署在 Tencent K3s 集群中的 OCIfree-arm-vm節(jié)點(diǎn)free-arm-vm └── Redis Pod通過(guò)集群檢查確認(rèn)free-arm-vm Ready Redis Pod Running Redis Service 6379Redis 的實(shí)際 Tailscale 地址是100.105.130.05.2 一開(kāi)始使用了錯(cuò)誤的地址曾經(jīng)把 Redis 配置成REDIS_HOST100.104.150.19這個(gè)地址實(shí)際上是 NUC 節(jié)點(diǎn)不是 Redis 所在的 OCI 節(jié)點(diǎn)。后來(lái)改回REDIS_HOST100.105.130.0 REDIS_PORT63795.3 為什么本地連接一開(kāi)始超時(shí)從 Main PC 測(cè)試100.105.130.0:6379 → timeout檢查路由發(fā)現(xiàn)Main PC 當(dāng)時(shí)沒(méi)有 Tailscale 路由把100.105.130.0當(dāng)成普通局域網(wǎng)地址發(fā)送到家庭網(wǎng)關(guān)。后來(lái)在 Main PC 安裝并啟用 Tailscaletailscaledactive 開(kāi)機(jī)啟動(dòng)enabled Tailscale IP100.121.12.126現(xiàn)在本地 LiteLLM 才具備訪問(wèn) OCI Redis Tailscale 地址的網(wǎng)絡(luò)條件。5.4 Kong/KIC 與 Redis 的關(guān)系KIC 負(fù)責(zé)將 Kubernetes 配置同步到 KongKong Proxy Service 才負(fù)責(zé)實(shí)際網(wǎng)絡(luò)轉(zhuǎn)發(fā)。但部署記錄中的低延遲方案不是繞經(jīng) Tencent 節(jié)點(diǎn)而是LiteLLM → Tailscale → 100.105.130.0:6379 → Redis Pod on free-arm-vm如果 LiteLLM 也部署在 K3s 集群內(nèi)部則應(yīng)該使用 Redis Service DNS如果 LiteLLM 在集群外且已加入 Tailscale則使用100.105.130.0。Redis 不應(yīng)直接暴露到公網(wǎng)。公網(wǎng)入口應(yīng)該給 LiteLLM API 使用Redis 繼續(xù)走 K3s 內(nèi)部網(wǎng)絡(luò)或 Tailscale。6.Setting Cache on Proxy不等于 Redis 已連接啟動(dòng)時(shí)看到Setting Cache on Proxy只表示 LiteLLM 正在初始化緩存功能。如果 Redis 不可達(dá)日志可能繼續(xù)出現(xiàn)Timeout connecting to server Error connecting to Sync Redis client這時(shí)可能出現(xiàn)LiteLLM Proxy啟動(dòng)成功 Redis 配置已開(kāi)啟 Redis 連接失敗 緩存不可用或降級(jí)后來(lái) Tailscale 配置完成后啟動(dòng)日志不一定每次都打印Setting Cache on Proxy但這不表示緩存被關(guān)閉。是否開(kāi)啟應(yīng)看config.yaml是否可用則要看 Redis 連接結(jié)果或?qū)嶋H緩存命中。當(dāng)前 Redis 是精確響應(yīng)緩存不是語(yǔ)義緩存。只有請(qǐng)求的模型、Prompt、消息順序和相關(guān)參數(shù)完全一致時(shí)才可能復(fù)用響應(yīng)。語(yǔ)義相近但文字不同的請(qǐng)求不會(huì)自動(dòng)命中。7. LiteLLM 的兩類 API Key本項(xiàng)目同時(shí)使用兩把不同用途的 KeyOPENAI_API_KEY_FREE_1Gemini API Key LITELLM_MASTER_KEYLiteLLM 網(wǎng)關(guān)訪問(wèn) Key調(diào)用鏈路是客戶端 使用 LITELLM_MASTER_KEY ↓ LiteLLM Proxy 使用 OPENAI_API_KEY_FREE_1 ↓ Gemini API因此客戶端調(diào)用 LiteLLM 時(shí)必須攜帶Authorization: Bearer $LITELLM_MASTER_KEY不能把 Gemini API Key 直接當(dāng)作客戶端訪問(wèn) LiteLLM 的 Key。7.1 占位 Master Key 導(dǎo)致的錯(cuò)誤最初.env中雖然存在LITELLM_MASTER_KEY但它仍然是占位值replace-with-private-master-key這會(huì)導(dǎo)致 LiteLLM 報(bào)Malformed API Key passed in.后來(lái)生成真實(shí)的sk-...Key 并寫(xiě)入.env。修改后必須重啟 LiteLLM因?yàn)?LiteLLM 只在進(jìn)程啟動(dòng)時(shí)讀取環(huán)境變量。7.2curl命令末尾多寫(xiě)字符還遇到過(guò)這樣的命令-HAuthorization: Bearer$LITELLM_MASTER_KEY1末尾的1會(huì)被拼接到 Header 值中導(dǎo)致 Key 失效。正確寫(xiě)法是-HAuthorization: Bearer$LITELLM_MASTER_KEY8./health和/v1/models返回 500 的原因匿名訪問(wèn)curlhttp://127.0.0.1:4000/health日志首先出現(xiàn)No api key passed in.隨后 LiteLLM 的異常處理器又嘗試導(dǎo)入可選的 Prisma 依賴ModuleNotFoundError: No module named prisma最終客戶端看到的是{type:internal_server_error}這個(gè) 500 的首要原因不是 Redis也不是 MySQL而是認(rèn)證失敗Prisma 錯(cuò)誤是錯(cuò)誤處理路徑中的二次異常。正確的調(diào)用方式是set-asource.envsetacurlhttp://127.0.0.1:4000/v1/models\-HAuthorization: Bearer$LITELLM_MASTER_KEY最終成功返回{data:[{id:gemini-3.7-flash}],object:list}這里也再次證明uv run --env-file .env給 LiteLLM 加載環(huán)境變量并不會(huì)自動(dòng)給另一個(gè)終端里的curl加載環(huán)境變量。9. 模型別名和真實(shí)模型名稱LiteLLM 配置中可以給模型定義別名model_list:-model_name:gemini-3.6-flash-freelayerlitellm_params:model:gemini/gemini-3.6-flashapi_key:os.environ/OPENAI_API_KEY_FREE_1客戶端請(qǐng)求使用的是gemini-3.6-flash-freelayer真正交給 Gemini Provider 的模型是gemini/gemini-3.6-flashfreelayer只是項(xiàng)目自定義別名不會(huì)自動(dòng)讓賬號(hào)進(jìn)入 Gemini 免費(fèi)層。免費(fèi)額度和限流策略由 Gemini API Key 對(duì)應(yīng)的賬號(hào)決定。LiteLLM 可能在模型尚未被真正調(diào)用前就成功啟動(dòng)即使底層模型名稱寫(xiě)錯(cuò)實(shí)際請(qǐng)求時(shí)仍可能返回模型不存在或 404。因此模型別名加載成功不代表上游模型調(diào)用已經(jīng)驗(yàn)證成功。10.max_tokens與 Gemini thinking第一次請(qǐng)求使用max_tokens:128返回finish_reason: length content: 很短或不完整原因是 Gemini 3.x 的 thinking/reasoning token 也會(huì)占用輸出額度。后來(lái)把額度提高到max_tokens:1024模型正常返回finish_reason: stop實(shí)際 Token 統(tǒng)計(jì)類似{completion_tokens:553,reasoning_tokens:526,text_tokens:27}這說(shuō)明max_tokens不是單純的“可見(jiàn)文字上限”而是包含模型推理過(guò)程在內(nèi)的輸出預(yù)算。對(duì)于一句簡(jiǎn)單回答128 可能仍然太小1024 可以讓模型有足夠空間完成 thinking 和正文。響應(yīng)中的thought_signatures:[...]是 Gemini Provider 的思考簽名元數(shù)據(jù)不是亂碼??蛻舳送ǔV恍枰x取curl...|jq-r.choices[0].message.content11. LiteLLM 的模型成本警告啟動(dòng)時(shí)還出現(xiàn)過(guò)model... not in built-in cost map cache cost fields will default to 0這表示當(dāng)前 LiteLLM 內(nèi)置價(jià)格表沒(méi)有識(shí)別某個(gè)內(nèi)部模型標(biāo)識(shí)。它影響的是緩存成本統(tǒng)計(jì)不影響Proxy 啟動(dòng)Gemini 請(qǐng)求Redis 連接OpenAI 兼容響應(yīng)如果以后需要精確統(tǒng)計(jì)緩存成本可以補(bǔ)充模型價(jià)格信息當(dāng)前階段可以先忽略這條警告。12. 最終驗(yàn)證命令12.1 查看模型列表set-asource.envsetacurlhttp://127.0.0.1:4000/v1/models\-HAuthorization: Bearer$LITELLM_MASTER_KEY12.2 調(diào)用 OpenAI 兼容聊天接口curlhttp://127.0.0.1:4000/v1/chat/completions\-HAuthorization: Bearer$LITELLM_MASTER_KEY\-HContent-Type: application/json\-d{ model: gemini-3.6-flash-freelayer, messages: [ {role: user, content: 你好請(qǐng)用一句話介紹你自己。} ], max_tokens: 1024 }12.3 只顯示模型正文curlhttp://127.0.0.1:4000/v1/chat/completions\-HAuthorization: Bearer$LITELLM_MASTER_KEY\-HContent-Type: application/json\-d{ model: gemini-3.6-flash-freelayer, messages: [ {role: user, content: Reply with exactly: OK} ], max_tokens: 1024 }|jq-r.choices[0].message.content13. 關(guān)于 429429 Too Many Requests與本地 LiteLLM 啟動(dòng)問(wèn)題不同。它通常來(lái)自 Gemini 上游常見(jiàn)原因包括免費(fèi)層請(qǐng)求頻率超過(guò)限制項(xiàng)目或 API Key 配額耗盡并發(fā)請(qǐng)求過(guò)多模型本身的配額策略如果gemini-3.7-flash經(jīng)常返回 429而gemini-3.6-flash可以成功說(shuō)明網(wǎng)絡(luò)、LiteLLM 和認(rèn)證鏈路未必有問(wèn)題更可能是特定模型或賬號(hào)配額問(wèn)題。當(dāng)前配置只有一個(gè)模型別名時(shí)LiteLLM 沒(méi)有備用模型可以切換。后續(xù)如果要做容災(zāi)需要在model_list中聲明多個(gè)模型并配置 fallback否則 429 會(huì)直接返回給客戶端。14. 當(dāng)前結(jié)論這次本地驗(yàn)證最終確認(rèn)了以下鏈路curl → LiteLLM Proxy :4000 → LITELLM_MASTER_KEY 網(wǎng)關(guān)認(rèn)證 → gemini-3.6-flash-freelayer 模型別名 → gemini/gemini-3.6-flash Provider → Gemini API同時(shí)LiteLLM Proxy 可以正常啟動(dòng)。litellm[proxy]是運(yùn)行 Proxy 所需的依賴集合。FastAPI 版本必須與 LiteLLM Proxy 兼容。Redis 部署在 OCIfree-arm-vm節(jié)點(diǎn)上跨集群訪問(wèn)依賴 Tailscale。Redis 是精確響應(yīng)緩存不是語(yǔ)義緩存。Gemini API Key 和 LiteLLM Master Key 是兩把不同的 Key。--env-file不會(huì)自動(dòng)更新另一個(gè)終端的 shell 環(huán)境。/v1/models和聊天接口需要攜帶 LiteLLM Master Key。prisma報(bào)錯(cuò)是認(rèn)證失敗后的二次異常不是本次最初原因。Gemini 3.x 的 thinking 會(huì)消耗max_tokens預(yù)算。429 需要單獨(dú)按上游配額和限流問(wèn)題處理。