2.1.219 之前,Claude Code 裡每一個被安全分類器標記的 Fable 5 請求,都會一律改投供應商預設的那個 Opus 模型,連 Opus 5 自己被標記時都沒有備援可去。這是官方 model-config 頁寫的舊行為。

從這裡走到今天(11 月 4 日,我本機 claude --version 印出 2.1.318 (Claude Code)),這個機制被改過好幾輪。我想照版本順序把它排一遍,因為它現在的樣子是一層層補出來的,順序不排清楚,很容易把它跟另一個長得很像的設定搞混。

2.1.219:從「一律」變成「看哪一類」

文件的說法是:Fable 5.1、Fable 5、Opus 5.5、Sonnet 5.5、Opus 5 這幾個模型,會帶著安全分類器跑,分類器「most often」標記資安與生物類內容。most often 只是最常見,這兩類之外也可能被標記,文件沒有給完整清單,標記實際上抓什麼我沒辦法多說。

2.1.219 起,備援改成依「誰拒絕、哪一類」決定去哪:

  • Fable 5.1、Fable 5、Opus 5.5:生物類被標記,重跑在 Opus 5;資安類被標記,重跑在 Opus 4.8。
  • Sonnet 5.5:資安類重跑在 Sonnet 5;生物類沒有備援模型,直接以拒絕結束。
  • Opus 5:資安類重跑在 Opus 4.8;生物類同樣沒有備援,直接拒絕。

這張分派表有個容易看漏的後果。在 Fable 5.1、Fable 5、Opus 5.5 上做生物領域的工作,第一個被標記的請求會把整個 session 移到 Opus 5;之後再有生物類請求被標記,Opus 5 沒有生物備援,就以拒絕結束。以上是我對文件引文的轉述。Opus 5 與 Sonnet 5.5 則是第一個被標記的請求就拒絕。

切換發生時,主對話的 transcript 會顯示一則通知;切過去之後,session 就停在備援模型上,要回原本的模型得自己跑 /model。拿客服轉接當比方:這不是電話占線換條線,是承辦人說「這案子我不能接」,轉給另一位同事,之後整串對話都留在那位同事手上。這個比方只用來記「換了就留下」,後面還有一條線要跟它對照。

備援之後的 effort 也值得提一下:保留被標記當下請求的 effort。文件舉的例子是 Opus 5.5 預設 medium,備援到 Opus 4.8 仍是 medium,雖然 Opus 4.8 自己的預設是 high。

備援鏈:另一條線

另一個機制叫備援鏈,設定是 fallbackModel,旗標是 --fallback-model。我本機 claude --help 篩出來的原始輸出長這樣(help 的換行把後半句截斷了,我沒讀到被截掉的部分):

