跳转到主要内容
扩展思考为 Claude 提供了增强的推理能力,用于处理复杂任务,同时在提供最终答案之前提供不同级别的透明度来了解其逐步思考过程。

支持的模型

以下模型支持扩展思考:
  • Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)
  • Claude Sonnet 4 (claude-sonnet-4-20250514)
  • Claude Sonnet 3.7 (claude-3-7-sonnet-20250219) (已弃用)
  • Claude Haiku 4.5 (claude-haiku-4-5-20251001)
  • Claude Opus 4.1 (claude-opus-4-1-20250805)
  • Claude Opus 4 (claude-opus-4-20250514)
API 行为在 Claude Sonnet 3.7 和 Claude 4 模型之间有所不同,但 API 形状保持完全相同。有关更多信息,请参阅不同模型版本间的思考差异

扩展思考的工作原理

启用扩展思考后,Claude 会创建 thinking 内容块,其中输出其内部推理。Claude 在制作最终响应之前会结合来自此推理的见解。 API 响应将包括 thinking 内容块,后跟 text 内容块。 以下是默认响应格式的示例:
{
  "content": [
    {
      "type": "thinking",
      "thinking": "让我逐步分析这个问题...",
      "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL...."
    },
    {
      "type": "text",
      "text": "根据我的分析..."
    }
  ]
}
有关扩展思考响应格式的更多信息,请参阅消息 API 参考

如何使用扩展思考

以下是在消息 API 中使用扩展思考的示例:
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-sonnet-4-5",
    "max_tokens": 16000,
    "thinking": {
        "type": "enabled",
        "budget_tokens": 10000
    },
    "messages": [
        {
            "role": "user",
            "content": "Are there an infinite number of prime numbers such that n mod 4 == 3?"
        }
    ]
}'
要启用扩展思考,添加一个 thinking 对象,将 type 参数设置为 enabled,并将 budget_tokens 设置为扩展思考的指定令牌预算。 budget_tokens 参数确定 Claude 允许用于其内部推理过程的最大令牌数。在 Claude 4 模型中,此限制适用于完整思考令牌,而不适用于总结输出。更大的预算可以通过为复杂问题启用更彻底的分析来改进响应质量,尽管 Claude 可能不会使用整个分配的预算,特别是在 32k 以上的范围内。 budget_tokens 必须设置为小于 max_tokens 的值。但是,当使用与工具交错的思考时,您可以超过此限制,因为令牌限制变成您的整个上下文窗口(200k 令牌)。

总结思考

启用扩展思考后,Claude 4 模型的消息 API 返回 Claude 完整思考过程的摘要。总结思考提供了扩展思考的全部智能优势,同时防止滥用。 以下是总结思考的一些重要考虑事项:
  • 您需要为原始请求生成的完整思考令牌付费,而不是摘要令牌。
  • 计费的输出令牌计数将不匹配您在响应中看到的令牌计数。
  • 思考输出的前几行更详细,提供了详细的推理,这对提示工程目的特别有帮助。
  • 随着 Anthropic 寻求改进扩展思考功能,总结行为可能会发生变化。
  • 总结保留了 Claude 思考过程的关键思想,增加的延迟最少,实现了可流式传输的用户体验,并便于从 Claude Sonnet 3.7 迁移到 Claude 4 模型。
  • 总结由与您在请求中指定的模型不同的模型处理。思考模型看不到总结的输出。
Claude Sonnet 3.7 继续返回完整的思考输出。在需要访问 Claude 4 模型完整思考输出的罕见情况下,请联系我们的销售团队

流式思考

您可以使用服务器发送事件 (SSE)流式传输扩展思考响应。 启用扩展思考的流式传输后,您会通过 thinking_delta 事件接收思考内容。 有关通过消息 API 流式传输的更多文档,请参阅流式消息 以下是如何处理思考流式传输的方法:
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-sonnet-4-5",
    "max_tokens": 16000,
    "stream": true,
    "thinking": {
        "type": "enabled",
        "budget_tokens": 10000
    },
    "messages": [
        {
            "role": "user",
            "content": "What is 27 * 453?"
        }
    ]
}'
示例流式输出:
event: message_start
data: {"type": "message_start", "message": {"id": "msg_01...", "type": "message", "role": "assistant", "content": [], "model": "claude-sonnet-4-5", "stop_reason": null, "stop_sequence": null}}

