🔗 資料來源

在大型語言模型的實際應用中,當我們以為配額相當充足時,卻時常會意外撞上 「429 RESOURCE_EXHAUSTED」 的錯誤牆。本篇筆記將帶您深入剖析這個令人困惑的現象,並提供具體的架構調整與 Python 實踐範例。

📋 目錄

🧐 現況矛盾:為何配額沒滿卻報錯?

在我們正式環境的 prod-enterprise-rd 專案中,負責「證件圖片辨識」與微調模型預測的服務,近期頻繁遭遇以下錯誤:

💡 錯誤訊息google.genai ClientError: 429 RESOURCE_EXHAUSTED (Resource exhausted. Please try again later.)

奇怪的是,在控制台上觀察到的系統限制用量僅為 0.02% (例如 us-central1 圖片輸入 RPM 實際僅 7,791,遠低於上限 4,025 萬)。而 Token 配額顯示為 「無限制」 ,每分鐘輸入約 1.6 萬 Tokens。這顯示 「瓶頸不在專案配額」 ,而是來自更深層的架構機制。

🏗️ 根因解析:動態共用配額 (DSQ)

Gemini 2.0+ 預設採用了 DSQ (Dynamic Shared Quota) 架構。有別於傳統的固定配額,DSQ 是將「模型 × 區域」的資源視為全球所有客戶共用的一大池子。

  • 傳統固定配額:自己的用量到頂才會發生 429,控制台可見百分比,且可逐步申請擴充。
  • DSQ 架構:只要共用池子無餘裕即拒絕請求,與自身專案用量無關。控制台顯示無限制,因此無法提前預警。

💡 重點提示:429 錯誤來自「共用容量池」的瞬時吃緊,配額頁面永遠不會反映這件事。

以下是 DSQ 架構下發生 429 錯誤的流程圖解:

graph TD A1[客戶端發出請求] --> B1{共用池是否有餘裕} B1 -- 有餘裕 --> C1[請求成功] B1 -- 無餘裕 --> D1[拋出 429 錯誤] D1 --> E1[控制台無預警]

🌩️ 何時最容易撞上 429 錯誤?

根據日誌分析,這些 429 錯誤具備 「間歇性、無規律」 的特徵(可能這一秒失敗,下一秒就成功),且多發生在以下情境:

  1. 熱門模型:例如使用 2.5 Flash 等競爭激烈的當紅模型,即使量小也不代表安全。
  2. 熱門區域:例如我們正在使用的 us-central1 是最擁擠的區域之一。
  3. 尖峰時段:美國上班時間全球需求最高,池子餘裕以秒為單位劇烈波動。
  4. 大請求:長 context 單次需一次取得大量容量,比小請求更容易被拒絕。

⚖️ 官方 SLA 觀點與解法比較

在「即付即用」現況下,官方將這類 429 視為正常的資源排擠,不計入 SLA 錯誤率 ,並建議客戶可自行重試。若要解決此問題,我們有以下幾個選項(由左至右成本遞增):

  1. 流量平緩與退避 (成本零):實作指數退避與抖動重試,對間歇性尖峰最有效。
  2. 多區域分流 (成本低):429 時自動改打其他允許區域的端點(需注意合規邊界,全域端點通常不可用)。
  3. 批次 API (成本更低):將可延後的流量放入佇列排隊,換取彈性。
  4. 佈建輸送量 PT (成本高):購買 GSU 預留吞吐量,這是唯一的容量保證,適合關鍵業務。

⚠️ 合規注意事項:所有架構調整必須以合規邊界為前提。全域端點 (Global) 與跨合規邊界的代理專案,通常因為資料落地 (data residency) 限制而 不可行

💻 Python 程式實踐:指數退避重試

基於上述分析,我們的「第一步」應該是採取零成本的 「指數退避與抖動重試」 。以下提供一段 Python 實作範例,使用 tenacity 套件來優雅地處理 429 錯誤:

import os
import google.generativeai as genai
from tenacity import retry, wait_random_exponential, stop_after_attempt, retry_if_exception_type

# 假設這是我們呼叫 Gemini 的主程式碼
genai.configure(api_key=os.environ["API_KEY"])
model = genai.GenerativeModel('gemini-2.5-flash')

# 攔截 429 錯誤並進行指數退避 (加入隨機抖動避免群聚效應)
@retry(
    wait=wait_random_exponential(multiplier=1, max=60), 
    stop=stop_after_attempt(5),
    retry=retry_if_exception_type(Exception) # 這裡可依據實際 SDK 的 429 Exception 替換
)
def generate_content_with_retry(prompt: str):
    """
    附帶重試機制的 Gemini API 呼叫函式
    """
    print(f"嘗試發送請求...")
    try:
         response = model.generate_content(prompt)
         return response.text
    except Exception as e:
         if "429" in str(e) or "RESOURCE_EXHAUSTED" in str(e):
             print("遇到 429 錯誤,準備進行退避重試...")
             raise e # 將錯誤拋出讓 tenacity 處理重試
         else:
             # 若非 429 則不重試,直接拋出
             raise e

# 測試執行
if __name__ == "__main__":
    prompt_text = "請說明何謂大型語言模型的動態配額?"
    try:
        result = generate_content_with_retry(prompt_text)
        print("成功取得結果:", result)
    except Exception as e:
        print("重試失敗,最終錯誤:", e)

🤖 程式碼提示:加入 wait_random_exponential 也就是俗稱的 Jitter(抖動),可以有效避免多個客戶端在同一時間點集體重試,造成「二次尖峰」癱瘓服務。

🚀 結論與未來進階

面對 Gemini 的 429 錯誤,我們必須先釐清:這不一定是你的專案用完了配額 ,更有可能是撞上了 DSQ 共用池的瞬時天花板。

我們接下來的具體行動方針:

  • 短期 (1-2週):先實作上述的指數退避與抖動重試,並將客戶端流量盡量平滑化。
  • 中期 (本季):若是對即時性要求不高的功能,改走 Batch API;並與資安單位確認後,建立合規邊界內的多區域 failover。
  • 長期:依據業務的關鍵性來評估是否購買 PT (預留輸送量),獲得真正的容量保障。

理解了底層架構,就能以最符合成本效益的方式跨越 429 的障礙,打造出高韌性的 AI 應用!