思考块的前缀检查
前缀检查比较什么、哪些模型会运行它、请求未通过前缀检查时会发生什么,以及如何处理 400 错误和不返回错误的情况。
每个将思考块发送回具有保留思考的模型的请求都会经过一次 “prefix check”(前缀检查)。如果在 Claude 生成某个块之后,system、tools 或更早的消息发生了更改,前缀检查就会失败。如果位于其他思考块之间的某个思考块被省略或移动,前缀检查也会失败。
未通过前缀检查可能会也可能不会导致 400 错误,这取决于模型,因此请处理两种情况。
前缀检查会影响谁
如果以下任一情况成立,您无需做任何更改:
- 由 Claude Code、claude.ai 或 Claude Managed Agents 管理您的对话历史。
- 由 Claude Agent SDK 管理历史,且您提供给 SDK 的系统提示保持不变。
- 您的代码遵循仅追加集成的三个习惯,并且在传输途中没有任何东西改写其请求。
- 您没有调用任何具有保留思考的模型。
如果您的请求会经过某个库、代理或网关,请将它发送的内容与您发送的内容进行比较。如果您运行着这样的组件,请参阅库、代理和网关,了解应当原样传递哪些内容。
哪些内容必须保持不变,以及原因
请将您的集成设计为 “append-only”(仅追加)。以下三个习惯可以做到这一点:
- 将每个助手轮次完全按照 API 返回的原样发送回去,包括文本为空的思考块。
- 为每个对话只构建一次
system和tools,并且每次都发送这同一份副本。 - 将每次更新放在末尾的一条新消息中,并保持其不变。
每个思考块的推理都恰好针对其之前的内容:system、tools 以及每一条更早的消息。这些内容就是该块的前缀,也正是前缀检查所比较的内容。
前缀检查与 “prompt caching”(提示缓存)类似,不同之处在于:更改会使其后的思考失效,而不仅仅是未命中缓存。
以一个在系统提示中写入当天日期的应用为例。到了午夜,日期发生变化,对话中已有思考的前缀也随之改变。这些思考会失效,下一个请求可能会失败。仅追加的应用会保持系统提示原样不变,并在末尾的一条消息中说明新的日期。
一次更改只会使已经位于其后的思考失效。位于其之前的思考,以及 Claude 在此之后生成的思考,都保持有效。
如果您的代码更改了系统提示、工具或历史
| 如果您的代码…… | 应当怎么做 |
|---|---|
| 使用日期、模式或指令文件重新构建系统提示 | 停止更改 system。将您自己的指令作为系统消息追加(即 messages 中的一个 role: "system" 条目),并在最新的轮次中说明新的值。对于其他人可以编辑的文本,请改为参阅其他人可以编辑的文本。 |
| 添加、移除或改写某个工具,或在 MCP 服务器连接时获得新工具 | 停止更改 tools,并在系统消息中添加或移除工具。 |
| 裁剪、折叠或总结较早的轮次,并保持最新的轮次不变 | 让 API 编写摘要,或移除保留的轮次中的思考。 |
其他原因,例如跳过文本为空的思考块或重建已保存的对话,各有其修复方法:请参阅更改工具或指令而不会导致前缀检查失败。要找出您的原因,请让智能体检查您的代码。
未通过前缀检查时各模型的行为
在 Claude API 和部分云平台上,在未设置 prefix_mismatch_behavior 的情况下,未通过前缀检查的结果取决于模型:
| 具有保留思考的模型 | 结果 |
|---|---|
| Claude Fable 5.2 | 400 错误 |
| Claude Mythos 5.2 | 400 错误 |
| Claude Mythos 5.1 | 400 错误 |
| Claude Fable 5.1 | 可能出现 400 错误 |
| Claude Opus 5.5 | 可能出现 400 错误 |
| Claude Sonnet 5.5 | 可能出现 400 错误 |
| Claude Haiku 5.5 | 可能出现 400 错误 |
在表中显示"可能出现 400 错误"的地方,存在两种情况:未通过前缀检查的请求可能收到 400 错误,也可能不返回错误。当不返回错误时,未通过前缀检查的块会到达模型。Claude 会像读取其他任何输入一样读取它们,您需要为它们付费,而 Claude 会基于针对已发生更改的内容所做的推理来工作。
在这些模型上,您可以检查请求会遇到哪种情况,但仍应处理两种情况。
未设置 prefix_mismatch_behavior 的 Message Batches 条目不会收到错误,并且 API 可能会丢弃未通过前缀检查的块。
处理两种情况
要让每个请求都得到同一种结果,请将您的集成设计为仅追加,或设置 prefix_mismatch_behavior。否则,您的代码必须处理两种行为。
- 仅追加:遵循这三个习惯,您的代码就不会导致请求未通过前缀检查。
- 该设置:在每个请求中使用
"error",每次失败都是 400 错误,您可以在代码中捕获它。 - 两种行为:如果您不设置该设置,请捕获 400 错误,并留意到达 Claude 的未通过前缀检查的思考。
出现 400 错误后,在该对话后续的请求中发送 "drop_block" 即可恢复。这是有代价的:请参阅丢弃思考的代价。不要在您的所有对话中都发送它来掩盖错误。
技术细节
前缀检查的工作原理
具有保留思考的模型会在每个思考块的 signature 中记录其前缀的指纹。API 参考将前缀检查称为对话检查,而 400 错误会指出未通过前缀检查的块 is bound to a different conversation。
哪些模型会运行前缀检查
前缀检查在 Claude API 和部分云平台上运行。它会在发往这些模型之一的请求中,检查由具有保留思考的模型生成的思考块。在 Claude API 上,其他模型(例如 Claude Opus 5)不会运行前缀检查,前缀检查也不涵盖它们的块。
什么算作更改
- 每个块都会与其自身的记录进行比较。API 会将本次请求中某个块之前的内容,与 Claude 生成该块时其签名所记录的内容进行比较。您的上一个请求不起任何作用。前缀检查只关注内容,而不关注会话、ID 或用户。相同的内容出现在新请求或分叉中也能通过前缀检查。
- 前缀检查比较的是您发送的内容,而不是 Claude 读取的内容。在令牌阈值处进行压缩和上下文编辑只会改变 Claude 读取的内容,因此它们从不算作更改。在阈值压缩块之后,API 只比较从该块开始的消息,以及
system和tools。按需压缩有其自身的条件:请参阅保留的思考保持有效的条件。对于通过 URL 发送的文件,以及在 Claude API 上的 MCP 工具列表,API 比较的是它获取到的内容。 - 您发送的思考必须是一段连续不间断的序列。思考块不会作为内容进行比较。但除第一个之外,您发送的每个思考块都必须与 Claude 生成它时拥有相同的上一个思考块。因此,您可以省略最早的、最新的或全部思考块,但不能省略中间的某一个。
一次更改可能小到只是工具描述中的一个词。更改、插入、移动或删除一条更早的消息都算作更改,无论其角色是什么。在思考块所在的消息中更改其之前的文本也算作更改。
什么不算作更改
| 以下不算作更改 | 原因 |
|---|---|
model、effort、max_tokens、tool_choice、metadata、thinking.display 及其他参数 | 只比较 system、tools 和消息 |
| JSON 格式或键的顺序 | API 比较的是解析后的 JSON,而不是其文本 |
cache_control 标记 | 它们说明的是缓存位置,而不是 Claude 读取的内容 |
设置了 defer_loading: true 且尚未被任何内容引用的工具 | 在被引用之前,它不在 Claude 的上下文中 |
设置 prefix_mismatch_behavior
beta 标头 thinking-binding-controls-2026-08-01 会添加设置 thinking.block_binding.prefix_mismatch_behavior 以及响应中的 input_transformations 数组。
| 值 | 如果某个块未通过前缀检查 |
|---|---|
"error" | API 以 400 错误拒绝该请求,错误中会指出第一个无效的块 |
"drop_block" | API 丢弃无效的块,请求成功。您无需为这些块付费,但丢弃它们可能会花费更多 |
未设置,或为 null | 取决于模型 |
只要更改仍然存在,未通过前缀检查的块在之后每个发送它们的请求中都保持无效。该设置只作用于携带它的那一个请求。在关闭思考("disabled" 或 "between_tools")的情况下,发送该设置在所有模型上都会导致 400 错误。
以下请求将 prefix_mismatch_behavior 设置为 "drop_block"。在第一个轮次中没有需要发送回去的思考,因此返回的 input_transformations 为空:
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
thinking={
"type": "adaptive",
"block_binding": {"prefix_mismatch_behavior": "drop_block"},
},
messages=[
{
"role": "user",
"content": "What is the greatest common divisor of 1071 and 462?",
}
],
betas=["thinking-binding-controls-2026-08-01"],
)
for block in response.content:
if block.type == "text":
print(block.text)
print(f"Input transformations: {len(response.input_transformations or [])}")监控生产流量
在未设置 prefix_mismatch_behavior 的情况下,在部分模型上,未通过前缀检查的请求可能不会收到错误。这时未通过前缀检查的思考块会到达模型。要在生产流量中找出这些块,请发送 thinking-binding-controls-2026-08-01 beta 标头,不设置该设置,并记录每个响应的 input_transformations 数组。该标头会添加这个数组,并不会改变模型接收到的内容。
每个这样的块都会在该数组中拥有各自的 thinking_mismatch_allowed 条目。一个条目如下所示:
{
"input_transformations": [
{
"type": "thinking_mismatch_allowed",
"path": "messages.1.content.0",
"reason": "prefix_binding_mismatch"
}
]
}要找出更改,请参阅找出您的代码更改了什么。
检查您遇到哪种情况
在部分模型上,未通过前缀检查的请求可能收到 400 错误,也可能不返回错误。以下测试可以显示某个请求遇到的是哪一种:
- 选取一个包含这些模型之一所生成思考块的对话。更改或添加系统提示(
system)。该块随后会未通过前缀检查,因为系统提示在 Claude 生成该块之后发生了更改。 - 将其发送给同一模型,附带
thinking-binding-controls-2026-08-01beta 标头,并且不设置prefix_mismatch_behavior。
响应为以下之一:
- 一个显示
bound to a different conversation的 400 错误:该请求收到了 400 错误。 - 一个
input_transformations数组中包含thinking_mismatch_allowed条目的 200 响应:该请求没有收到错误。每个条目都指出一个未通过前缀检查并到达了 Claude 的思考块。 - 一个
input_transformations数组为空的 200 响应:没有块未通过前缀检查,因此该测试无法说明任何情况。请确认该对话包含来自该模型的思考块。
该结果只描述这一个请求,并不保证之后的请求会怎样,因此请编写代码来处理两种情况。
后续步骤
针对您的代码对对话所做的每种更改,应当改为发送什么。
恢复失败的对话,找出您的代码更改了什么,并测试问题是否已消除。
什么是保留思考、思考块与什么绑定,以及切换模型或账户。
此页面对您有帮助吗?