event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "让我逐步解决这个问题:\n\n1. 首先分解 27 * 453"}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\n2. 453 = 400 + 50 + 3"}}

// 其他思考增量...

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "EqQBCgIYAhIM1gbcDa9GJwZA2b3hGgxBdjrkzLoky3dl1pkiMOYds..."}}

event: content_block_stop
data: {"type": "content_block_stop", "index": 0}

event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "text", "text": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "text_delta", "text": "27 * 453 = 12,231"}}

// 其他文本增量...

event: content_block_stop
data: {"type": "content_block_stop", "index": 1}

event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": null}}

event: message_stop
data: {"type": "message_stop"}
使用流式传输启用思考时,您可能会注意到文本有时以较大的块到达,交替使用较小的逐令牌传递。这是预期的行为,特别是对于思考内容。流式传输系统需要分批处理内容以获得最佳性能,这可能导致这种”分块”传递模式,流式传输事件之间可能会有延迟。我们正在不断努力改进这种体验,未来的更新将专注于使思考内容流式传输更平滑。

扩展思考与工具使用

扩展思考可以与工具使用一起使用,允许 Claude 通过工具选择和结果处理进行推理。 使用扩展思考和工具使用时,请注意以下限制:
  1. 工具选择限制:思考的工具使用仅支持 tool_choice: {"type": "auto"} (默认) 或 tool_choice: {"type": "none"}。使用 tool_choice: {"type": "any"}tool_choice: {"type": "tool", "name": "..."} 将导致错误,因为这些选项强制工具使用,这与扩展思考不兼容。
  2. 保留思考块:在工具使用期间,您必须将 thinking 块传回 API 以用于最后的助手消息。将完整的未修改块传回 API 以保持推理连续性。

在对话中切换思考模式

您不能在助手转向的中间切换思考,包括在工具使用循环期间。整个助手转向必须在单一思考模式下运行:
  • 如果启用了思考,最终助手转向必须以思考块开始。
  • 如果禁用了思考,最终助手转向不得包含任何思考块
从模型的角度来看,工具使用循环是助手转向的一部分。助手转向在 Claude 完成其完整响应后才完成,这可能包括多个工具调用和结果。 例如,此序列都是单个助手转向的一部分:
用户:"巴黎的天气如何?"
助手:[thinking] + [tool_use: get_weather]
用户:[tool_result: "20°C,晴天"]
助手:[text: "巴黎的天气是 20°C,晴天"]
尽管有多个 API 消息,但工具使用循环在概念上是一个连续助手响应的一部分。

常见错误场景

您可能会遇到此错误:
Expected `thinking` or `redacted_thinking`, but found `tool_use`.
When `thinking` is enabled, a final `assistant` message must start
with a thinking block (preceding the lastmost set of `tool_use` and
`tool_result` blocks).
这通常发生在以下情况:
  1. 您在工具使用序列期间禁用了思考
  2. 您想再次启用思考
  3. 您的最后一条助手消息包含工具使用块但没有思考块

实用指导

✗ 无效:在工具使用后立即切换思考
用户:"天气如何?"
助手:[tool_use] (思考已禁用)
用户:[tool_result]
// 无法在此处启用思考 - 仍在同一助手转向中
✓ 有效:首先完成助手转向
用户:"天气如何?"
助手:[tool_use] (思考已禁用)
用户:[tool_result]
助手:[text: "天气晴朗"] 
用户:"明天呢?" (思考已禁用)
助手:[thinking] + [text: "..."] (思考已启用 - 新转向)
最佳实践:在每个转向开始时规划您的思考策略,而不是尝试在中途切换。
切换思考模式也会使消息历史的提示缓存失效。有关更多详情,请参阅扩展思考与提示缓存部分。
以下是一个实际示例,展示了在提供工具结果时如何保留思考块:
weather_tool = {
    "name": "get_weather",
    "description": "Get current weather for a location",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
        "required": ["location"]
    }
}

