Claude Fable 5.1、Claude Fable 5、Claude Opus 5.5、Claude Opus 5、Claude Sonnet 5.5 和 Claude Haiku 5.5 包含可能拒绝请求的 “safety classifier”(安全分类器)。发生这种情况时,您会收到一个正常的响应(而不是错误),其中包含 stop_reason: "refusal"。其 stop_details.category 指明了相关的策略领域(请参阅拒绝是什么样的)。通常,您仍然可以通过将同一请求发送到另一个 Claude 模型来获得答案。本页介绍如何识别 “refusal”(拒绝),以及如何设置这种重试。
当您基于这些模型中的任何一个进行构建,并希望被拒绝的请求自动转交给另一个模型时,请阅读本页。如果您在响应中看到了 "refusal" 并想知道接下来该怎么做,本页同样适用。
相关页面:
- 停止原因与回退:
stop_reason值的完整列表 - 回退额度:在您自行构建重试时,如何避免两次支付提示缓存费用
- SDK 中间件:封装了所有这些功能的 SDK 辅助工具
- 回退与计费示例:一个完整的端到端示例
最简单的设置(在 Claude API 和 Claude Models on Google Cloud (Anthropic managed) 上处于 beta 阶段):将 fallbacks 设置为 "default",API 就会在 Anthropic 针对该拒绝类别推荐的回退模型上重试被拒绝的请求。对于没有推荐回退模型的类别,拒绝将保持不变。Claude Haiku 5.5 没有服务器端回退,因此对于该模型,请使用借助 SDK 中间件的客户端回退或自行编写重试。
cURL
curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: server-side-fallback-2026-07-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-fable-5",
"max_tokens": 1024,
"fallbacks": "default",
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'
CLI
ant beta:messages create \
--model claude-fable-5 \
--max-tokens 1024 \
--message '{"role":"user","content":"Hello, Claude"}' \
--fallbacks default \
--beta server-side-fallback-2026-07-01
Python
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)
TypeScript
const client = new Anthropic();
const response = await 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"]
});
console.log(response.model);
C#
AnthropicClient client = new();
BetaMessage response = await client.Beta.Messages.Create(
new()
{
Model = Messages::Model.ClaudeFable5,
MaxTokens = 1024,
Messages = [new BetaUserMessageParam { Content = "Hello, Claude" }],
Fallbacks = new Default(),
Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
}
);
Console.WriteLine(response.Model.Raw());
Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeFable5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
},
Fallbacks: anthropic.BetaFallbacksParamOfDefault(),
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
})
if err != nil {
panic(err)
}
fmt.Println(response.Model)
Java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaMessage response = client.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_FABLE_5)
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.fallbacksDefault()
.addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
.build());
IO.println(response.model().asString());
PHP
$client = new Client();
$response = $client->beta->messages->create(
model: Model::CLAUDE_FABLE_5,
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
fallbacks: 'default',
betas: ['server-side-fallback-2026-07-01'],
);
echo $response->model, PHP_EOL;
Ruby
client = Anthropic::Client.new
response = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_FABLE_5,
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
fallbacks: :default,
betas: ["server-side-fallback-2026-07-01"]
)
puts 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" |
该请求属于上述四个具名类别之外的使用政策领域。良性工作也可能触发此类别。 | 否 |
拒绝可能在任何输出之前到达,也可能在部分输出之后于流式传输中途到达。无论哪种情况,都应将任何部分输出视为不完整并丢弃。
将推理保留在思考块中
reasoning_extraction 拒绝通常源于要求模型将其思考或推理放入输出中的提示,无论是逐字输出还是以固定格式输出。常见示例:
- 模型在回答之前填写的
<thinking>、<reasoning>或草稿区部分 - JSON 输出或工具输入中的
reasoning、thinking或trace字段 - 模型的私人笔记,或持续记录其推理过程的日志
- 要求逐字或完整提供的推理
这类措辞可能出现在系统提示、skill 或工具描述中,因此也请检查这些内容。
您仍然可以要求 Claude 解释其答案。可以要求提供简短的解释、结果背后的依据,或其所采取操作的摘要。要查看模型是如何推理的,请设置 display: "summarized" 并阅读 thinking 块。此类别没有推荐的回退模型,因此请修改提示,而不是重试请求。如果改写后的请求仍被拒绝,请联系支持团队并附上请求 ID。
拒绝如何计费
这些计费规则适用于所有平台。
在产生任何输出之前的拒绝: 为了遏制大规模规避 Anthropic 安全保障措施的尝试,当在产生任何输出之前到达的拒绝的 stop_details.category 为 "bio"、"frontier_llm" 或 "reasoning_extraction" 时,将对其计费。截至 2026 年 9 月,这些是 Anthropic 测得误报量较低的类别。这些拒绝与其他任何请求一样计费,按运行该请求的模型的费率计算。其他任何类别或类别为 null 的、在产生任何输出之前的拒绝不计费。无论哪种情况,content 都为空,令牌计数显示在 usage 中。该请求仍会计入您的速率限制。
流式传输中途的拒绝: 流式传输中途的拒绝会按正常费率对输入令牌和已流式传输的输出计费。
回退: 使用回退时,如果触发回退的拒绝是在流式传输中途到达的,或属于计费类别之一,则除回退请求外,该拒绝也会被计费。回退抵扣会补偿回退请求的提示缓存未命中,因此您无需为缓存对话支付两次费用。Claude Haiku 5.5 的拒绝不附带回退抵扣,因此在其之后进行的回退需要支付写入回退模型提示缓存的全部费用。关于服务端回退如何报告每次尝试,请参阅计费与速率限制。
随着 Anthropic 持续测量并改进其安全防护措施的误报率,计费类别可能会发生变化。拒绝类别表中的在产生任何输出之前是否计费一列列出了计费类别。
选择回退方式
有三种方式可以在另一个模型上重试被拒绝的请求。合适的方式取决于您的运行环境以及您需要多大的控制权。
| 您的情况 | 使用 | 原因 |
|---|---|---|
| Claude API 或 Claude Models on Google Cloud (Anthropic managed),最简单的设置 | 服务器端回退 | 一次请求,一个响应。由 API 处理重试。 |
| 任何平台,使用 Anthropic SDK | 借助 SDK 中间件的客户端回退 | 在客户端上配置一次。重试自动进行。 |
| 原始 HTTP 或自定义重试逻辑 | 手动重试并配合回退抵扣 | 完全控制。回退抵扣可降低成本。 |
服务端回退和 SDK 中间件会为您应用回退抵扣。只有在您自行构建重试时,才需要参阅回退抵扣页面。Claude Haiku 5.5 既没有服务端回退,也没有回退抵扣,因此请使用 SDK 中间件或手动重试。
服务端回退
服务端回退在单次 API 调用内重试被拒绝的请求。在默认模式下,当主模型拒绝且该拒绝类别有推荐的回退时,API 会在 Anthropic 针对该类别推荐的模型上运行同一请求。您也可以改为自行指定最多三个回退模型。无论哪种方式,您都会收到一个指明作答模型的响应,因此您的用户在一次往返中即可获得答案。
注意
服务器端回退在 Claude API 和 Claude Models on Google Cloud (Anthropic managed) 上处于 beta 阶段。在不支持服务器端回退的平台上,请改用借助 SDK 中间件的客户端回退。Message Batches API 不支持
fallbacks参数(包含该参数的批处理项会以错误结果返回)。Claude Haiku 5.5 没有服务器端回退:使用fallbacks: "default"时,被拒绝的请求仍保持拒绝,而指定回退模型列表会返回 400 错误。
发起请求
将 fallbacks 参数设置为字符串 "default",并发送 server-side-fallback-2026-07-01 beta 标头。然后,API 会应用所请求模型的服务端定义的默认路由,该路由根据分类器报告的拒绝类别选择推荐的回退模型,因此被拒绝的请求可以得到处理,而无需您在推荐发生变化时维护模型列表。
默认路由绝不会因您未选择的模型而引发预先的超大图像拒绝:如果某个路由模型会对标记为 "oversized_image": "error" 的图像进行缩放,则该模型会从路由中被剔除,因此被标记的图像绝不会以缩放后的形式被处理。
cURL
curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: server-side-fallback-2026-07-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-fable-5",
"max_tokens": 1024,
"fallbacks": "default",
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'
CLI
ant beta:messages create \
--model claude-fable-5 \
--max-tokens 1024 \
--message '{"role":"user","content":"Hello, Claude"}' \
--fallbacks default \
--beta server-side-fallback-2026-07-01
Python
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,
}
)
)
TypeScript
const client = new Anthropic();
const response = await 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 确认该响应是否由回退模型提供。
const { stop_reason, model, usage } = response;
const servedByFallback =
(usage.iterations ?? []).some((entry) => entry.type === "fallback_message") &&
stop_reason !== "refusal";
console.log(
JSON.stringify({
stop_reason,
model,
served_by_fallback: servedByFallback
})
);
C#
AnthropicClient client = new();
var response = await client.Beta.Messages.Create(
new()
{
Model = Messages::Model.ClaudeFable5,
MaxTokens = 1024,
Messages = [new BetaUserMessageParam { Content = "Hello, Claude" }],
Fallbacks = new Default(),
Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
}
);
// usage.iterations 中出现 fallback_message 条目表示已运行回退模型;
// 请结合 stop_reason 确认该响应确实由回退模型提供。
bool fallbackRan = (response.Usage.Iterations ?? []).Any(iteration =>
iteration.TryPickBetaFallbackMessageIterationUsage(out _)
);
bool servedByFallback =
fallbackRan && response.StopReason?.Value() != BetaStopReason.Refusal;
Console.WriteLine(
JsonSerializer.Serialize(
new
{
stop_reason = response.StopReason?.Raw(),
model = response.Model.Raw(),
served_by_fallback = servedByFallback,
}
)
);
Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeFable5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
},
Fallbacks: anthropic.BetaFallbacksParamOfDefault(),
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
})
if err != nil {
panic(err)
}
// usage.iterations 中出现 fallback_message 条目表示运行了回退模型;
// 请结合 stop_reason 确认该响应是否由回退模型提供。
fallbackRan := slices.ContainsFunc(
response.Usage.Iterations,
func(iteration anthropic.BetaIterationsUsageItemUnion) bool {
_, isFallback := iteration.AsAny().(anthropic.BetaFallbackMessageIterationUsage)
return isFallback
},
)
servedByFallback := fallbackRan && response.StopReason != anthropic.BetaStopReasonRefusal
summary, err := json.Marshal(struct {
StopReason anthropic.BetaStopReason `json:"stop_reason"`
Model anthropic.Model `json:"model"`
ServedByFallback bool `json:"served_by_fallback"`
}{response.StopReason, response.Model, servedByFallback})
if err != nil {
panic(err)
}
fmt.Println(string(summary))
Java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaMessage response = client.beta().messages().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_FABLE_5)
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.fallbacksDefault()
.addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
.build()
);
// 出现 fallback_message 用量条目表示响应由回退模型生成;
// 出现 refusal 停止原因则表示没有任何模型提供该响应。
List<BetaUsage.Iteration> iterations =
response.usage().iterations().orElse(List.of());
boolean servedByFallback =
iterations.stream().anyMatch(BetaUsage.Iteration::isFallbackMessage)
&& response.stopReason().filter(BetaStopReason.REFUSAL::equals).isEmpty();
IO.println("""
{"stop_reason":"%s","model":"%s","served_by_fallback":%b}\
""".formatted(
response.stopReason().map(BetaStopReason::asString).orElse("null"),
response.model().asString(),
servedByFallback));
PHP
$client = new Client();
$response = $client->beta->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: Model::CLAUDE_FABLE_5,
fallbacks: 'default',
betas: ['server-side-fallback-2026-07-01'],
);
// usage.iterations 中出现 fallback_message 条目表示运行了回退模型;
// 请结合 stop_reason 确认该响应是否由回退模型提供。
$iterations = $response->usage->iterations ?? [];
$servedByFallback = array_any($iterations, fn($entry) => $entry->type === 'fallback_message')
&& $response->stopReason !== 'refusal';
echo json_encode([
'stop_reason' => $response->stopReason,
'model' => $response->model,
'served_by_fallback' => $servedByFallback,
]), PHP_EOL;
Ruby
client = Anthropic::Client.new
response = client.beta.messages.create(
model: Anthropic::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 确认该回退模型是否提供了响应。
iterations = response.usage.iterations || []
served_by_fallback = iterations.any? { it.type == :fallback_message } &&
response.stop_reason != :refusal
stop_reason = response.stop_reason
model = response.model
puts JSON.generate({stop_reason:, model:, served_by_fallback:})
Anthropic 根据模型的能力,为每个模型和每个策略类别分别设置安全保障措施:根据类别不同,被标记的请求可能会回退到能力较弱的模型,或被拒绝。"default" 模式为您封装了这些按模型、按类别的推荐,因此被拒绝的请求会在 Anthropic 针对该类别推荐的模型上重试。无论哪种方式,回退都是可见的:响应会指明处理它的模型,fallback 内容块会标记交接点。
该路由在服务器端应用,不会在 Models API 上按模型公布。要查看哪个模型处理了被拒绝的请求,请检查响应的顶层 model 字段,并在 usage.iterations 中查找 fallback_message 条目,正如本页示例所示。
只有安全分类器的拒绝才会触发回退。所请求模型上的速率限制、过载或服务器错误会原样返回给您。
注意
beta 标头必须精确携带日期
2026-07-01(同时支持"default"和显式列表形式)或2026-06-01(仅接受显式列表形式)。在任何其他server-side-fallback-*值下,fallbacks参数会被拒绝并返回 400 错误。如果您是基于此功能的早期预览版构建的,请将 beta 标头以及请求和响应结构一并更新为本页所示的形式。
指定您自己的回退模型
除了默认路由,您还可以将 fallbacks 设置为最多包含三个模型的列表。当所请求的模型拒绝时,API 会在同一请求上运行链中的下一个模型。当您希望精确控制由哪些模型处理被拒绝的请求时(例如固定使用您的应用程序已验证过的模型),请使用此形式。
指定的回退模型会计入超大图像检查。如果请求的图像块设置了 "oversized_image": "error",则会预先针对所请求的模型和每个指定的回退模型进行检查。如果其中任何一个会调整该图像的大小,请求就会被拒绝,并且拒绝中报告的缩放目标适用于所有这些模型。
高亮显示的行是与默认路由请求的唯一区别。
cURL
curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: server-side-fallback-2026-07-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-fable-5",
"max_tokens": 1024,
"fallbacks": [{"model": "claude-opus-4-8"}],
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'
CLI
ant beta:messages create \
--model claude-fable-5 \
--max-tokens 1024 \
--message '{"role":"user","content":"Hello, Claude"}' \
--fallbacks '[{"model":"claude-opus-4-8"}]' \
--beta server-side-fallback-2026-07-01
Python
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)
TypeScript
const client = new Anthropic();
const response = await 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"]
});
console.log(response.model);
C#
AnthropicClient client = new();
BetaMessage response = await client.Beta.Messages.Create(
new()
{
Model = Messages::Model.ClaudeFable5,
MaxTokens = 1024,
Messages = [new BetaUserMessageParam { Content = "Hello, Claude" }],
Fallbacks = new([new(Messages::Model.ClaudeOpus4_8)]),
Betas = [AnthropicBeta.ServerSideFallback2026_07_01],
}
);
Console.WriteLine(response.Model.Raw());
Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeFable5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
},
Fallbacks: anthropic.BetaFallbacksParamUnion{
OfBetaFallbackArray: []anthropic.BetaFallbackParam{{Model: anthropic.ModelClaudeOpus4_8}},
},
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaServerSideFallback2026_07_01},
})
if err != nil {
panic(err)
}
fmt.Println(response.Model)
Java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaMessage response = client.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_FABLE_5)
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.fallbacksOfFallbackParams(List.of(BetaFallbackParam.builder()
.model(Model.CLAUDE_OPUS_4_8)
.build()))
.addBeta(AnthropicBeta.SERVER_SIDE_FALLBACK_2026_07_01)
.build());
IO.println(response.model().asString());
PHP
$client = new Client();
$response = $client->beta->messages->create(
model: Model::CLAUDE_FABLE_5,
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
fallbacks: [['model' => 'claude-opus-4-8']],
betas: ['server-side-fallback-2026-07-01'],
);
echo $response->model, PHP_EOL;
Ruby
client = Anthropic::Client.new
response = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_FABLE_5,
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
fallbacks: [{model: Anthropic::Model::CLAUDE_OPUS_4_8}],
betas: ["server-side-fallback-2026-07-01"]
)
puts response.model
fallbacks 列表适用以下几条规则:
- 条目按顺序尝试。每个条目都必须与其他条目以及所请求的模型不同。
- 每个条目必须是所请求模型允许的目标之一。设置 beta 请求头后,该列表会作为
allowed_fallback_models发布在 Models API 中该模型的条目上。 - 每个条目指定一个
model,并且可以仅针对该次尝试覆盖max_tokens、thinking、output_config和speed。 - 该请求必须作为直接请求对所指定的每个模型都有效。如果某个回退模型不支持请求所使用的功能,API 会预先拒绝该请求。在
2026-07-01标头下,thinking: {"type": "between_tools"}是例外:当 Claude Sonnet 5.5 请求回退到 Claude Sonnet 5 时,回退尝试会以thinking: {"type": "disabled"}和display: "omitted"运行。在2026-06-01下,请在 Claude Sonnet 5 条目上设置thinking,否则请求会被预先拒绝。 - 与默认模式一样,只有安全分类器的拒绝才会触发回退。所请求模型上的速率限制、过载或服务器错误会原样返回给您。
- 如果回退模型受到速率限制或过载,则不会进行回退尝试,而是返回之前的拒绝。此时,该拒绝的
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 |
与其结果配对时保留。没有匹配结果时丢弃。 |
注意
connector_text块携带某些使用工具的响应在工具调用之间包含的叙述文本。
流式传输
在流式传输请求中,重试发生在同一个流上,您已收到的任何内容都不会失效。您看到的内容取决于拒绝发生的时间。
当拒绝发生在产生任何输出之前时:
message_start指明回退模型,fallback块是第一个内容块。- 由于
message_start会等待回退尝试开始,因此首字节时间包含被拒绝的尝试。
当拒绝发生在输出中途时:
- 打开的内容块关闭,
fallback块(一对普通的content_block_start和content_block_stop,没有增量)标记边界。 - 回退模型从部分输出处继续。只有部分输出的
text块会作为上下文传递给回退模型。其他块类型保留在content中。 message_start已经指明了所请求的模型,因此请从fallback块的to.model以及最终message_delta的usage.iterations中的fallback_message条目读取处理模型。
非流式传输响应
在非流式请求中,输出中途的拒绝表现不同:响应会省略被拒绝模型的部分输出,回退模型在没有该输出作为上下文的情况下作答。结果看起来像是在产生任何输出之前的拒绝,fallback 块位于最前。被拒绝的尝试及其输出令牌仍会出现在 usage.iterations 中。
注意
工具使用期间的拒绝: 已完成的工具工作不会阻止回退。当拒绝在服务器工具(例如网页搜索或代码执行)已在请求内执行完毕之后触发时,回退尝试会继续进行:已完成的工具结果会被带过去,回退模型可以继续调用服务器工具。唯一不会重试的情况是:流式传输中的拒绝在任何类型的工具使用块(客户端工具、服务器工具或 MCP 工具调用)仍在流上打开时触发:该拒绝会直接返回,并且如果设置了
fallback-credit-2026-07-01标头,它仍会携带一个可通过继续部分响应来兑换的抵扣令牌。非流式传输请求不受影响;API 会清除部分工作并在响应前重试。
计费与速率限制
每次尝试都遵循拒绝如何计费中的规则,按运行它的模型的费率计费。在生成任何输出之前就拒绝的尝试,仅当其拒绝类别属于计费类别时才计费;无论是否计费,其令牌都会在其 usage.iterations 条目上报告。每次生成了输出的尝试(包括在响应中途拒绝的尝试)都单独计费。usage.iterations 数组是您被计费内容的逐次尝试记录。顶层 usage 计数仅描述生成所返回消息的那次尝试。来自不同模型的令牌绝不会被汇总到一个字段中。
每次运行的尝试(包括拒绝的尝试)都计入其自身模型的速率限制。
粘性路由
对话回退后,API 会记录哪个模型处理了它。该对话后续包含 fallbacks 的请求会直接发送到该回退模型,而不运行所请求的模型。这避免了在每一轮都为一次可预见会再次被拒绝的尝试付费。
路由决策的几个特性:
- 它保留约 1 小时,并限定在您的组织范围内。
- 它以对话前缀的内容哈希加上处理它的模型的形式存储。消息内容本身不会被存储。
- 它是尽力而为的,因此您的代码必须能够处理所请求的模型在任何时候被再次尝试的情况。
粘性路由同时适用于流式传输和非流式传输请求。在流式传输请求中,路由决策在流打开之前做出,因此 message_start 事件的 model 字段已经携带回退模型的 ID。
配合 SDK 中间件的客户端回退
SDK 包含一个拒绝回退中间件。您只需在客户端上使用回退模型列表配置一次。之后,通过 client.beta.messages (csharp, go: client.Beta.Messages; java: client.beta().messages(); php: $client->beta->messages) 进行的调用会在任何平台上自动重试被拒绝的请求。该中间件还会在其处理的每个请求上发送 fallback-credit-2026-07-01 beta 标头,因此重试会被重新定价,无需逐个请求进行设置。Claude Haiku 5.5 的拒绝不附带回退抵扣,因此在其之后进行的重试需要支付写入回退模型提示缓存的全部费用。
设置
将 BetaRefusalFallbackMiddleware (typescript: betaRefusalFallbackMiddleware(); go: betafallback.BetaRefusalFallbackMiddleware(); csharp: BetaRefusalFallbackHandler; java: BetaRefusalFallbackInterceptor; php: RefusalFallbackMiddleware) 传递给客户端构造函数,并在一个对话的各个请求之间共享同一个 BetaFallbackState (go: betafallback.BetaFallbackState) 实例。
cURL
拒绝回退中间件是 Claude SDK 的一部分,因此 cURL 请求无法使用它。若要在单个请求内进行回退,请使用服务端回退。若要自行编写重试,请参阅回退抵扣。
CLI
ant CLI 不包含拒绝回退中间件。若要在单个请求内进行回退,请使用服务端回退。若要自行编写重试,请参阅回退抵扣。
Python
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}")
TypeScript
import { BetaFallbackState, betaRefusalFallbackMiddleware } from "@anthropic-ai/sdk";
// 遇到拒绝时,中间件会在列出的回退模型上重试,并且
// 会在其处理的每个请求上自动发送 fallback-credit beta 标头。
const client = new Anthropic({
middleware: [betaRefusalFallbackMiddleware([{ model: "claude-opus-4-8" }])]
});
// 在整个对话中共享同一个状态,以便后续请求保持
// 固定在已接受请求的模型上。
const fallbackState = new BetaFallbackState();
// 流式传输:遇到拒绝时,中间件会在回退模型上重试,并且
// 将其事件拼接到已打开的流上。
const stream = client.beta.messages
.stream(
{
max_tokens: 1024,
model: "claude-fable-5",
messages: [{ role: "user", content: "Hello, Claude" }]
},
{ fallbackState }
)
.on("text", (text) => process.stdout.write(text));
const finalMessage = await stream.finalMessage();
console.log("\nserved by:", finalMessage.model);
// 非流式传输:复用该状态可使对话保持固定。
const message = await client.beta.messages.create(
{
max_tokens: 1024,
model: "claude-fable-5",
messages: [{ role: "user", content: "Hello, Claude" }]
},
{ fallbackState }
);
console.log("served by:", message.model);
C#
using Anthropic;
using Anthropic.Helpers;
using Anthropic.Models.Beta.Messages;
using Messages = Anthropic.Models.Messages;
// 遇到拒绝时,处理程序会在列出的回退模型上重试,并且
// 会在其处理的每个请求上自动发送 fallback-credit beta 标头。
AnthropicClient client = new()
{
Handlers =
[
new BetaRefusalFallbackHandler { Fallbacks = [new(Messages::Model.ClaudeOpus4_8)] },
],
};
// 将共享此状态的后续请求固定到已接受请求的模型上。
BetaFallbackState fallbackState = BetaFallbackState.Create();
MessageCreateParams parameters = new()
{
Model = Messages::Model.ClaudeFable5,
MaxTokens = 1024,
Messages = [new BetaUserMessageParam { Content = "Hello, Claude" }],
};
// 流式传输:如果流以拒绝结束,处理程序会将回退
// 模型的事件拼接到仍处于打开状态的流上。
BetaMessageContentAggregator aggregator = new();
using (fallbackState.Use())
{
var responseUpdates = client.Beta.Messages.CreateStreaming(parameters);
await foreach (BetaRawMessageStreamEvent rawEvent in responseUpdates.CollectAsync(aggregator))
{
if (
rawEvent.TryPickContentBlockDelta(out var deltaEvent)
&& deltaEvent.Delta.TryPickText(out var textDelta)
)
{
Console.Write(textDelta.Text);
}
}
}
BetaMessage streamedMessage = aggregator.Message();
Console.WriteLine($"\nserved by: {streamedMessage.Model.Raw()}");
// 非流式传输:复用该状态可使对话保持固定在已接受请求的模型上。
using (fallbackState.Use())
{
BetaMessage message = await client.Beta.Messages.Create(parameters);
Console.WriteLine($"served by: {message.Model.Raw()}");
}
Go
import (
// ...
"github.com/anthropics/anthropic-sdk-go/v2/lib/betafallback"
// ...
)
func main() {
ctx := context.Background()
// 该中间件会在请求被拒绝时依次在每个回退模型上重试,
// 并自动将请求加入 fallback-credit beta。
client := anthropic.NewClient(
option.WithMiddleware(betafallback.BetaRefusalFallbackMiddleware(
[]anthropic.BetaFallbackParam{{Model: anthropic.ModelClaudeOpus4_8}},
)),
)
// 每个对话一个状态:共享该状态的请求会固定到
// 已接受请求的模型上,因此后续请求不会再询问曾拒绝的模型。
state := &betafallback.BetaFallbackState{}
conversation := betafallback.WithBetaFallbackState(state)
params := anthropic.BetaMessageNewParams{
MaxTokens: 1024,
Model: anthropic.ModelClaudeFable5,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello, Claude")),
},
}
// 流式传输:遇到拒绝时,中间件会就地重试,将回退模型的
// 事件拼接到已打开的流上,形成一条连续的消息。
stream := client.Beta.Messages.NewStreaming(ctx, params, conversation)
defer stream.Close()
var streamed anthropic.BetaMessage
for stream.Next() {
event := stream.Current()
if err := streamed.Accumulate(event); err != nil {
panic(err)
}
switch eventVariant := event.AsAny().(type) {
case anthropic.BetaRawContentBlockDeltaEvent:
if textDelta, ok := eventVariant.Delta.AsAny().(anthropic.BetaTextDelta); ok {
fmt.Print(textDelta.Text)
}
}
}
if err := stream.Err(); err != nil {
panic(err)
}
fmt.Println("\nserved by:", streamed.Model)
// 非流式传输:共享状态会将此后续请求固定到
// 处理了流式传输轮次的模型上。
message, err := client.Beta.Messages.New(ctx, params, conversation)
if err != nil {
panic(err)
}
fmt.Println("served by:", message.Model)
}
Java
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.RequestOptions;
import com.anthropic.core.http.StreamResponse;
import com.anthropic.helpers.BetaFallbackState;
import com.anthropic.helpers.BetaMessageAccumulator;
import com.anthropic.helpers.BetaRefusalFallbackInterceptor;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.BetaRawMessageStreamEvent;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
void main() {
// 该拦截器会在回退模型上重试被拒绝的请求。它会自动
// 为其处理的每个请求添加 fallback-credit beta 标头。
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.addInterceptor(BetaRefusalFallbackInterceptor.builder()
.addFallback(Model.CLAUDE_OPUS_4_8)
.build())
.build();
// 在请求之间共享同一状态,使后续请求固定在已接受请求的模型上。
BetaFallbackState state = BetaFallbackState.create();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_FABLE_5)
.maxTokens(1024)
.addUserMessage("Hello, Claude")
.build();
// 流式传输:发生拒绝时,回退模型的事件会被拼接到已打开的流上。
BetaMessageAccumulator accumulator = BetaMessageAccumulator.create();
try (StreamResponse<BetaRawMessageStreamEvent> streamResponse = client.beta()
.messages()
.createStreaming(params, RequestOptions.builder()
.fallbackState(state)
.build())) {
streamResponse.stream()
.peek(accumulator::accumulate)
.forEach(event -> event.contentBlockDelta()
.flatMap(deltaEvent -> deltaEvent.delta().text())
.ifPresent(textDelta -> IO.print(textDelta.text())));
}
IO.println("\nserved by: " + accumulator.message().model().asString());
// 非流式传输:复用同一状态可使对话保持固定。
BetaMessage message = client.beta()
.messages()
.create(params, RequestOptions.builder()
.fallbackState(state)
.build());
IO.println("served by: " + message.model().asString());
}
PHP
use Anthropic\Beta\Messages\BetaRawContentBlockDeltaEvent;
use Anthropic\Beta\Messages\BetaTextDelta;
use Anthropic\Client;
use Anthropic\Lib\Middleware\BetaFallbackState;
use Anthropic\Lib\Middleware\RefusalFallbackMiddleware;
use Anthropic\Lib\Streaming\MessageAccumulator;
use Anthropic\Messages\Model;
// 只需配置一次回退链。遇到拒绝时,中间件会沿着回退链重试
// 该请求,并为您发送 fallback-credit beta 请求头。
$client = new Client(
requestOptions: [
'middleware' => [new RefusalFallbackMiddleware([['model' => 'claude-opus-4-8']])],
],
);
// 在整个对话中共享同一个状态,以便后续请求始终固定
// 到已接受请求的模型上。
$state = new BetaFallbackState();
// 流式传输:遇到拒绝时,中间件会将回退模型的事件拼接
// 到仍处于打开状态的流上。累加器的模型即为实际提供服务的模型。
$stream = $client->beta->messages->createStream(
model: Model::CLAUDE_FABLE_5,
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
requestOptions: ['fallbackState' => $state],
);
$accumulator = MessageAccumulator::forBetaMessages();
foreach ($stream as $event) {
$accumulator->accumulate($event);
if ($event instanceof BetaRawContentBlockDeltaEvent
&& $event->delta instanceof BetaTextDelta) {
echo $event->delta->text;
}
}
echo "\nserved by: {$accumulator->message()->model}\n";
// 非流式传输:使用相同的中间件。复用该状态可使对话始终
// 固定到已接受请求的模型上。
$message = $client->beta->messages->create(
model: Model::CLAUDE_FABLE_5,
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
requestOptions: ['fallbackState' => $state],
);
echo "served by: {$message->model}\n";
Ruby
# 遇到拒绝时,中间件会沿回退链重试该请求。
# 它会在处理的每个请求上发送 fallback-credit beta 标头。
client = Anthropic::Client.new(
middleware: [Anthropic::BetaRefusalFallbackMiddleware.new([{model: "claude-opus-4-8"}])]
)
# 在整个对话中共享同一状态,使后续请求保持
# 固定在已接受请求的模型上。
state = Anthropic::BetaFallbackState.new
# 流式传输:遇到拒绝时,中间件会将回退模型的
# 事件拼接到仍处于打开状态的流上。
stream = client.beta.messages.stream(
model: Anthropic::Model::CLAUDE_FABLE_5,
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
request_options: {fallback_state: state}
)
stream.text.each { print it }
puts "\nserved by: #{stream.accumulated_message.model}"
# 非流式传输:复用该状态可使对话保持固定在已接受请求的模型上。
message = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_FABLE_5,
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
request_options: {fallback_state: state}
)
puts "served by: #{message.model}"
行为方式
- 重试会按顺序遍历您的回退列表。如果某个回退模型本身也拒绝,请求会被传递给下一个条目。
- 当列表中的每个模型都拒绝后,中间件会返回最终的拒绝(最后一个模型的拒绝响应),而不是抛出错误。
- 来自 Claude Fable 5.1、Claude Opus 5.5、Claude Sonnet 5.5、Claude Haiku 5.5 或 Claude Fable 5 的思考块会原样传递。每次重试都会重新发送您的原始请求体,中间件在后续请求中从对话历史中移除的唯一块是它自己添加的
fallback边界块。回退模型无法读取 Claude Fable 5.1 的块,只有 Claude Fable 5.1 和 Claude Mythos 5.1 能读取,因此 API 会丢弃它们。对于除 Claude Fable 5.1 和 Claude Mythos 5.1 之外的所有回退模型,API 也会丢弃 Claude Opus 5.5 的块(请参阅在对话中途切换模型)。对于除 Claude API 和 Claude Models on Google Cloud (Google managed) 上的 Claude Opus 5.5 之外的所有回退模型,它同样会丢弃 Claude Sonnet 5.5 的块。在 Claude API 和 Claude Models on Google Cloud (Google managed) 上,Claude Opus 5.5 和 Claude Sonnet 5.5 能读取 Claude Haiku 5.5 的块,而对于无法读取这些块的回退模型,API 会丢弃它们。 - 通过中间件处理的响应会在每个模型边界处包含一个
fallback内容块,与服务器端回退的响应相同。中间件会在后续请求中为您管理这些块。 - 接受请求的模型会记录在
BetaFallbackState(go:betafallback.BetaFallbackState) 中,因此共享该状态的后续请求会固定使用该模型,而不会重新询问曾拒绝的模型。
注意
中间件和服务端
fallbacks参数完成相同的工作。请配置其中之一,切勿在同一请求上同时配置两者。要从安装了中间件的应用程序发送服务端fallbacks请求,请使用一个不带中间件的单独客户端实例。
自行编写重试
通过原始 HTTP 或使用自定义重试逻辑时,请实现中间件所封装的模式:
-
检测拒绝
检查响应中是否有
stop_reason: "refusal"。 -
在回退模型上重新发送
发送同一请求,并将
model设置为回退模型,例如 Claude Opus 4.8。如果被拒绝的请求发送了thinking: {"type": "between_tools"},请先修改thinking:只有 Claude Sonnet 5.5 接受该值,因此请省略thinking或设置回退模型接受的值。在2026-07-01标头下,服务器端回退在回退到 Claude Sonnet 5 时会为您进行此修改。被 Claude Fable 5.1 或 Claude Fable 5 拒绝的请求通常可以由另一个模型处理。如何处理对话历史取决于您是否兑换回退抵扣:- 不兑换额度: 您可以保留之前的
thinking和redacted_thinking块,也可以将其移除以节省输入令牌。无论哪种方式,回退模型通常都无法使用它们:只有 Claude Fable 5.1 和 Claude Mythos 5.1 能读取 Claude Fable 5.1 的块,只有这两个模型以及 Claude Fable 5 和 Claude Mythos 5 能读取 Claude Fable 5 的块,因此对于任何其他回退模型,API 都会丢弃它们。对于除 Claude Fable 5.1 和 Claude Mythos 5.1 之外的所有回退模型,API 也会丢弃 Claude Opus 5.5 的块(请参阅在对话中途切换模型)。对于除 Claude API 和 Claude Models on Google Cloud (Google managed) 上的 Claude Opus 5.5 之外的所有回退模型,它同样会丢弃 Claude Sonnet 5.5 的块。Claude Haiku 5.5 拒绝后的重试始终属于这种情况。在 Claude API 和 Claude Models on Google Cloud (Google managed) 上,Claude Opus 5.5 和 Claude Sonnet 5.5 能读取 Claude Haiku 5.5 的块,因此在这些平台上回退到这两个模型中的任一个时,请保留这些块。对于无法读取这些块的回退模型,API 会丢弃它们且不对其计费。 - 兑换额度: 请原样发送请求体,因为兑换要求完全匹配。在兑换时,服务器会处理之前模型的思考块,因此请勿移除它们(请参阅必须与被拒绝请求匹配的字段)。
- 不兑换额度: 您可以保留之前的
-
继续使用回退模型
对于多轮对话,请在后续轮次中继续使用回退模型,而不要切换回原模型。
手动重试会完整写入回退模型的提示缓存,这比读取现有缓存的费用更高。回退额度会退还该费用;请在您自行构建的每次重试中兑换它。Claude Haiku 5.5 的拒绝不附带回退额度,因此在其之后进行的重试需要支付写入回退模型提示缓存的全部费用。
Message Batches 中的拒绝
Message Batch 中被拒绝的请求会以 result.type: "succeeded" 和 stop_reason: "refusal" 返回。批处理结果携带与同步响应相同的 stop_details 对象,因此您可以通过 stop_reason 或 stop_details.type 检测拒绝。一个区别是:批处理拒绝不会生成回退抵扣,因此批处理结果上的 stop_details 绝不会包含 fallback_credit_token。
服务端回退不适用于批处理(包含 fallbacks 的批处理请求会产生逐项的出错结果)。要重试被拒绝的批处理项:
- 从结果中收集被拒绝的项。
- 保留多轮历史中的思考块,或将其移除以节省输入令牌。对于无法读取这些块的回退模型,API 会将其丢弃且不计费,与手动重试中相同。
- 如果被拒绝的项发送了
thinking: {"type": "between_tools"},请省略thinking或设置回退模型接受的值。只有 Claude Sonnet 5.5 接受between_tools。 - 在回退模型上将它们作为新批处理或直接请求重新提交。
常见陷阱
- 在不同的模型上重试。 将被拒绝的请求重新发送到同一模型通常会再次被拒绝。请将重试指向回退模型。
- 按请求而不是按轮次或会话来规划重试预算。 单个轮次可能产生多次拒绝,例如一个智能体加上其子智能体。
- 在每条请求路径上配置回退。 重试处理程序、错误恢复分支和后台工作进程都需要它。一个在没有回退的情况下重新发出请求的处理程序,恰恰会在最可能需要保护的请求上失去保护。
- 为子智能体调用配置各自的回退。
fallbacks参数不会延续到在工具执行内部发出的模型调用。 - 让回退成为请求的属性,而不是环境状态的属性。 共享标志、缓存的配置值或全局开关可能会失去同步,并悄无声息地使请求失去保护。当您无法确认回退处于活动状态时,请配置它,而不要假设它已开启。
- 将拒绝作为独立信号进行监测。 拒绝是 HTTP 200,因此基于错误率或 5xx 响应构建的监控永远看不到它。为每次拒绝发出一个事件,为每个由回退处理的响应发出一个事件(
usage.iterations中的fallback_message条目标记后者),然后针对两个计数之间的差距设置告警。 - 根据
stop_reason或stop_details.type进行分支,而不是根据content或内部的stop_details字段。stop_details对象在拒绝时始终存在,但其category和explanation字段可能为null。请直接检查stop_reason是否等于"refusal"。
后续步骤
-
回退抵扣:当您自行构建重试时,避免支付两次提示缓存成本。
-
停止原因与回退:每个
stop_reason值及其处理方式。 -
SDK 中间件:SDK 中间件的工作原理,包括拒绝回退辅助工具。
-
迁移指南:将现有应用程序迁移到 Claude Fable 5.1。
可用性
分类器拒绝在部分模型上可用或处于 beta 阶段,具体取决于API。
API 支持
Messages API
- 可用性:始终开启
| 状态 | 模型 |
|---|---|
| 可用 | Claude Fable 5.1, Claude Mythos 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5, Claude Haiku 5.5 |
Managed Agents
- 可用性:始终开启
| 状态 | 云平台 | 模型 |
|---|---|---|
| Beta | Claude API, Claude Platform on AWS | Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5, Claude Haiku 5.5 |