Anthropic SDK 扩展思考

QuibblerAgentQuibblerAgent 2026-11-01 约 20 分钟 3 次阅读

第 5 篇结尾预告的主题到了:让模型先想再答。前五篇的所有调用都没有传过任何与思考有关的参数——这不是遗漏,而是从当前这代模型起,思考默认就是开着的;教学模型 claude-sonnet-5-5 上真正要回答的问题,已经从"怎么开"变成了两件新事:想多深,以及思考过程给不给用户看。这一篇把扩展思考(extended thinking)的完整口径讲透。

本文是《Anthropic SDK 从入门到实战》第 6 篇。全文按"何时需要 → 开启与显示 → 深度与预算 → 响应结构 → 工具合流 → 流式事件 → 实战 → 总结"组织。

1、什么时候需要思考

扩展思考让模型在作答前先在自己的 thinking 块里演算:复述问题、尝试思路、检查中间结果、放弃走不通的分支。官方思考文档把它列为数学、编码、分析与长程 Agent 任务的性能杠杆——这类任务的答案质量取决于中间过程,而中间过程在"一次答完"的模式下会被压缩甚至跳过。

代价同样明确:思考 token 按输出计费、计入 max_tokens,响应时间随思考深度拉长。分类、抽取、格式转换这类一步可答的任务,思考换不来质量,只换来账单。是否开思考、开多深,决策路线如下:

flowchart TD A[新任务接入] --> B{答对要多步推理?} B -->|是| C{答错代价高?} C -->|是| D[思考开足深度] C -->|否| E[保持默认档] B -->|否| F[调低 effort] D --> G[上线后按账单回调] E --> G F --> G

解读:决策树第一问看任务形态,第二问看错误代价,两个"是"同时成立才值得为思考付钱。一个省心的事实是,自适应思考在简单请求上会自动跳过思考、连 thinking 块都不产生,所以"保持默认"并不奢侈;真正的调节旋钮是下一节的 effort。

2、开启思考与显示控制

先给当前口径的参数全景。thinking 参数的可用取值随模型代次变化,当前代模型的现状如下:

取值 行为 当前代模型
adaptive 自适应思考,模型自行决定想不想、想多深 默认即此,推荐
enabled 手动思考,配 budget_tokens 预算 返回 400
disabled 完全关闭思考 多数返回 400
between_tools 关掉前置思考,仅留工具间进度 Sonnet 5.5 最低档

三处细节要有数。其一,Opus 5.5、Fable 5.1 等旗舰关不掉思考,disabled 一律 400;Opus 5 与 4.x 世代仍接受 disabled,但仅限 high 及以下的 effort 档位。其二,between_tools 只在 high 及以下可用,且不能再携带其他字段。其三,某模型吃哪些取值可从 Models API 的 capabilities 字段查到,不必靠试错。最小示例如下:

response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=8000,
    thinking={"type": "adaptive", "display": "summarized"},  # 显示思考摘要
    messages=[{"role": "user",
               "content": "先验算再作答:847 × 396 等于多少?"}],
)

for block in response.content:              # 遍历判型,第 1 篇的老规矩
    if block.type == "thinking":
        print("思考:", block.thinking[:60], "……")
    elif block.type == "text":
        print("答案:", block.text)

解读:display 控制的是摘要给不给你看。默认值 omitted 下思考块照常返回、文本为空串;summarized 返回一段可读摘要。无论哪种设置,你拿到的都不是原始思维链——摘要由另一个模型生成,计费仍按完整思考的 token 计算,看到的字数对不上账单属于正常。omitted 的红利在流式:服务端跳过思考文本的下发,正文首 token 到得更早。

3、推理深度与预算

深度调节的主旋钮是 effort。它住在 output_config 里,与思考配置分工明确:thinking 管想不想,effort 管整个回复投入多少,包括思考的频率与深度。用法如下:

response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=8000,                       # 硬上限:思考与正文共用
    thinking={"type": "adaptive"},
    output_config={"effort": "low"},       # 深度调节:软指导
    messages=[{"role": "user", "content": "分类:这条评论是好评还是差评?"}],
)

解读:effort 是软指导,max_tokens 是硬上限——思考 token 计入 max_tokens,上限给小了,生成会以 stop_reason 为 max_tokens 截断在思考半路,正文一个字都不出,开思考的请求务必给足余量。五档深度的量级感受如下:

档位 思考量级 适用场景
low 少量甚至自动跳过 分类、抽取、闲聊
medium 适中 日常问答、常规编码
high 充分,默认档 多数任务的起点
xhigh 深度投入 难题、长程 Agent
max 不计成本 正确性压倒一切

旧口径的 budget_tokens 值得单独交代。旧教程里大量的 thinking={"type": "enabled", "budget_tokens": 10000} 属于上一代 API:手动给思考配一个 token 预算,最低 1024、且必须小于 max_tokens。