# 第一个请求 - Claude 用思考和工具请求响应
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[weather_tool],
    messages=[
        {"role": "user", "content": "What's the weather in Paris?"}
    ]
)
API 响应将包括思考、文本和 tool_use 块:
{
    "content": [
        {
            "type": "thinking",
            "thinking": "用户想知道巴黎的当前天气。我可以访问一个函数 `get_weather`...",
            "signature": "BDaL4VrbR2Oj0hO4XpJxT28J5TILnCrrUXoKiiNBZW9P+nr8XSj1zuZzAl4egiCCpQNvfyUuFFJP5CncdYZEQPPmLxYsNrcs...."
        },
        {
            "type": "text",
            "text": "我可以帮您获取巴黎的当前天气信息。让我为您检查一下"
        },
        {
            "type": "tool_use",
            "id": "toolu_01CswdEQBMshySk6Y9DFKrfq",
            "name": "get_weather",
            "input": {
                "location": "Paris"
            }
        }
    ]
}
现在让我们继续对话并使用该工具
# 提取思考块和工具使用块
thinking_block = next((block for block in response.content
                      if block.type == 'thinking'), None)
tool_use_block = next((block for block in response.content
                      if block.type == 'tool_use'), None)

# 调用您的实际天气 API,这是您实际 API 调用的地方
# 假设这是我们得到的返回
weather_data = {"temperature": 88}

# 第二个请求 - 包括思考块和工具结果
# 响应中不会生成新的思考块
continuation = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[weather_tool],
    messages=[
        {"role": "user", "content": "What's the weather in Paris?"},
        # 注意 thinking_block 和 tool_use_block 都被传入
        # 如果不传入,将引发错误
        {"role": "assistant", "content": [thinking_block, tool_use_block]},
        {"role": "user", "content": [{
            "type": "tool_result",
            "tool_use_id": tool_use_block.id,
            "content": f"Current temperature: {weather_data['temperature']}°F"
        }]}
    ]
)
API 响应现在包括文本
{
    "content": [
        {
            "type": "text",
            "text": "巴黎目前的温度是 88°F (31°C)"
        }
    ]
}

保留思考块

在工具使用期间,您必须将 thinking 块传回 API,并且必须将完整的未修改块传回 API。这对于维持模型的推理流和对话完整性至关重要。
虽然您可以从之前的 assistant 角色转向中省略 thinking 块,但我们建议始终将所有思考块传回 API 以进行任何多轮对话。API 将:
  • 自动过滤提供的思考块
  • 使用必要的相关思考块来保留模型的推理
  • 仅对显示给 Claude 的块的输入令牌进行计费
在对话中切换思考模式时,请记住整个助手转向(包括工具使用循环)必须在单一思考模式下运行。有关更多详情,请参阅在对话中切换思考模式
当 Claude 调用工具时,它暂停了响应的构造以等待外部信息。当返回工具结果时,Claude 将继续构建该现有响应。这需要在工具使用期间保留思考块,原因有两个:
  1. 推理连续性:思考块捕获了导致工具请求的 Claude 逐步推理。当您发布工具结果时,包括原始思考可确保 Claude 能够从中断的地方继续推理。
  2. 上下文维护:虽然工具结果在 API 结构中显示为用户消息,但它们是连续推理流的一部分。保留思考块在多个 API 调用中维持这个概念流。有关上下文管理的更多信息,请参阅我们的上下文窗口指南
重要:提供 thinking 块时,连续 thinking 块的整个序列必须与模型在原始请求期间生成的输出相匹配;您不能重新排列或修改这些块的序列。

交错思考

Claude 4 模型中的扩展思考与工具使用支持交错思考,这使 Claude 能够在工具调用之间进行思考,并在接收工具结果后进行更复杂的推理。 通过交错思考,Claude 可以:
  • 在决定下一步之前对工具调用的结果进行推理
  • 在推理步骤之间链接多个工具调用
  • 根据中间结果做出更细致的决定
要启用交错思考,请将 beta 标头 interleaved-thinking-2025-05-14 添加到您的 API 请求中。 以下是交错思考的一些重要考虑事项:
  • 使用交错思考,budget_tokens 可以超过 max_tokens 参数,因为它代表一个助手转向内所有思考块的总预算。
  • 交错思考仅支持通过消息 API 使用的工具
  • 交错思考仅支持 Claude 4 模型,使用 beta 标头 interleaved-thinking-2025-05-14
  • 直接调用 Claude API 允许您将 interleaved-thinking-2025-05-14 传递到任何模型的请求中,没有任何效果。
  • 在第三方平台(例如 Amazon BedrockVertex AI)上,如果您将 interleaved-thinking-2025-05-14 传递到除 Claude Opus 4.1、Opus 4 或 Sonnet 4 之外的任何模型,您的请求将失败。
