拒絕與備援
Claude Fable 與 Claude Opus 模型如何回傳分類器拒絕,以及如何在備援模型上重試被拒絕的請求。
Claude Fable 5.1、Claude Fable 5 與 Claude Opus 5 內建可拒絕請求的安全分類器(safety classifiers)。發生這種情況時,您會收到一個正常的回應(而非錯誤),其中帶有 stop_reason: "refusal"。其 stop_details.category 會指出政策領域(請參閱拒絕的樣貌)。通常您仍可透過將相同請求傳送至另一個 Claude 模型來取得答案。本頁說明如何辨識拒絕,以及如何設定該重試。
當您以上述任一模型進行開發,並希望被拒絕的請求能自動轉交給另一個模型時,請閱讀本頁。若您已在回應中看到 "refusal" 並想知道下一步該怎麼做,本頁同樣適用。
相關頁面:
- 停止原因與備援:
stop_reason值的完整清單。 - 備援抵免:當您自行建構重試時,如何避免重複支付提示快取成本。
- SDK 中介軟體:封裝上述所有功能的 SDK 輔助工具。
- 備援與計費 cookbook:一個完整的端對端範例。
最簡單的設定(在 Claude API 上為 beta):將 fallbacks 設為 "default",API 便會在 Anthropic 針對該拒絕類別所建議的備援模型上重試被拒絕的請求。對於沒有建議備援的類別,拒絕維持不變。
client = Anthropic()
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks="default",
betas=["server-side-fallback-2026-07-01"],
)
print(response.model)以下各節說明拒絕回應包含哪些內容、何時使用伺服器端或用戶端備援,以及各自的計費方式。
拒絕的樣貌
拒絕是一個成功的 HTTP 200 回應,帶有 stop_reason: "refusal":
{
"id": "msg_01XFUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-fable-5",
"content": [],
"stop_reason": "refusal",
"stop_details": {
"type": "refusal",
"category": "cyber",
"explanation": "This request was declined because it could enable cyber harm."
},
"usage": {
"input_tokens": 412,
"output_tokens": 0
}
}stop_details 物件說明拒絕的原因:
category: 指出觸發分類器的政策領域。explanation: 人類可讀的描述。此文字並不穩定,因此請直接顯示而非解析它。recommended_model: 僅出現在設定了fallbacks的請求上(伺服器端備援,beta)。當 API 略過備援嘗試時(例如備援模型受到速率限制),它會指出可直接重試的模型,否則為null。這是一個提示,而非保證。- 當拒絕無法對應到具名類別時,
category與explanation皆為null。該null是正常且永久的值,而非佔位符。 - 對於
refusal以外的所有停止原因,stop_details本身為null。
category | 含義 |
|---|---|
"cyber" | 該請求可能促成網路危害,例如惡意軟體或漏洞利用程式開發。良性的網路安全工作也可能觸發此類別。 |
"bio" | 該請求可能促成生物危害,例如危險的實驗室方法。有益的生命科學工作也可能觸發此類別。 |
"frontier_llm" | 該請求可能協助開發競爭性 AI 模型,這在 Anthropic 的商業條款下受到限制。良性的機器學習工作也可能觸發此類別。 |
"reasoning_extraction" | 該請求要求模型在回應文字中重現其內部推理。若要以結構化形式取得推理,請改用自適應思考。 |
"general_harms" | 該請求屬於上述四個具名類別以外的使用政策領域。良性工作也可能觸發此類別。 |
拒絕可能在任何輸出之前到達,也可能在部分輸出後於串流中途到達。無論哪種情況,請將任何部分輸出視為不完整並捨棄。
選擇備援方式
有三種方式可在另一個模型上重試被拒絕的請求。合適的方式取決於您的執行環境以及您需要多少控制權。
| 您的情況 | 使用 | 原因 |
|---|---|---|
| Claude API,最簡單的設定 | 伺服器端備援 | 一個請求,一個回應。API 處理重試。 |
| 任何平台,使用 Anthropic SDK | SDK 中介軟體 | 在用戶端設定一次。重試自動進行。 |
| 原始 HTTP 或自訂重試邏輯 | 手動重試搭配備援抵免 | 完全控制。備援抵免可降低成本。 |
伺服器端備援與 SDK 中介軟體會為您套用備援抵免。只有在您自行建構重試時,才需要參閱備援抵免頁面。
伺服器端備援
伺服器端備援(server-side fallback)在單一 API 呼叫內重試被拒絕的請求。在預設模式下,當主要模型拒絕且該拒絕類別有建議的備援時,API 會在 Anthropic 針對該類別建議的模型上執行相同請求。您也可以改為自行指定最多三個備援模型。無論哪種方式,您都會收到一個指出回答模型的回應,因此您的使用者在一次往返中即可獲得答案。
發出請求
將 fallbacks 參數設為字串 "default",並傳送 server-side-fallback-2026-07-01 beta 標頭。API 隨後會套用所請求模型的伺服器定義預設路由,該路由會根據分類器回報的拒絕類別選擇建議的備援模型,因此被拒絕的請求可獲得服務,而您無需在建議變更時維護模型清單。
預設路由絕不會因您未選擇的模型而引發預先的過大圖片拒絕:若某個路由模型會縮放標記為 "oversized_image": "error" 的圖片,該模型會從路由中移除,因此已標記的圖片絕不會以縮放後的形式提供。
client = Anthropic()
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks="default",
betas=["server-side-fallback-2026-07-01"],
)
# usage.iterations 中出現 fallback_message 項目表示已執行備援模型;
# 請搭配 stop_reason 確認該回應是否由備援模型提供。
fallback_ran = any(
iteration.type == "fallback_message"
for iteration in response.usage.iterations or []
)
served_by_fallback = fallback_ran and response.stop_reason != "refusal"
print(
json.dumps(
{
"stop_reason": response.stop_reason,
"model": response.model,
"served_by_fallback": served_by_fallback,
}
)
)Anthropic 依據模型的能力,為每個模型及每個政策類別個別設定防護措施:視類別而定,被標記的請求可能備援至能力較低的模型,或被拒絕。"default" 模式為您編碼了這些依模型、依類別的建議,因此被拒絕的請求會在 Anthropic 針對該類別建議的模型上重試。無論哪種方式,備援都是可見的:回應會指出提供服務的模型,而 fallback 內容區塊會標記交接點。
路由在伺服器端套用,且不會在 Models API 上依模型公布。若要查看哪個模型服務了被拒絕的請求,請檢查回應的頂層 model 欄位,並在 usage.iterations 中尋找 fallback_message 項目,如本頁範例所示。
只有安全分類器的拒絕會觸發備援。所請求模型上的速率限制、過載或伺服器錯誤會原樣回傳給您。
自行指定備援模型
除了預設路由之外,您也可以將 fallbacks 設為最多三個模型的清單。當所請求的模型拒絕時,API 會在相同請求上執行鏈中的下一個模型。當您想精確控制由哪些模型服務被拒絕的請求時(例如固定使用您的應用程式已驗證合格的模型),請使用此形式。
具名的備援模型會計入過大圖片檢查:若請求的圖片區塊設定了 "oversized_image": "error",則會預先針對所請求的模型及每個具名備援進行檢查,只要其中任一模型會縮放該圖片,請求即被拒絕,且拒絕所回報的縮放目標適用於所有這些模型。
醒目標示的行是與預設路由請求唯一的差異。
client = Anthropic()
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks=[{"model": "claude-opus-4-8"}],
betas=["server-side-fallback-2026-07-01"],
)
print(response.model)fallbacks 清單適用以下幾項規則:
- 項目依序嘗試。每個項目必須與其他項目及所請求的模型不同。
- 每個項目必須是所請求模型的允許目標之一。設定 beta 標頭後,該清單會以
allowed_fallback_models公布於 Models API 中該模型的項目上。 - 每個項目指定一個
model,並可僅針對該次嘗試覆寫max_tokens、thinking、output_config與speed。 - 該請求必須能作為對每個具名模型的直接請求而有效。若某個備援模型不支援請求所使用的功能,API 會預先拒絕該請求。
- 與預設模式相同,只有安全分類器的拒絕會觸發備援。所請求模型上的速率限制、過載或伺服器錯誤會原樣回傳給您。
- 若備援模型受到速率限制或過載,則不會進行備援嘗試,而是回傳先前的拒絕。該拒絕的
stop_details.recommended_model隨後會指出可直接重試的模型。請依您預期的拒絕量來規劃備援模型的速率限制,否則在負載下備援會退化為拒絕。
兩種模式下的回應結構相同:服務該輪次的模型出現在頂層 model 欄位,fallback 內容區塊標記交接點,而 usage.iterations 記錄每次嘗試。
回應包含的內容
回應看起來與任何其他訊息相同,但有兩項新增:
- 頂層
model欄位回報產生所回傳訊息的模型,無論是所請求的模型還是備援模型。 fallback內容區塊標記content中一個模型的輸出交接給下一個模型的每個位置:{"type": "fallback", "from": {"model": ...}, "to": {"model": ...}}。- 當拒絕的那一跳是所請求的模型時,
from.model會回顯您傳送的模型字串。 to.model永遠是接續模型的已解析 ID。
- 當拒絕的那一跳是所請求的模型時,
在任何輸出之前發生拒絕時,fallback 區塊是第一個內容區塊。例如,當預設路由針對該拒絕類別選擇 Claude Opus 4.8 時:
{
"id": "msg_01XFUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [
{
"type": "fallback",
"from": { "model": "claude-fable-5" },
"to": { "model": "claude-opus-4-8" }
},
{ "type": "text", "text": "Hi! How can I help you today?" }
],
"stop_reason": "end_turn",
"stop_details": null,
"usage": {
"input_tokens": 412,
"output_tokens": 264,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0,
"iterations": [
{
"type": "message",
"model": "claude-fable-5",
"input_tokens": 535,
"output_tokens": 0,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0
},
{
"type": "fallback_message",
"model": "claude-opus-4-8",
"input_tokens": 412,
"output_tokens": 264,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0
}
]
}
}usage.iterations 陣列記錄每次嘗試。拒絕的模型以一般的 message 項目出現,而服務該輪次的模型以 fallback_message 項目出現。若鏈中每個模型都拒絕,回應即為最後一個模型的拒絕,每個較早的跳點各有一個 message 項目,最後一個則為 fallback_message 項目。
黏性路由可將後續輪次直接傳送至備援模型。這樣的輪次不帶 fallback 內容區塊,因為該輪次沒有模型拒絕。請透過 usage.iterations 中的 fallback_message 項目、所請求模型沒有 message 項目,以及回應的 model 欄位來辨識它。
延續對話
在下一輪次,請將助理內容依您收到的原樣傳回。在輸出中途備援之後,content 可能包含拒絕模型在交接前產生的區塊類型。下表說明當您回顯該輪次時,哪些應保留、哪些應捨棄。
| 區塊類型 | 在下一輪次 |
|---|---|
fallback | 精確保留在其出現的位置。API 使用其位置來驗證其周圍的思考區塊,因此若該區塊被省略或移動,回顯邊界兩側思考區塊的請求會被拒絕。 |
text | 保留。 |
最後一個 fallback 區塊之後的任何區塊 | 保留。 |
最後一個 fallback 區塊之前的 thinking、redacted_thinking 或 connector_text | 捨棄。 |
最後一個 fallback 區塊之前的用戶端 tool_use | 捨棄。 |
最後一個 fallback 區塊之前的 server_tool_use | 與其結果配對時保留。沒有對應結果時捨棄。 |
串流
在串流(streaming)請求上,重試在同一個串流上進行,且您已收到的任何內容都不會失效。您看到的內容取決於拒絕發生的時間點。
當拒絕發生在任何輸出之前:
message_start指出備援模型,且fallback區塊是第一個內容區塊。- 由於
message_start會等待備援嘗試開始,首位元組時間包含被拒絕的嘗試。
當拒絕發生在輸出中途:
- 開啟中的內容區塊會關閉,而
fallback區塊(一對沒有 delta 的一般content_block_start與content_block_stop)標記邊界。 - 備援模型從部分輸出接續。只有部分輸出的
text區塊會作為上下文傳遞給備援模型。其他區塊類型保留在content中。 message_start已指出所請求的模型,因此請從fallback區塊的to.model以及最終message_delta的usage.iterations中的fallback_message項目讀取服務模型。
非串流回應
在非串流請求上,輸出中途的拒絕行為不同:回應會省略被拒絕模型的部分輸出,而備援模型從頭回答。結果看起來像是在任何輸出之前的拒絕,fallback 區塊位於最前。被拒絕的嘗試及其輸出 token 仍會出現在 usage.iterations 中。
計費與速率限制
在產生任何輸出之前即拒絕的嘗試不會計費:其 token 會回報於其 usage.iterations 項目上但不收費。每個產生了輸出的嘗試(包括在回應中途拒絕的嘗試)都會依執行它的模型費率分別計費。usage.iterations 陣列是您被計費內容的逐次嘗試記錄。頂層 usage 計數僅描述產生所回傳訊息的那次嘗試。來自不同模型的 token 絕不會加總到同一個欄位中。
每次執行的嘗試(包括拒絕的嘗試)都會計入其自身模型的速率限制。
黏性路由
對話備援之後,API 會記錄由哪個模型服務。該對話後續包含 fallbacks 的請求會直接送往該備援模型,而不執行所請求的模型。這可避免在每一輪次為可預期會再次被拒絕的嘗試付費。
路由決策的幾項特性:
- 保留約 1 小時,且範圍限於您的組織。
- 以對話前綴的內容雜湊加上服務它的模型來儲存。訊息內容本身不會被儲存。
- 屬於盡力而為,因此您的程式碼必須能處理所請求的模型在任何時候被再次嘗試的情況。
黏性路由(sticky routing)同時適用於串流與非串流請求。在串流請求上,路由決策在串流開啟前做出,因此 message_start 事件的 model 欄位已帶有備援模型的 ID。
搭配 SDK 中介軟體的用戶端備援
每個 Anthropic SDK 都包含拒絕備援中介軟體(middleware)。您在用戶端以備援模型清單設定一次。透過 client.beta.messages 的呼叫隨後會在任何平台上自動重試被拒絕的請求。該中介軟體也會在其處理的每個請求上傳送 fallback-credit-2026-07-01 beta 標頭,因此重試會重新計價而無需逐請求設定。
設定方式
將中介軟體傳遞給用戶端建構函式,並在一個對話的各請求之間共用一個 BetaFallbackState 實例。
from anthropic import Anthropic, BetaFallbackState, BetaRefusalFallbackMiddleware
# 遇到拒絕時,中介軟體會改用所列的備援模型重試,並且
# 自動在其處理的每個請求上傳送 fallback-credit beta 標頭。
client = Anthropic(
middleware=[BetaRefusalFallbackMiddleware([{"model": "claude-opus-4-8"}])],
)
state = BetaFallbackState() # pins follow-ups to the model that accepted
# 串流:遇到拒絕時,中介軟體會改用備援模型重試,並且
# 將其事件接續到已開啟的串流上。
with (
state,
client.beta.messages.stream(
max_tokens=1024,
model="claude-fable-5",
messages=[{"role": "user", "content": "Hello, Claude"}],
) as stream,
):
for text in stream.text_stream:
print(text, end="", flush=True)
final_message = stream.get_final_message()
print(f"\nserved by: {final_message.model}")
# 非串流:重複使用該狀態可讓對話保持固定。
with state:
message = client.beta.messages.create(
max_tokens=1024,
model="claude-fable-5",
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(f"served by: {message.model}")行為方式
- 重試依序走訪您的備援清單。本身也拒絕的備援模型會將請求傳遞給下一個項目。
- 當清單中每個模型都已拒絕時,中介軟體會回傳最終拒絕(最後一個模型的拒絕回應),而非拋出錯誤。
- 來自 Claude Fable 5.1 或 Claude Fable 5 的思考區塊會原樣通過。每次重試都會重新傳送您的原始請求主體,而中介軟體在後續請求中從對話歷史移除的唯一區塊,是它自己新增的
fallback邊界區塊。備援模型無法讀取 Claude Fable 5.1 區塊,這些區塊僅為該模型或更新的模型保留,因此 API 會捨棄它們。 - 透過中介軟體服務的回應在每個模型邊界包含一個
fallback內容區塊,與伺服器端備援回應相同。中介軟體會在後續請求中為您管理這些區塊。 - 接受請求的模型會記錄在
BetaFallbackState中,因此共用該狀態的後續請求會固定使用它,而非重新詢問已拒絕的模型。
自行撰寫重試
透過原始 HTTP 或自訂重試邏輯,實作中介軟體所封裝的模式:
偵測拒絕
檢查回應是否有
stop_reason: "refusal"。在備援模型上重新傳送
將
model設為備援模型(例如 Claude Opus 4.8)後傳送相同請求。另一個模型通常可以服務 Claude Fable 5.1 或 Claude Fable 5 拒絕的請求。您如何處理對話歷史取決於您是否兌換備援抵免:- 不兌換抵免: 您可以保留較早的
thinking與redacted_thinking區塊,或將其移除以節省輸入 token。無論哪種方式,備援模型都無法使用它們:它會忽略 Claude Fable 5 區塊,而 Claude Fable 5.1 區塊僅為該模型或更新的模型保留,因此 API 會捨棄它們。 - 兌換抵免: 原樣傳送主體,因為兌換要求精確相符。伺服器會在兌換時處理較早模型的思考區塊,因此請勿移除它們(請參閱必須與被拒絕請求相符的欄位)。
- 不兌換抵免: 您可以保留較早的
留在備援模型上
對於多輪對話,後續輪次請繼續使用備援模型,而非切換回去。
手動重試會從頭寫入備援模型的提示快取,這比讀取現有快取的成本更高。備援抵免會退還該成本;請在您自行建構的每次重試上兌換它。
Message Batches 中的拒絕
Message Batch 中被拒絕的請求會以 result.type: "succeeded" 搭配 stop_reason: "refusal" 回傳。批次結果帶有與同步回應相同的 stop_details 物件,因此您可以透過 stop_reason 或 stop_details.type 偵測拒絕。一項差異:批次拒絕不會產生備援抵免,因此批次結果上的 stop_details 絕不會包含 fallback_credit_token。
伺服器端備援不適用於批次(包含 fallbacks 的批次請求會產生逐項目的錯誤結果)。若要重試被拒絕的批次項目:
- 從結果中收集被拒絕的項目。
- 從任何多輪歷史中移除 Claude Fable 5.1 或 Claude Fable 5 的思考區塊。
- 以新批次或直接請求的形式在備援模型上重新提交它們。
常見陷阱
- 在不同的模型上重試。 將被拒絕的請求重新傳送至同一模型通常會再次被拒絕。請將重試指向備援模型。
- 依請求而非依輪次或依工作階段規劃重試預算。 單一輪次可能產生多次拒絕,例如一個代理加上其子代理。
- 在每條請求路徑上設定備援。 重試處理程式、錯誤復原分支與背景工作程式都需要它。在沒有備援的情況下重新發出請求的處理程式,恰恰會在最可能需要保護的請求上失去保護。
- 為子代理呼叫提供各自的備援。
fallbacks參數不會傳播到從工具執行內部發出的模型呼叫。 - 讓備援成為請求的屬性,而非環境狀態的屬性。 共用旗標、快取的設定值或全域開關可能會失去同步,並悄悄地讓請求失去保護。當您無法確認備援已啟用時,請設定它,而非假設它已開啟。
- 將拒絕作為獨立訊號進行監測。 拒絕是 HTTP 200,因此建立在錯誤率或 5xx 回應上的監控永遠看不到它。請為每次拒絕發出一個事件,並為每個由備援服務的回應發出一個事件(
usage.iterations中的fallback_message項目標記後者),然後針對兩個計數之間的差距發出警示。 - 依
stop_reason或stop_details.type分支,而非依content或內部的stop_details欄位。stop_details物件在拒絕時永遠存在,但其category與explanation欄位可能為null。請直接檢查stop_reason是否等於"refusal"。
後續步驟
Was this page helpful?