請求成功了,卻依然是失敗
想像一下,你已經和同一個角色聊了好幾個星期。你熟悉 TA 說話的方式。TA 的語言始終如一。TA 的回覆感覺敏銳又鮮活。
然後,在角色和模型都沒有變動的情況下,有些地方開始不對勁了。
下一則回覆要等十秒才開始出現。一個仰賴結構化輸出的功能突然壞掉。角色開始東拉西扯、夾雜另一種語言的片段,或是用一種彷彿出自某個弱得多的模型的風格回應。
從伺服器的角度來看,什麼都沒有失敗。API 回傳了 200 OK。Token 送達了。這個請求照常計費。
從使用者的角度來看,模型就是變差了。
正是這個落差,讓我們為 Reverie 打造了一套供應商金絲雀探測與路由隔離系統。
同一個模型名稱,許多種服務環境
OpenRouter 是我們主要的模型閘道。它的優勢之一,是同一個模型可以由許多家獨立的推論供應商提供服務。某家供應商無法使用時,另一家可以接手請求。比起依賴單一端點,這讓我們有更多容量、更有競爭力的價格,以及更好的韌性。
但它也帶來一個隱藏的變數。
模型名稱可能保持不變,但提供服務的基礎架構卻可能每次請求都不一樣。每家供應商可能使用不同的推論引擎、硬體配置、量化等級、解析器、聊天範本、佇列,或額外的政策層。
OpenRouter 的供應商路由文件說明,它的預設路由會考量近期的可用性與價格,並以其他供應商作為備援。這些都是重要的訊號——但一個端點可能在線上、價格低廉,卻仍然對某個特定產品產出無法接受的結果。
OpenRouter 也公開寫過提供同一個模型的不同供應商之間,存在可量測的差異。理論上,相同的權重搭配相同的精度,表現應該一致。但在正式環境中,提供大型模型服務是很複雜的事,差異就會浮現。
我們說的「模型降級」是什麼意思
「模型降級」並不是一個標準化的單一診斷,也不必然代表供應商刻意換成了較小的模型。
我們是從實際運作的角度使用這個詞:當一個服務端點在具代表性的提示詞下,已無法維持我們對所請求模型預期的行為、能力或互動效能時,它就是降級了——即使請求在技術上可能是成功的。
對 Reverie 來說,最明顯的幾種形式是:
- 延遲降級——連線成功了,但首 Token 時間拉長到足以破壞即時對話的感覺。
- 能力降級——一個理應支援結構化輸出的端點,回傳格式錯誤的文字,或不再符合 schema。
- 行為降級——回覆變得損毀、語無倫次、出乎意料地重複,或明顯漂移成另一種語言。
- 政策不符——上游主機加上了自己的過濾層,把 Reverie 支援的場景變成拒答、空白回應或被截斷的回覆。
可能的原因有很多。較低精度的量化可能會影響困難的提示詞,OpenRouter 的文件和已發表的量化研究都提到了這一點。但量化並不能解釋一切:推論引擎的 bug、工具解析器、tokenizer 或聊天範本的錯誤、超載的佇列,以及供應商端的中介軟體,都可能同樣重要。OpenRouter 也曾觀察到,特別是在工具呼叫上,解析器的實作往往是比精度本身更大的差異來源。
真正重要的問題不是「哪個原因聽起來最可疑?」,而是「我們能不能鎖定那個端點,並重現這個故障?」
讓問題變得具體的兩個案例
對我們來說,這不是理論上的擔憂。
在一次鎖定供應商的比較中,某家供應商在 20 則回覆中有 19 則的開頭遭到汙染:插入了標點符號,或一段毫不相關的 token 片段。在另外十四家提供同一模型的主機上,這個問題在 49 則回覆中出現 0 次。在角色扮演中,回覆的第一個字元常常是 Markdown 的強調標記,所以一個多餘的位元組,也可能破壞整段回應的格式。
在另一次測試中,某個端點在 3 次生成中有 3 次出現嚴重的葡萄牙語與西班牙語混雜。同樣的提示詞,在模型的官方主機,以及其他使用相同精度等級的受測主機上,都維持使用葡萄牙語。這讓「模型本來就不穩定」成了一個站不住腳的解釋;缺陷是跟著服務端點走的。
這些故障很隱微,因為它們不一定會拋出例外。傳統的運作時間監控看到的是一個健康的 API。使用者看到的,卻是一個突然沒辦法好好說話的角色。
我們的答案:測試每一條路由,而不只是模型
每三十分鐘,我們的金絲雀就會找出目前正在提供 Reverie 預設聊天模型的所有供應商。接著,它會向每家供應商送出一小組合成請求,每個請求都只鎖定該供應商,並停用備援。
鎖定很重要。如果探測請求允許備援,一家健康的第二供應商就可能掩蓋第一家的故障。我們需要確切知道是哪個服務環境產生了這個結果。
系統會測試四項範圍明確、可由機器判定的契約。
1. 首 Token 時間
對串流對話來說,吞吐量只是體驗的一半。第一個 Token 決定了使用者要盯著載入動畫多久。
我們直接從串流中量測。一家供應商如果在十秒內沒有產出任何文字,就無法通過延遲探測。這個門檻刻意設得很寬鬆,而且單一輪變慢並不足以讓主機被移除;暫時性的佇列壓力本來就會發生。
2. 過濾與無聲拒答
我們會送出一段固定的角色扮演續寫請求,它代表 Reverie 所支援的流量類型。探測會檢查結束原因、高信賴度的拒答模式,以及空白輸出。
我們也會對已知會過濾的主機執行這項探測,作為陽性對照。如果某個對照組意外通過了,我們不會因此恭喜其他所有供應商——而是把這個探測本身標記為不可靠。一項已經無法偵測出已知故障模式的測試,不能當作健康的證據。
3. 結構化輸出
我們會要求一個具有固定 schema 的小型物件,並驗證結果。傳輸錯誤和格式錯誤的輸出會被分開處理:連線中斷代表無法連上供應商,而一次成功的生成卻無法符合 schema,則是能力退化的證據。
這個區別很重要。「端點宣稱支援這個參數」和「端點能穩定遵守這個參數」,並不是同一個承諾。
4. 語言完整性
Reverie 支援 17 種介面語言,因此只用英文做的冒煙測試,會漏掉一些使用者實際遇到的故障。
金絲雀會使用合成的角色扮演提示詞,輪流測試我們支援的各個語系。一個在本機執行、結果確定的語言偵測器,會尋找高信賴度的整段回應語言切換、持續的語碼混雜,以及明顯的編碼損毀。它不會把真實對話送去探測,也不會請另一個模型做主觀判斷。
模稜兩可或非常短的輸出會被視為無法判定,而不是失敗。偵測到語言故障時,金絲雀會立刻再執行兩次確認,並以多數結果為準。下一輪也會重複測試同一個語系,這樣供應商就無法只因為輪替換到了別的語言,就逃過連續輪次的規則。
我們刻意讓它不輕信自己的結論
自動移除推論容量很有用,但一次誤判造成的中斷,可能比它原本要解決的缺陷還嚴重。因此,金絲雀設有好幾道煞車:
- 傳輸錯誤不算品質故障。 逾時、速率限制和 5xx 回應只會凍結分數,而不會累加暫停所需的連續失敗次數。
- 一輪失敗還不夠。 供應商必須連續兩輪(每半小時一輪)都失敗。
- 觀察與執行分開進行。 我們可以在觀察模式下執行完整系統,記錄哪些主機會被暫停、通知管理員,並在啟用自動處置之前先量測誤判率。
- 容量有底線。 如果暫停某家供應商會讓正式環境的主機少於兩家,金絲雀就不會暫停它,而是改為通知管理員。
- 恢復也要經過測試。 被暫停的供應商仍然會持續接受探測。連續兩輪全數通過,主機就能提前恢復。
- 重複故障會逐步加重。 第一次事件暫停一天,下一次暫停三天,之後每次七天。連續十四天沒有問題後,違規紀錄就會淡去,暫停階梯也會重設。
我們的目標不是懲罰供應商,而是把使用者流量從一條可重現地不健康的路由上移開,同時在問題修好後,保留一條清楚的回歸路徑。
三層保護
最終的路由決策結合了三種排除項目:
- 經過審查的內建排除項目,針對我們已量測確認、不希望被意外重新引入的缺陷或政策不符。
- 執行階段的事故排除項目,管理員可以立即新增,無需等待部署。
- 金絲雀的限時暫停,針對超過自動故障門檻的供應商。
Reverie 建立請求時,這些清單會合併進 OpenRouter 的 provider.ignore 偏好設定。使用者不必一再重試、等著運氣選中比較好的供應商;不健康的路由會直接被排除在考慮之外。
每一項自動決策都會被記錄下來,發生有意義的狀態轉換時會通知管理員,必要時也可以手動解除暫停。
這套系統能保證什麼——以及不能保證什麼
金絲雀刻意維持很窄的範圍。它可以用程式碼判斷延遲、schema 有效性、高信賴度的過濾、語言完整性以及編碼健康。它不會假裝能評斷一個角色夠不夠有趣、情緒感知夠不夠敏銳,或是否忠於複雜的個性。
這些更廣泛的問題,仍然需要以具代表性的提示詞、人工審查、正式環境的遙測資料所建立的評估,以及——最重要的——使用者回報。
這套系統也不代表每一則奇怪的回覆都證明模型降級了。生成式模型本質上是機率性的。一段很長的對話可能累積互相衝突的上下文。角色設定可能包含彼此競爭的指令。有時候,一則奇怪的回覆就只是一則奇怪的回覆。
改變的是,「同一個模型」不再是我們調查的終點。現在我們可以鎖定服務路由、重現客觀的故障,並讓受影響的流量遠離它。
可靠性,意味著守住體驗
多供應商路由依然很有價值。它讓 Reverie 擁有足夠的容量與韌性,在個別端點故障時仍能讓對話持續運作。
但韌性不只是收到一個 HTTP 回應。一則來得太晚、失去必要結構、拒絕平台支援的內容,或突然切換語言的回覆,並不會因為帳單上寫著請求成功,就變成健康的結果。
對 AI 角色產品來說,可靠性代表的是一件更有人味的事:無論這次是哪一台機器替角色開口,角色都應該還是那個角色。
這就是我們新的路由防護機制要守護的標準。
如果你發現回覆品質或語言突然改變,請把模型名稱和大概的時間回報給我們。這些回報能幫助我們把你遇到的狀況,對應到當時實際提供服務的那條路由。