import anthropic

client = anthropic.Anthropic()

# 定义工具
calculator_tool = {
    "name": "calculator",
    "description": "Perform mathematical calculations",
    "input_schema": {
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "Mathematical expression to evaluate"
            }
        },
        "required": ["expression"]
    }
}

database_tool = {
    "name": "database_query",
    "description": "Query product database",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "SQL query to execute"
            }
        },
        "required": ["query"]
    }
}

# 第一个请求 - Claude 在所有工具调用之前思考一次
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    messages=[{
        "role": "user",
        "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
    }]
)

# 响应包括思考后跟工具使用
# 注意:Claude 在开始时思考一次,然后做出所有工具决定
print("First response:")
for block in response.content:
    if block.type == "thinking":
        print(f"Thinking (summarized): {block.thinking}")
    elif block.type == "tool_use":
        print(f"Tool use: {block.name} with input {block.input}")
    elif block.type == "text":
        print(f"Text: {block.text}")

# 您将执行工具并返回结果...
# 获得两个工具结果后,Claude 直接响应而不进行额外思考
在此不使用交错思考的示例中:
  1. Claude 在开始时思考一次以理解任务
  2. 提前做出所有工具使用决定
  3. 当返回工具结果时,Claude 立即提供响应而不进行额外思考
import anthropic

client = anthropic.Anthropic()

# 与之前相同的工具定义
calculator_tool = {
    "name": "calculator",
    "description": "Perform mathematical calculations",
    "input_schema": {
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "Mathematical expression to evaluate"
            }
        },
        "required": ["expression"]
    }
}

database_tool = {
    "name": "database_query",
    "description": "Query product database",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "SQL query to execute"
            }
        },
        "required": ["query"]
    }
}

# 启用交错思考的第一个请求
response = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[{
        "role": "user",
        "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
    }]
)

print("Initial response:")
thinking_blocks = []
tool_use_blocks = []

for block in response.content:
    if block.type == "thinking":
        thinking_blocks.append(block)
        print(f"Thinking: {block.thinking}")
    elif block.type == "tool_use":
        tool_use_blocks.append(block)
        print(f"Tool use: {block.name} with input {block.input}")
    elif block.type == "text":
        print(f"Text: {block.text}")

# 第一个工具结果(计算器)
calculator_result = "7500"  # 150 * 50

# 继续第一个工具结果
response2 = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[
        {
            "role": "user",
            "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
        },
        {
            "role": "assistant",
            "content": [thinking_blocks[0], tool_use_blocks[0]]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[0].id,
                "content": calculator_result
            }]
        }
    ]
)

print("\nAfter calculator result:")
# 使用交错思考,Claude 可以在决定查询数据库之前对计算器结果进行思考
for block in response2.content:
    if block.type == "thinking":
        thinking_blocks.append(block)
        print(f"Interleaved thinking: {block.thinking}")
    elif block.type == "tool_use":
        tool_use_blocks.append(block)
        print(f"Tool use: {block.name} with input {block.input}")

# 第二个工具结果(数据库)
database_result = "5200"  # 示例平均月收入

# 继续第二个工具结果
response3 = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[
        {
            "role": "user",
            "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
        },
        {
            "role": "assistant",
            "content": [thinking_blocks[0], tool_use_blocks[0]]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[0].id,
                "content": calculator_result
            }]
        },
        {
            "role": "assistant",
            "content": thinking_blocks[1:] + tool_use_blocks[1:]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[1].id,
                "content": database_result
            }]
        }
    ]
)

print("\nAfter database result:")
# 使用交错思考,Claude 可以在制定最终响应之前对两个结果进行思考
for block in response3.content:
    if block.type == "thinking":
        print(f"Final thinking: {block.thinking}")
    elif block.type == "text":
        print(f"Final response: {block.text}")
在此使用交错思考的示例中:
  1. Claude 最初思考任务
  2. 收到计算器结果后,Claude 可以再次思考该结果的含义
  3. Claude 然后根据第一个结果决定如何查询数据库
  4. 收到数据库结果后,Claude 在制定最终响应之前再思考一次两个结果
  5. 思考预算分布在转向内的所有思考块中
这种模式允许更复杂的推理链,其中每个工具的输出通知下一个决定。

扩展思考与提示缓存

