系统指令通常位于顶层 system 字段中,排在对话中所有消息之前。这个位置非常适合 prompt caching(提示缓存):系统提示是稳定前缀的一部分,因此后续轮次可以命中缓存。但对于您在会话进行到一半时才发现需要的指令来说,这个位置并不理想,因为编辑顶层 system 字段会改变提示的最开头部分,从而使其后所有内容的缓存失效。
对话中途的系统消息弥补了这一缺口。您可以在对话中新指令变得相关的位置追加一条 {"role": "system"} 消息,而不是编辑顶层 system 字段。缓存的前缀保持不变,因此下一个请求仍然可以从缓存中读取它,而新指令仍然作为系统指令应用,而不是作为普通的用户文本。
本页涵盖两个功能:对话中途的系统消息(已正式发布),以及对话中途的工具变更(随 Claude Opus 5 推出的测试版功能,将相同的方法应用于 tools 数组)。
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)对话中途的工具变更处于测试阶段。要使用它们,请在您的请求中包含测试版标头 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?