1
2
3
--fallback-model <model>              Enable automatic fallback to specified
-p, --print Print response and exit (useful for
--safe-mode Start with all customizations

備援鏈處理的是另一種狀況:主模型過載、不可用,或回傳其他不可重試的伺服器錯誤。驗證、帳務、rate-limit、請求大小、傳輸錯誤,以及組織政策檢查拒絕,都不會觸發它。它的切換只持續當前這個 turn,下一則訊息會再先試主模型,最多三個模型。

照剛才的比方,這是電話占線,換一條線打這一通,下一通還是先撥主線。

兩者的對照關鍵是「換了留不留」:內容備援留下來,備援鏈只管一個 turn。文件原文寫明兩節互相參照,而備援鏈那一節沒有任何一句提到被標記的請求,所以兩者的觸發條件是分開的。

備援鏈在 2.1.247 之前,遇到鏈涵蓋的失敗,subagent 會直接結束;2.1.247 起改成 subagent 依序試你設的備援。這一條只屬於備援鏈,跟內容備援無關。

2.1.312(10 月 27 日):連不認得的模型名稱也算

第三方供應商(Bedrock、Agent Platform、Foundry)的模型 ID 因供應商而異,所以 model-config 頁寫的是:只有 Claude Code 能辨識涉及的每個模型時才會備援,辨識不了就不切換,被標記請求以拒絕訊息結束,要自己用 /model 切換後重試。

2.1.312 的 changelog 寫的是另一個方向:自動切換也涵蓋 Claude Code 不認得的模型名稱,例如自訂部署名。這兩句我也沒有查出怎麼對上,先並排放著。同一條 changelog 還說,設 CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK=1 可以把它們關掉。

我在 changelog 裡讀到提到這個變數的,就是這一條。它最早出現在哪個版本,我只讀了 2.1.305 到 2.1.318 這一段,之前的我沒讀,不知道。env-vars 頁的說明是:設為 1 會關閉分類器標記請求時的自動換模型,也就是 switchModelsOnFlag 控制的那個行為。

1
2
3
{
"switchModelsOnFlag": false
}

這是文件列的設定鍵,布林值,/config 裡顯示為「Switch models when a message is flagged」,選項是 Switch automatically 與 Ask each time。true 切到備援模型繼續;false 在互動 session 裡暫停,讓你選「切換」或「改 prompt 重試」;沒有對話框可以顯示的地方,例如 -p,被標記的請求直接以錯誤結束。預設是不設,Claude Code 自動切換,不過互動 session 裡它可能先問你一次,選「留在目前模型」的話,存下來的值就是 false。

CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK=1 設下去之後,被標記的請求是暫停詢問,還是直接拒絕?env-vars 那一列只寫「turn off the automatic model switch」,沒回答這個問題,我不補答案。它跟 switchModelsOnFlag 同時設時誰優先,我也沒查到。

2.1.317(11 月 3 日):subagent 的說法對不上了

昨天發的 2.1.317 改了一件跟 subagent 有關的事。changelog 的原句是:把 subagent 改成在「Switch models when a message is flagged」設為「Ask each time」時停止,而不是切換模型。

但 model-config 那頁,在 Ask each time 的例外清單裡,是這樣寫 subagent 的:不顯示提示,被標記且會切換模型的請求直接在備援模型上重跑。

兩句並排,一個說停止,一個說在備援上重跑。我沒有解開這個矛盾,也沒有觸發過任何被標記的請求,所以沒辦法替它們裁決。文件頁可能還沒跟上 2.1.317,也可能兩句講的是不同情境,這兩個猜測都沒有證據。在這件事釐清之前,要在 Ask each time 底下派 subagent 處理敏感內容的人,別預設它會怎麼走。

同一份例外清單還有幾項我照原文列:被標記的類別沒有備援模型時不顯示提示、直接拒絕;兩個模型都標記同一個請求時,可以改 prompt 重試或開新 session;非互動模式與無法顯示提示的 SDK 整合,被標記請求以拒絕結束該 turn;備援目標被 availableModels 擋住時,不顯示提示,也以拒絕結束。

我的立場,與會讓我改變看法的條件

我只讀了 changelog 的 2.1.305 到 2.1.318 這一段,所以上面只是這一段的軌跡,不是完整歷史。我的立場是:無人值守的 -p 流程,我會明寫 switchModelsOnFlag: false,讓被標記的請求報錯,也不要它悄悄把整條流程換到別的模型上。這是我的偏好,文件只給事實,沒有評價哪種比較好。

會讓我改主意的條件是:如果你的工作本來就大量碰資安題材,每次被擋都停下來會讓流程整個卡死,那自動切換反而是對的選擇。另外,等 subagent 那兩句說法對齊、而且 CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK 關閉後的行為文件寫明,我也會重新評估這個預設。

這篇所有備援行為都只讀文件,沒有實際觸發過。transcript 的通知長什麼樣、/config 的選項怎麼顯示、false 之後的暫停對話框、subagent 實際怎麼走,我都沒看過。本機只跑過 claude --version 與 claude --help 篩出的上面三行。懷疑是自己的 CLAUDE.md 或 hooks 惹的,文件建議用 claude --safe-mode 開 session 排除,這一段我也沒實跑過。

下一步最該盯的是那條矛盾:如果下個版本的 changelog 或 model-config 頁其中一邊改了措辭,我們才會知道 subagent 到底怎麼走。API 層的 refusal 與 fallback 是另一套,欄位與行為都不同,那一篇的切角在這裡。

參考來源:Model configuration(Automatic model fallback、Fallback model chains)、Settings reference、環境變數、Changelog(2.1.312、2.1.317)