提示缓存与思考有几个重要考虑事项:
扩展思考任务通常需要超过 5 分钟才能完成。考虑使用1 小时缓存持续时间来在较长的思考会话和多步工作流中维持缓存命中。
思考块上下文移除
  • 来自先前转向的思考块从上下文中移除,这可能影响缓存断点
  • 继续与工具使用的对话时,思考块被缓存并在从缓存读取时计为输入令牌
  • 这产生了一个权衡:虽然思考块在视觉上不消耗上下文窗口空间,但在缓存时它们仍然计入您的输入令牌使用
  • 如果思考被禁用,如果您在当前工具使用转向中传递思考内容,请求将失败。在其他情况下,传递给 API 的思考内容被简单地忽略
缓存失效模式
  • 对思考参数的更改(启用/禁用或预算分配)使消息缓存断点失效
  • 交错思考放大了缓存失效,因为思考块可以在多个工具调用之间发生
  • 系统提示和工具尽管思考参数更改或块移除仍保持缓存
虽然思考块被移除用于缓存和上下文计算,但在继续与工具使用的对话时必须保留它们,特别是使用交错思考时。

理解思考块缓存行为

使用扩展思考与工具使用时,思考块表现出特定的缓存行为,影响令牌计数: 工作原理:
  1. 缓存仅在您发出包含工具结果的后续请求时发生
  2. 当发出后续请求时,先前的对话历史(包括思考块)可以被缓存
  3. 这些缓存的思考块在从缓存读取时计为输入令牌在您的使用指标中
  4. 当包含非工具结果用户块时,所有先前的思考块被忽略并从上下文中剥离
详细示例流程: 请求 1:
用户:"巴黎的天气如何?"
响应 1:
[thinking_block_1] + [tool_use block 1]
请求 2:
用户:["巴黎的天气如何?"], 
助手:[thinking_block_1] + [tool_use block 1], 
用户:[tool_result_1, cache=True]
响应 2:
[thinking_block_2] + [text block 2]
请求 2 写入请求内容的缓存(不是响应)。缓存包括原始用户消息、第一个思考块、工具使用块和工具结果。 请求 3:
用户:["巴黎的天气如何?"], 
助手:[thinking_block_1] + [tool_use block 1], 
用户:[tool_result_1, cache=True], 
助手:[thinking_block_2] + [text block 2], 
用户:[文本响应, cache=True]
因为包含了非工具结果用户块,所有先前的思考块被忽略。此请求将被处理为相同:
用户:["巴黎的天气如何?"], 
助手:[tool_use block 1], 
用户:[tool_result_1, cache=True], 
助手:[text block 2], 
用户:[文本响应, cache=True]
关键点:
  • 这种缓存行为自动发生,即使没有显式 cache_control 标记
  • 这种行为在使用常规思考或交错思考时是一致的
from anthropic import Anthropic
import requests
from bs4 import BeautifulSoup

client = Anthropic()

def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # 移除脚本和样式元素
    for script in soup(["script", "style"]):
        script.decompose()

    # 获取文本
    text = soup.get_text()

    # 分解为行并移除每行上的前导和尾随空格
    lines = (line.strip() for line in text.splitlines())
    # 将多个标题分解为每行一个
    chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
    # 删除空行
    text = '\n'.join(chunk for chunk in chunks if chunk)

    return text

# 获取文章内容
book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
book_content = fetch_article_content(book_url)
# 仅使用足够的文本用于缓存(前几章)
LARGE_TEXT = book_content[:5000]

SYSTEM_PROMPT=[
    {
        "type": "text",
        "text": "您是一个被指派进行文学分析的 AI 助手。仔细分析以下文本。",
    },
    {
        "type": "text",
        "text": LARGE_TEXT,
        "cache_control": {"type": "ephemeral"}
    }
]

MESSAGES = [
    {
        "role": "user",
        "content": "分析这段文字的语气。"
    }
]

# 第一个请求 - 建立缓存
print("第一个请求 - 建立缓存")
response1 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    system=SYSTEM_PROMPT,
    messages=MESSAGES
)

print(f"第一个响应使用情况:{response1.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response1.content
})
MESSAGES.append({
    "role": "user",
    "content": "分析这段文字中的人物。"
})
# 第二个请求 - 相同的思考参数(预期缓存命中)
print("\n第二个请求 - 相同的思考参数(预期缓存命中)")
response2 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    system=SYSTEM_PROMPT,
    messages=MESSAGES
)