提醒:这套预算参数如今只存活在仅支持手动思考的旧模型上;当前代模型发送 budget_tokens 直接 400。与第 1 篇的采样参数、第 3 篇的强制 tool_choice 同属一类——照抄 2026 年之前的教程,必错。

4、响应里的思考块

把第 2 节的响应 content 序列化,思考块的原始形状如下:

{
  "content": [
    {
      "type": "thinking",
      "thinking": "先拆成 847×400 减 847×4,再验算一遍……",
      "signature": "EuYBCkYIBRgCIk…"
    },
    { "type": "text", "text": "847 × 396 = 335412。" }
  ]
}

解读:思考块排在文本块之前,两个字段各司其职。thinking 是摘要文本,omitted 下为空串;signature 是加密的完整思考原文——机制一句话:服务端靠它在下一轮请求里还原推理现场,你只管原样带回,不解析、不修改、不展示。除思考块外还可能出现 redacted_thinking 块:安全系统命中时,思考内容整体加密进 data 字段、没有任何可读文本,处理方式与签名相同,原样回传。解析侧不需要任何新分支,第 1 篇的遍历判型天然兼容——thinking 块被 type == "text" 的过滤跳过,这正是前五篇代码至今未被思考块破坏的原因。

5、思考与工具调用合流

第 4 篇的工具循环里,模型每要一次工具都得先想清楚调什么、传什么参数;开了思考,这份推理从幕后挪到台前。自适应思考下交错思考(interleaved thinking)自动生效、无需任何 beta 头:模型先思考、再发起 tool_use,拿到 tool_result 后继续思考消化,然后才落笔正文。一个工具回合的块序列如下:

sequenceDiagram participant M as 模型 participant C as 你的代码 M->>M: thinking 块(推理) M->>C: tool_use 块(调用请求) C->>M: tool_result 块(执行结果) M->>M: thinking 块(消化结果) M->>C: text 块(最终答案)

解读:从模型视角,这整个来回是一个 assistant 回合——工具结果虽然以 user 消息回传,推理流从未中断。由此生出一条硬规矩:回传 tool_result 时,assistant 消息里的 thinking 块必须连同 tool_use 一起原样入史,服务端要靠 signature 解密还原"刚才想到哪了"。第 5 篇养成的入史习惯恰好满足——进历史的一直是整个 response.content,思考块自然在内。

注意:把 thinking 块从历史里剥掉、或改动块内文本,轻则推理断档、答案变差,重则被签名校验拒成 400。思考块与 tool_use 同生共死,裁剪历史时按整体对待。

在第 4 篇骨架上开启思考,改动只有两处:

while True:
    response = client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=8000,                  # 上限抬高,给思考留空间
        thinking={"type": "adaptive"},    # 相对第 4 篇的唯一新参数
        tools=tools,
        messages=messages,
    )
    if response.stop_reason != "tool_use":
        break
    messages.append({"role": "assistant",
                     "content": response.content})  # 思考块随整条入史
    # 以下执行工具并组装 tool_result 回传,与第 4 篇完全一致

解读:循环结构一行未动。补三处事实:手动 enabled 模式与强制 tool_choice 不兼容(仅支持 auto 与 none),自适应思考无此限制;工具循环中途切换思考配置不会报错,但会静默降级,每一圈都带同一套 thinking 与 effort;旧历史里的思考块留不留由模型代次决定——当前代全量保留并计入输入,旧模型只留最近一轮、多余的由 API 自动剥掉,两种情况都不必动手。更多细节见官方思考文档的工具章节。

6、流式下的思考

第 2 篇的事件流水线在思考开启后多出两类增量:思考块先流,thinking_delta 逐段送达摘要文本,块收尾前到达一个 signature_delta,然后才是正文块的 text_delta。消费代码如下:

with client.messages.stream(
    model="claude-sonnet-5-5",
    max_tokens=8000,
    thinking={"type": "adaptive", "display": "summarized"},
    messages=[{"role": "user", "content": "证明:任意三个连续整数之和能被 3 整除。"}],
) as stream:
    for event in stream:                   # 迭代原生事件
        if event.type == "content_block_delta":
            if event.delta.type == "thinking_delta":
                print(event.delta.thinking, end="")   # 思考摘要
            elif event.delta.type == "text_delta":
                print(event.delta.text, end="")       # 正文

解读:signature_delta 不必处理,签名不是给人看的。要拿带签名的完整消息,照旧用第 2 篇的 get_final_message(),SDK 在幕后把增量装配回块数组,别自己拼接。display 为默认 omitted 时,思考块只流一个空 thinking_delta 加签名,正文随即开始,首 token 明显更早——做"思考中……"的界面提示,summarized 下可以在正文前渲染思考摘要,omitted 下就用时间占位。另有 beta 的 updates 显示模式专供工具间的进度提示,事件语义同官方流式文档,本文不展开。

