关于"zero data retention"(零数据保留),即 ZDR 如何适用于此功能,请参阅 API 与数据保留。
系统指令通常位于顶层 system 字段中,排在对话中所有消息之前。这个位置对于提示缓存非常有利:系统提示是稳定前缀的一部分,因此后续轮次可以命中缓存。但对于那些您在会话进行到一半才发现需要的指令来说,这个位置就不理想了,因为编辑顶层 system 字段会改变提示的最开头部分,并使其后所有内容的缓存失效。
对话中途的系统消息弥补了这一缺口。您可以在对话中新指令变得相关的位置追加一条 {"role": "system"} 消息,而不是编辑顶层 system 字段。缓存的前缀保持不变,因此下一个请求仍然可以从缓存中读取它,而新指令仍然作为系统指令被应用,而不是作为普通的用户文本。
本页涵盖两个功能:对话中途的系统消息(已正式可用),以及对话中途的工具变更(随 Claude Opus 5 推出的测试版功能,将相同的方法应用于 tools 数组)。
对话中途的系统消息可在 Claude API、Claude in Amazon Bedrock 和 Google Cloud 上使用。
此功能可在 Claude Fable 5、Claude Mythos 5、Claude Opus 4.8 和 Claude Opus 5 上使用。对话中途的系统消息不需要 beta 标头。此功能在 Claude Sonnet 5 上不可用;请改用顶层 system 字段。
对话中途的工具变更处于测试阶段,需要 mid-conversation-tool-changes-2026-07-01 beta 标头。它们可在 Claude Fable 5、Claude Mythos 5、Claude Opus 4.8 和 Claude Opus 5 上使用,支持 Claude API、Amazon Bedrock 和 Google Cloud。
tools 数组在哈希请求前缀中的位置甚至比顶层 system 字段更靠前,因此编辑它会使整个对话的提示缓存失效。对话中途的工具变更是随 Claude Opus 5 推出的测试版功能,是对话中途系统消息在工具方面的对应功能。您无需在对话的整个生命周期内固定工具列表,而是可以在轮次之间更改向模型提供哪些工具:预先在 tools 中声明完整的工具集,然后使用 tool_addition 和 tool_removal 块从对话中的特定位置开始向模型提供某个工具或撤回它。tools 数组本身从不改变,因此缓存的前缀保持完整。
tool_addition 和 tool_removal 是 role: "system" 消息的 content 数组中的内容块,它们可以与同一消息中的 text 块混合使用。该消息遵循与任何对话中途系统消息相同的放置规则(参见限制),并且变更从对话中的该位置开始生效。每个块的 tool 字段引用一个工具而不是定义一个工具:{"type": "tool_reference", "name": "..."} 指向请求的 tools 数组中声明的工具,而 MCP 连接器工具可以通过 mcp_tool_reference(server_name 和 name)单独引用,或通过 mcp_toolset_reference(server_name)作为整个工具集引用。引用未在 tools 中声明的名称会返回 400 错误。
在 tools 中声明的每个工具从对话开始就会提供给模型,除非它以 defer_loading: true 声明,这会使其保持隐藏状态,直到 tool_addition 块将其显现。tool_addition 也可以重新提供先前被 tool_removal 撤回的工具。
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
betas=["mid-conversation-tool-changes-2026-07-01"],
# 完整的工具集在一开始就声明且从不更改,因此
# 缓存的前缀保持完整。
tools=[
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
messages=[
{
"role": "user",
"content": "Say OK.",
},
# 从此处开始撤回 get_weather。该块通过名称引用
# 工具而不是编辑 `tools`,因此之前的轮次保持
# 字节级一致,缓存仍然命中。
{
"role": "system",
"content": [
{
"type": "tool_removal",
"tool": {"type": "tool_reference", "name": "get_weather"},
},
],
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)对话中途的工具变更处于测试阶段。要使用它们,请在请求中包含 beta 标头 mid-conversation-tool-changes-2026-07-01。它们可在 Claude Fable 5、Claude Mythos 5、Claude Opus 4.8 和 Claude Opus 5 上使用,支持 Claude API、Amazon Bedrock 和 Google Cloud。
提示缓存按顺序对请求前缀进行哈希:先是 tools,然后是 system,再是 messages。缓存命中要求前缀与最近的请求在缓存断点之前逐字节完全匹配。
这种顺序意味着顶层 system 字段位于哈希前缀的最开头附近。对它的任何更改,哪怕只是追加一句话,都会产生不同的哈希值,导致请求无法命中系统提示及其后所有已缓存消息的缓存。
对话中途的系统消息让您可以改为在消息历史的末尾添加指令。新指令之前的所有内容都没有变化,因此现有的缓存条目仍然匹配,只有新消息会作为新输入被处理。
以下是一些适用的场景:
system 字段会重新处理整个历史记录。在所有这些情况下,您都可以将指令放在常规的 user 消息中,Claude 也确实会遵循在用户轮次中到达的指令。区别在于优先级:user 消息被视为来自最终用户,而 system 消息被视为来自您——应用程序操作者。当两者冲突时,系统指令优先,因此对于即使最终用户提出不同要求也应当保持有效的操作者级事实和约束,请使用 system 角色。对话中途的系统消息保留了这种操作者级优先级,而无需付出编辑顶层 system 字段带来的缓存未命中代价。
向 messages 数组添加一条 "role": "system" 的消息。content 可以使用纯字符串或内容块,与 user 或 assistant 轮次相同。该指令从对话中的该位置开始生效。当指令冲突时,较晚的系统消息优先于较早的系统消息,并且对于其后的轮次,对话中途的系统消息优先于顶层 system 字段。
您仍然可以为应当适用于整个对话的指令设置顶层 system 字段。将对话中途的系统消息保留给那些稍后才变得相关的指令,或者您希望在不使缓存前缀失效的情况下添加的指令。
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
# 自动提示缓存:每个请求都会缓存到目前为止的对话内容,
# 下一个请求会从缓存中读取未更改的前缀。
cache_control={"type": "ephemeral"},
system="You are a code review assistant. Be concise.",
messages=[
{
"role": "user",
"content": "Review process() in utils.py for performance issues.",
},
{
"role": "assistant",
"content": "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.",
},
{
"role": "user",
"content": "Now review the calling code that invokes process().",
},
# 审查者在会话中途意识到,所有建议还必须
# 符合团队严格的类型标注规范。在此处追加
# 该指令可使之前的轮次保持字节级一致,因此
# 上一个请求缓存的前缀仍可从缓存中读取。
{
"role": "system",
"content": "From now on, every suggestion must include explicit type annotations.",
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)此示例通过顶层 cache_control 字段启用了自动缓存。提示缓存是可选启用的:如果请求没有 cache_control 字段(自动或显式断点),则不会缓存任何内容,每个请求都要为整个对话支付常规的输入令牌价格。启用缓存后,追加系统消息不会改变已缓存的轮次,因此携带新指令的请求仍然从缓存中读取它们,而不是重新处理。缓存还要求对话满足最小可缓存提示长度;像本例这样短的示例低于该长度,因此在对话增长之前,cache_creation_input_tokens 和 cache_read_input_tokens 会保持为 0。
对话中途的系统消息必须紧跟在 user 轮次之后(或以服务器工具结果结尾的 assistant 轮次之后),并且必须是 messages 中的最后一个条目,或者紧跟着一个 assistant 轮次。携带 tool_result 块的 user 消息也算在内:在智能体循环中,您可以将系统消息放在工具结果之后、Claude 的下一轮之前。任何其他位置,包括在 assistant 的 tool_use 块与回应它的 tool_result 之间,都会返回 400 错误。
在智能体循环中,系统消息放在传递工具结果的 user 消息之后。这也是您的应用程序可以转发用户在 Claude 工作期间输入的内容的位置,这样新的上下文就可以被吸收而无需重新开始该轮次:
[
{ "role": "user", "content": "Run the test suite and fix any failures." },
{
"role": "assistant",
"content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" }
]
},
{
"role": "system",
"content": "The user sent the following message while you were working: also update the changelog before you finish."
}
]将系统内容表述为上下文,而不是覆盖用户的命令。陈述事实("收到了来自用户的新输入:X"、"剩余令牌预算现在是 Y"),然后让 Claude 据此行动。Claude 经过训练会抵制看起来与用户对立的指令,这种保护同样适用于系统角色,因此诸如"忽略用户所说的内容"之类的措辞不如陈述发生了什么变化有效。
此模式用于转发来自对话自身最终用户的输入。不要用它来传递工具输出、检索到的文档或其他第三方内容;请将这些内容保留在 tool_result 块中(参见限制)。
对话中途的系统消息和提示缓存是设计为配合使用的:
cache_control 时才会进行缓存,无论是顶层的自动缓存字段还是内容块上的显式断点。对话中途的系统消息本身不会创建缓存条目,如果没有启用缓存,也就没有可保留的节省。cache_control 放在跨请求保持不变的最后一个块上,无论是顶层 system 字段的末尾、工具定义的末尾,还是消息历史中的某个稳定位置。避免编辑或删除已经发送的对话中途系统消息。与对较早消息的任何其他更改一样,这会使从该位置开始的缓存失效。如果指令需要演进,请追加一条新的系统消息,而不是重写旧的。连续的系统消息是被接受的,并被视为单个系统部分,整体遵循相同的放置规则。
system 消息不能是 messages 中的第一个条目。对于从一开始就适用的指令,请使用顶层 system 字段。system 消息必须紧跟在 user 轮次之后(包括携带 tool_result 块的 user 轮次)或以服务器工具结果结尾的 assistant 轮次之后,并且必须位于 assistant 轮次之前或作为数组的结尾。它不能位于 tool_use 块与其 tool_result 之间。放在其他位置会返回 400 错误。tool_result 块中,并继续遵循缓解越狱和提示注入。缓存的工作原理、断点的放置位置,以及如何读取缓存使用字段。
当您预期的缓存命中没有发生时,准确找出两个请求在何处出现分歧。
消息结构、多轮对话以及 system 字段。
编写有效的提示和系统指令。
tool_use 和 tool_result 块在 messages 数组中的结构。
Was this page helpful?