print(f"第二个响应使用情况:{response2.usage}")

# 第三个请求 - 不同的思考参数(消息缓存未命中)
print("\n第三个请求 - 不同的思考参数(消息缓存未命中)")
response3 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 8000  # 更改思考预算
    },
    system=SYSTEM_PROMPT,  # 系统提示保持缓存
    messages=MESSAGES  # 消息缓存失效
)

print(f"第三个响应使用情况:{response3.usage}")
from anthropic import Anthropic
import requests
from bs4 import BeautifulSoup

client = Anthropic()

def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # 移除脚本和样式元素
    for script in soup(["script", "style"]):
        script.decompose()

    # 获取文本
    text = soup.get_text()

    # 分解为行并移除每行上的前导和尾随空格
    lines = (line.strip() for line in text.splitlines())
    # 将多个标题分解为每行一个
    chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
    # 删除空行
    text = '\n'.join(chunk for chunk in chunks if chunk)

    return text

# 获取文章内容
book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
book_content = fetch_article_content(book_url)
# 仅使用足够的文本用于缓存(前几章)
LARGE_TEXT = book_content[:5000]

# 无系统提示 - 改为在消息中缓存
MESSAGES = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": LARGE_TEXT,
                "cache_control": {"type": "ephemeral"},
            },
            {
                "type": "text",
                "text": "分析这段文字的语气。"
            }
        ]
    }
]

# 第一个请求 - 建立缓存
print("第一个请求 - 建立缓存")
response1 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    messages=MESSAGES
)

print(f"第一个响应使用情况:{response1.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response1.content
})
MESSAGES.append({
    "role": "user",
    "content": "分析这段文字中的人物。"
})
# 第二个请求 - 相同的思考参数(预期缓存命中)
print("\n第二个请求 - 相同的思考参数(预期缓存命中)")
response2 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000  # 相同的思考预算
    },
    messages=MESSAGES
)

print(f"第二个响应使用情况:{response2.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response2.content
})
MESSAGES.append({
    "role": "user",
    "content": "分析这段文字中的设置。"
})

# 第三个请求 - 不同的思考预算(预期缓存未命中)
print("\n第三个请求 - 不同的思考预算(预期缓存未命中)")
response3 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 8000  # 不同的思考预算破坏缓存
    },
    messages=MESSAGES
)

print(f"第三个响应使用情况:{response3.usage}")
以下是脚本的输出(您可能会看到略有不同的数字)
第一个请求 - 建立缓存
第一个响应使用情况:{ cache_creation_input_tokens: 1370, cache_read_input_tokens: 0, input_tokens: 17, output_tokens: 700 }

第二个请求 - 相同的思考参数(预期缓存命中)

第二个响应使用情况:{ cache_creation_input_tokens: 0, cache_read_input_tokens: 1370, input_tokens: 303, output_tokens: 874 }

第三个请求 - 不同的思考预算(预期缓存未命中)
第三个响应使用情况:{ cache_creation_input_tokens: 1370, cache_read_input_tokens: 0, input_tokens: 747, output_tokens: 619 }
此示例演示了当缓存在消息数组中设置时,更改思考参数(budget_tokens 从 4000 增加到 8000)使缓存失效。第三个请求显示没有缓存命中,cache_creation_input_tokens=1370cache_read_input_tokens=0,证明当思考参数更改时消息缓存失效。

扩展思考的最大令牌和上下文窗口大小

在较旧的 Claude 模型(Claude Sonnet 3.7 之前),如果提示令牌和 max_tokens 的总和超过模型的上下文窗口,系统会自动调整 max_tokens 以适应上下文限制。这意味着您可以设置一个大的 max_tokens 值,系统会根据需要静默减少它。 使用 Claude 3.7 和 4 模型,max_tokens(启用思考时包括您的思考预算)被强制执行为严格限制。如果提示令牌 + max_tokens 超过上下文窗口大小,系统现在将返回验证错误。
您可以阅读我们的上下文窗口指南以获得更深入的了解。

扩展思考的上下文窗口

使用启用思考的上下文窗口计算时,需要注意一些考虑事项:
  • 来自先前转向的思考块被剥离并不计入您的上下文窗口
  • 当前转向思考计入该转向的 max_tokens 限制
下面的图表演示了启用扩展思考时的专门令牌管理: 带扩展思考的上下文窗口图 有效的上下文窗口计算为:
context window =
  (current input tokens - previous thinking tokens) +
  (thinking tokens + encrypted thinking tokens + text output tokens)