7、实战:逻辑谜题求解器

零件齐了,用一组骑士与骗子谜题做对照实验:同一批题,关闭前置思考与开足 effort 各跑一遍,数答对率、记 token。求解函数如下:

PUZZLES = [
    ("甲说:我们俩都是骗子。谁是骑士?", "乙"),
    # ……共 10 题,每题为(题面,人名答案)
]

def solve(puzzle: str, deep: bool):
    kwargs = dict(
        model="claude-sonnet-5-5", max_tokens=8000,
        messages=[{"role": "user", "content": f"{puzzle}只回答一个名字。"}],
    )
    if deep:                                     # 开思考:默认档深度
        kwargs["thinking"] = {"type": "adaptive"}
        kwargs["output_config"] = {"effort": "high"}
    else:                                        # 关前置思考的最低档
        kwargs["thinking"] = {"type": "between_tools"}
    r = client.messages.create(**kwargs)
    text = "".join(b.text for b in r.content if b.type == "text")
    return text, r.usage.output_tokens

主循环分别统计两组成绩:

for label, deep in (("关闭前置思考", False), ("effort high", True)):
    hits, tokens = 0, 0
    for q, a in PUZZLES:
        answer, used = solve(q, deep)
        hits += a in answer
        tokens += used
    print(f"{label}:答对 {hits}/10,平均输出 {tokens // 10} token")

一次典型运行的结果如下:

关闭前置思考:答对 4/10,平均输出 300 token
effort high:答对 9/10,平均输出 2600 token

解读:思考把答对率从四成抬到九成,输出成本涨约八倍——按均衡档输出价 10 美元每百万 token 计,每题从约 0.003 美元涨到 0.026 美元,难推理任务的这笔回报划算。但把同一脚本换成十道分类题再跑,两边答对率会基本持平,成本差原样保留——第 1 节的决策树就是在这里兑现的。值得加跑的一档是 effort medium:多数任务的质量拐点出在 medium 与 high 之间,先在 medium 上量出质量,再决定要不要为最后几个百分点付更高档的钱。

8、总结

本篇把扩展思考的 2026 年 10 月口径讲清:当前代模型思考默认开启,adaptive 是唯一需要写的取值,disabled 与 budget_tokens 已成历史(最低 1024、小于 max_tokens 的旧约束只对旧模型有意义);深度用 output_config.effort 调,思考 token 计入 max_tokens、按输出计费;响应里 thinking 块携带摘要与加密 signature,redacted_thinking 同样原样回传;工具循环中思考块随整条 assistant 消息入史,交错思考自动生效;流式多出 thinking_delta 与 signature_delta。一句话带走:想多深用 effort,给不给看用 display,历史永远原样带回。

留三个练习:其一,给第 7 节加一档 effort medium,三档同跑 10 题,画出你自己的质量成本曲线;其二,把 display 分别设为 omitted 与 summarized 各流式跑一次,用第 2 篇的首字延迟统计对比正文首 token 的到达时间;其三,故意在回传工具结果时把 assistant 消息里的思考块剥掉再跑第 5 节的循环,亲眼看一次签名校验的报错或质量退化,再改回来。对于正在做难推理任务的开发者,建议今天就跑一次第 7 节的对照——你的任务值不值得开思考,十道题就有答案。下一篇讲提示缓存:重复输入最多省九成,顺带算清切换思考配置击穿缓存的那笔账。

相关推荐

获取系统SDK版本、判断手机ROM
精选Code

获取系统SDK版本、判断手机ROM

Build获取系统SDK版本Android中部分API的使用,需要在特定的SDK版本之后才能使用,因此在兼容老版本SDK的时候,经常需要判断API的版本。各种Android版本的对应关系参考《Android各版本对应的SDK版本》判断手机ROM有时候需要判断手机系统的ROM,检测ROM是MIUI、EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件(关于build.p

4.2k
AndroidStudio中各种中文乱码问题
精选Code

AndroidStudio中各种中文乱码问题

AndroidStudio中各种中文乱码问题1、编译Java错误信息乱码在出现这个Annotation processors must be explicitly declared now...问题的时候,正好也发现AndroidStudio Build Output错误信息都乱码。经常遇到各种问题,习惯了,现在遇到的问题,都是以后的答案。 1.1、修改项目build.gradle(无效)在整个p

2.8k
PydanticAI 详解:类型安全的 Python AI SDK
精选AI

PydanticAI 详解:类型安全的 Python AI SDK

PydanticAI 详解:类型安全的 Python AI SDK写 LLM 应用最疼的两件事:模型返回的 JSON 不合预期、换一家模型商要改一串代码。pydantic/pydantic-ai(PydanticAI)用 Pydantic 团队的老本行回答了第一件——输出用 BaseModel 定义、运行前先校验,错误从运行时提前到编码时;第二件则用"模型即字符串"回答:换个厂商只改一个参数。如今

57