前言:沒有人準備「它不能用」的那一刻
我們花很多時間討論 AI 能做什麼、怎麼用得更好、哪個模型比較強。
幾乎沒有人準備一件事:它突然不能用的時候,你怎麼辦。
不是「回答得不好」,是「完全叫不動」。而且是在你最需要它的那一刻。
這篇文章記錄一次真實的狀況:一場需要即時互動的活動,開場前 48 分鐘,背後的模型服務回傳配額用盡。從發現到恢復共約 30 分鐘。我把整個排錯過程完整寫下來,包括我一開始判斷錯的那一段。
補一句背景:這次排錯實際動手的是我的 agent,我從頭到尾在旁邊看著。下文的「我」包含這段協作——包括判斷錯的那一步,我當下也跟著信了。
一、症狀:兩個看起來無關的異常
事情不是從錯誤訊息開始的,是從「怪怪的」開始的。
我在例行檢查日誌時看到兩件事:
第一件,錯誤層級的訊息:
ERROR pool error in dispatch_batch: pool exhausted
INFO pool full, suspending oldest idle session
連線池滿了,系統正在不斷把舊的連線踢掉換新的。
第二件,回應時間異常:
使用者 A 387,289 ms = 6 分 27 秒
使用者 B 299,965 ms = 5 分 00 秒
使用者 C 270,801 ms = 4 分 31 秒
正常應該是 17~30 秒。現在要 4 到 6 分鐘。
二、第一個判斷:錯的
我的第一反應是:連線池滿了,所以大家在排隊,所以慢。
這個推理完全通順。池子有上限、上限到了、新的請求要等舊的釋放、於是每個人的等待時間拉長。
我甚至已經開始查連線池的設定,想看看上限能不能調大。
這是錯的。
不是因為推理有問題,是因為我把「同時出現的兩個症狀」直接假設成因果關係。連線池滿和回應變慢確實同時發生,但它們不是 A 導致 B,而是同一個更深的原因導致了 A 和 B。
如果當時直接把連線池上限調大,會發生什麼?池子不會滿了,但每個請求還是要等 4 到 6 分鐘——因為真正的瓶頸根本不在池子。而我會浪費掉最寶貴的那 20 分鐘。
三、真因:一則被忽略的錯誤訊息
轉折點是一則完整的錯誤內容:
429 · Too many tokens per day, please wait before trying again
Available Model Group Fallbacks = None
每日 token 配額用盡。而且沒有設定任何備援模型群組。
到這裡,前面兩個症狀立刻串起來了:
- 每個請求都撞到配額限制 → 觸發重試機制 → 一個請求要跑好幾分鐘才放棄或成功 → 這是回應變慢的原因
- 請求跑得久 → 連線遲遲不釋放 → 這是連線池爆掉的原因
一個根因,兩個症狀。而我差點去修那個症狀。
教訓一:同時出現的異常不一定互為因果,更常見的是它們共有一個上游原因。修症狀之前,先找那個共同上游。
四、排除:把所有可能的路一次測完
確認根因之後,剩下的問題只有一個:還有哪條路可以走?
這時候最忌諱的是一條一條慢慢試。時間是最稀缺的資源,我的做法是把所有候選路徑一次列出來、一次測完:
| 候選 | 結果 |
|---|---|
| 原本使用的主力模型 | ❌ 配額耗盡,實測回 429 |
| 同服務商的另一顆同級模型 | ❌ 模型識別碼已失效 |
| 同服務商的輕量模型 | ❌ 該版本已終止服務 |
| 換另一家服務商 | ❌ 環境裡沒有可用的憑證 |
| 同服務商的開源模型 | ✅ 實測有回應 |
五條路,只有一條通。
這裡有個關鍵細節:我不是「看設定檔判斷哪個可用」,而是對每一顆模型實際送一個測試請求。
如果只看設定檔,前三顆看起來都是註冊好、可以用的。但實際送出去才知道兩顆的模型識別碼已經失效——設定檔不會告訴你這件事。
教訓二:判斷「這條路通不通」,唯一可信的方法是實際走一次。設定檔、文件、以及你的記憶,都只能用來產生候選清單。
五、切換:改一行,然後接受代價
幸運的是,當初的設定把模型名稱抽成了單一參數。切換是改一行、重啟服務。
不幸的是,能用的那顆是開源模型,能力跟原本的主力模型有明顯落差,而當天的任務高度依賴工具使用能力。
這裡我做了一個判斷:先讓它能動,再擔心它動得好不好。
因為「慢但能用」和「完全不能用」之間的差距,遠大於「好用」和「堪用」之間的差距。
同時我做了三件配套:
- 備份原設定,並確認還原是一行指令的事——配額每日重置,隔天要能立刻切回去
- 明確告知現場負責人能力落差,以及最可能出問題的環節,讓他有心理準備而不是在現場才驚訝
- 準備降級方案:如果新模型連基本任務都跑不動,當場改成示範模式
第三點最後沒有用上,但準備它的成本是十分鐘,不準備的代價是現場沒有 B 計畫。
六、事後:三個該寫進清單的東西
1. 活動前要檢查的不只是「服務活著」
我的開場前檢查清單裡有「服務健康」這一項,它當天是綠燈——容器在跑、健康檢查通過、服務也確實回應請求。
清單裡沒有的是「配額餘量」。
服務活著和服務可用是兩件事。一個配額用盡的服務,健康檢查會全綠,但它什麼也做不了。
新增規則:需要即時互動的活動,前一天要檢查配額餘量,不只檢查服務狀態。
2. Fallbacks = None 是一個該被當成警報的設定
那則錯誤訊息裡最重要的一句,其實不是「配額用盡」,是後面那行:
Available Model Group Fallbacks = None
沒有備援。 這代表主力一掛,整條路就斷了,沒有任何自動降級。
這個設定不是當天才變成 None 的,它一直都是 None。只是在那之前,沒有人需要它。
新增規則:任何單點依賴的外部服務,「沒有 fallback」本身就該是一個要被明確接受或修正的決定,而不是預設值。
3. 降級路徑要在平時就測過
我當天是在壓力下第一次測那顆備援模型。它剛好能用,是運氣。
降級路徑如果沒有在平常測過,它就不是降級路徑,只是一個你以為存在的選項。
結語:AI 維運跟傳統維運沒有不同
這次事件裡沒有任何一個教訓是 AI 專屬的。
同時出現的症狀不一定互為因果、判斷可用性要實測、單點依賴要有備援、降級路徑要平時演練——這些是維運的基本功,已經存在幾十年了。
差別只在於:因為 AI 用起來太順、太像對話,我們很容易忘記它背後是一個有配額、有速率限制、會失效、會終止服務的外部依賴。
你不會把公司的核心流程押在一個沒有 SLA、沒有備援、沒人監控餘量的第三方 API 上。
但如果那個 API 剛好長得像聊天視窗,我們就會。