我们建议使用令牌计数 API为您的特定用例获得准确的令牌计数,特别是在处理包含思考的多轮对话时。

扩展思考和工具使用的上下文窗口

使用扩展思考和工具使用时,思考块必须显式保留并与工具结果一起返回。 扩展思考和工具使用的有效上下文窗口计算变为:
context window =
  (current input tokens + previous thinking tokens + tool use tokens) +
  (thinking tokens + encrypted thinking tokens + text output tokens)
下面的图表说明了扩展思考和工具使用的令牌管理: 带扩展思考和工具使用的上下文窗口图

使用扩展思考管理令牌

鉴于 Claude 3.7 和 4 模型的上下文窗口和 max_tokens 行为,您可能需要:
  • 更积极地监控和管理您的令牌使用
  • 随着提示长度的变化调整 max_tokens
  • 可能更频繁地使用令牌计数端点
  • 意识到先前的思考块不会在您的上下文窗口中累积
这一变化是为了提供更可预测和透明的行为,特别是因为最大令牌限制已大幅增加。

思考加密

完整的思考内容被加密并在 signature 字段中返回。此字段用于验证思考块是否由 Claude 生成,当传回 API 时。
仅在使用带扩展思考的工具时严格需要发送回思考块。否则,您可以从先前的转向中省略思考块,或者如果您传回它们,让 API 为您剥离它们。如果发送回思考块,我们建议为了一致性和避免潜在问题,将所有内容按接收方式传回。
以下是关于思考加密的一些重要考虑事项:
  • 流式传输响应时,签名通过 content_block_delta 事件内的 signature_deltacontent_block_stop 事件之前添加。
  • signature 值在 Claude 4 模型中的长度明显长于先前的模型。
  • signature 字段是一个不透明字段,不应被解释或解析 - 它仅出于验证目的而存在。
  • signature 值在平台之间兼容(Claude API、Amazon BedrockVertex AI)。在一个平台上生成的值将与另一个平台兼容。

思考编辑

偶尔 Claude 的内部推理会被我们的安全系统标记。当这种情况发生时,我们会加密 thinking 块的部分或全部,并将其作为 redacted_thinking 块返回给您。redacted_thinking 块在传回 API 时被解密,允许 Claude 继续其响应而不会失去上下文。 在构建使用扩展思考的面向客户的应用程序时:
  • 意识到编辑的思考块包含不可读的加密内容
  • 考虑提供简单的解释,例如:“Claude 的一些内部推理已自动加密以确保安全。这不会影响响应的质量。”
  • 如果向用户显示思考块,您可以过滤掉编辑的块,同时保留正常的思考块
  • 透明地说明使用扩展思考功能可能偶尔导致某些推理被加密
  • 实现适当的错误处理以优雅地管理编辑的思考而不破坏您的 UI
以下是显示正常和编辑思考块的示例:
{
  "content": [
    {
      "type": "thinking",
      "thinking": "让我逐步分析这个...",
      "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL...."
    },
    {
      "type": "redacted_thinking",
      "data": "EmwKAhgBEgy3va3pzix/LafPsn4aDFIT2Xlxh0L5L8rLVyIwxtE3rAFBa8cr3qpPkNRj2YfWXGmKDxH4mPnZ5sQ7vB9URj2pLmN3kF8/dW5hR7xJ0aP1oLs9yTcMnKVf2wRpEGjH9XZaBt4UvDcPrQ..."
    },
    {
      "type": "text",
      "text": "根据我的分析..."
    }
  ]
}
在您的输出中看到编辑的思考块是预期的行为。模型仍然可以使用这个编辑的推理来通知其响应,同时维持安全护栏。如果您需要在应用程序中测试编辑的思考处理,您可以使用此特殊测试字符串作为您的提示:ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB
在多轮对话中将 thinkingredacted_thinking 块传回 API 时,您必须将完整的未修改块传回 API 以用于最后的助手转向。这对于维持模型的推理流至关重要。我们建议始终将所有思考块传回 API。有关更多详情,请参阅上面的保留思考块部分。
此示例演示了如何处理当 Claude 的内部推理包含被安全系统标记的内容时可能出现的 redacted_thinking 块:
import anthropic

client = anthropic.Anthropic()

