LLM API 呼叫重試怎麼寫:哪些錯誤該重試,哪些別碰

做過 LLM 服務的人都寫過這段程式碼:呼叫失敗,for 迴圈重試三次。看起來很穩,但生產環境的事故裡有一半跟這三行迴圈有關。LLM API 的失敗行為與傳統 HTTP API 不一樣,重試策略得按它的特性來設計。

專屬插圖
LLM API 呼叫重試怎麼寫:哪些錯誤該重試,哪些別碰

LLM API 呼叫重試怎麼寫:哪些錯誤該重試,哪些別碰

做過 LLM 服務的人都寫過這段程式碼:呼叫失敗,for 迴圈重試三次。看起來很穩,但生產環境的事故裡有一半跟這三行迴圈有關。LLM API 的失敗行為與傳統 HTTP API 不一樣,重試策略得按它的特性來設計。

先把錯誤分類

LLM API 常見的失敗型態大致五類:

- **429(限流)**:觸發 RPM 或 token 配額上限,回應標頭通常帶 Retry-After。該重試,直接按標頭的值等待。

- **5xx 和網路逾時**:上游模型服務不穩定。該重試,但要計入熔斷統計。

- **串流傳輸中途卡死**:連線半斷,客戶端沒有拋出錯誤,只是長時間收不到新 token。必須設定閒置逾時(例如 20 秒無資料即中斷),逾時就按失敗處理。

- **400(參數錯誤、上下文超限)**:不要重試。輸入沒變,第 101 次呼叫和第 1 次一樣錯。它暴露的是要立刻修的問題:輸入超長、JSON 格式錯誤、模型名稱寫錯。

- **內容審核拒絕**:部分服務回傳特定錯誤碼。重試不會讓它通過,只會多燒錢。

判斷標準一句話:**只重試狀態可能變化的錯誤**。400 和審核拒絕是確定性的,重試每次都是確定的失敗。

退避怎麼算

別用固定 1 秒間隔。20 個請求同時撞限流,固定間隔意味著 20 個再一起撞。使用指數退避加隨機抖動:等待時間 = 1s × 2^n × rand(0.5~1.5),上限 30 秒。429 帶 Retry-After 時優先使用伺服器給的值,不要自己算。

重試次數一般 3~4 次封頂,再加一個總逾時(例如整個任務最多等 60 秒)。前三次都失敗,大概率是伺服器端問題,第四次盲打沒有意義。總逾時的作用是給上游定契約:同步介面到點必須回傳 503,或者轉非同步任務讓使用者來輪詢,不能無限掛著。

重試的隱性成本:重複計費

這是 LLM 特有的坑:**請求實際執行了,但回應沒回來,重跑一次會不會被收兩次錢?** 典型情境:請求已成功、模型生成完畢、最後一個網路環節斷了,客戶端拿到的是逾時。重試時前一次呼叫往往已經計費。

對策分兩檔。小請求(幾百 token 以內)損失可忽略,不用處理。大任務(長文生成、批次處理)使用冪等鍵:請求帶同一個 key,伺服器端對重複 key 直接回傳已有結果而不是重算。部分 API 原生支援(如請求標頭 Idempotency-Key);服務不支援的話,自己存請求指紋(輸入雜湊 + 參數雜湊)到 Redis,TTL 半小時,重試前先查有沒有同指紋的結果。

熔斷和降級

呼叫多個模型服務時,加個熔斷器:滑動視窗(例如 1 分鐘)錯誤率超過 50% 就斷開,新請求直接走降級路徑,每 30~60 秒放一個試探請求檢查恢復。使用 pybreaker 或 Resilience4j,自己寫也就十行程式碼。

串流介面實用的降級順序:重試失敗 → 切換備用小模型並在結果裡標註 → 仍失敗回傳快取的保守答案(標註快取來源)→ 仍失敗明確告訴使用者服務繁忙並回傳 503。不要讓呼叫方無限等待一個不存在的串流。

最後一個容易被忽略的細節:統計「次數」而不是「請求」。某介面成功率 90%,失敗全被靜默重試救了,監控會顯示 100%。真出故障時你分不清正常抖動和服務整體掛了。日誌裡保留 attempt 次數,監控裡分「首次成功率」和「最終成功率」兩條線,平時看起來差不多,出事時這條差值就是你最需要的警報信號。

留言區

歡迎分享你的想法!

發表留言

0/500

載入留言中…