# 使用触发编辑思考的特殊提示(仅用于演示目的)
response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    messages=[{
        "role": "user",
        "content": "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB"
    }]
)

# 识别编辑的思考块
has_redacted_thinking = any(
    block.type == "redacted_thinking" for block in response.content
)

if has_redacted_thinking:
    print("响应包含编辑的思考块")
    # 这些块在后续请求中仍然可用

    # 提取所有块(编辑和非编辑)
    all_thinking_blocks = [
        block for block in response.content
        if block.type in ["thinking", "redacted_thinking"]
    ]

    # 在传递到后续请求时,包括所有块而不进行修改
    # 这保留了 Claude 推理的完整性

    print(f"找到 {len(all_thinking_blocks)} 个思考块总计")
    print(f"这些块仍然可计费为输出令牌")

不同模型版本间的思考差异

消息 API 在 Claude Sonnet 3.7 和 Claude 4 模型之间处理思考的方式不同,主要是在编辑和总结行为方面。 请参阅下表以获得简明比较:
功能Claude Sonnet 3.7Claude 4 模型
思考输出返回完整思考输出返回总结思考
交错思考不支持支持 interleaved-thinking-2025-05-14 beta 标头

定价

扩展思考使用标准令牌定价方案:
模型基础输入令牌缓存写入缓存命中输出令牌
Claude Opus 4.1$15 / MTok$18.75 / MTok$1.50 / MTok$75 / MTok
Claude Opus 4$15 / MTok$18.75 / MTok$1.50 / MTok$75 / MTok
Claude Sonnet 4.5$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
Claude Sonnet 4$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
Claude Sonnet 3.7$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
思考过程产生的费用包括:
  • 思考期间使用的令牌(输出令牌)
  • 后续请求中包含的最后一个助手转向的思考块(输入令牌)
  • 标准文本输出令牌
启用扩展思考时,会自动包含专门的系统提示来支持此功能。
使用总结思考时:
  • 输入令牌:原始请求中的令牌(不包括先前转向的思考令牌)
  • 输出令牌(计费):Claude 内部生成的原始思考令牌
  • 输出令牌(可见):您在响应中看到的总结思考令牌
  • 无费用:用于生成摘要的令牌
计费的输出令牌计数将不匹配响应中的可见令牌计数。您需要为完整的思考过程付费,而不是您看到的摘要。

扩展思考的最佳实践和考虑事项

使用思考预算

  • **预算优化:**最小预算为 1,024 令牌。我们建议从最小值开始,逐步增加思考预算以找到您用例的最优范围。更高的令牌计数可以实现更全面的推理,但根据任务的不同会有递减的回报。增加预算可以改进响应质量,但代价是增加延迟。对于关键任务,测试不同的设置以找到最优平衡。请注意,思考预算是一个目标而不是严格限制 - 实际令牌使用可能因任务而异。
  • **起点:**对于复杂任务,从较大的思考预算(16k+ 令牌)开始,并根据您的需要进行调整。
  • **大型预算:**对于思考预算超过 32k,我们建议使用批处理以避免网络问题。推动模型思考超过 32k 令牌的请求会导致长时间运行的请求,可能会遇到系统超时和开放连接限制。
  • **令牌使用跟踪:**监控思考令牌使用以优化成本和性能。

性能考虑

  • **响应时间:**准备好可能更长的响应时间,因为推理过程需要额外的处理。考虑到生成思考块可能会增加总体响应时间。
  • **流式传输要求:**当 max_tokens 大于 21,333 时需要流式传输。流式传输时,准备好处理思考和文本内容块,因为它们到达。

功能兼容性

  • 思考与 temperaturetop_k 修改以及强制工具使用不兼容。
  • 启用思考时,您可以将 top_p 设置为 1 到 0.95 之间的值。
  • 启用思考时无法预填充响应。
  • 对思考预算的更改使包含消息的缓存提示前缀失效。但是,当思考参数更改时,缓存的系统提示和工具定义将继续工作。

使用指南

  • **任务选择:**对于特别复杂的任务使用扩展思考,这些任务受益于逐步推理,如数学、编码和分析。
  • **上下文处理:**您不需要自己移除先前的思考块。Claude API 自动忽略先前转向的思考块,它们在计算上下文使用时不包括。
  • **提示工程:**如果您想最大化 Claude 的思考能力,请查看我们的扩展思考提示技巧

后续步骤