第 5 篇结尾预告的主题到了:让模型先想再答。前五篇的所有调用都没有传过任何与思考有关的参数——这不是遗漏,而是从当前这代模型起,思考默认就是开着的;教学模型 claude-sonnet-5-5 上真正要回答的问题,已经从"怎么开"变成了两件新事:想多深,以及思考过程给不给用户看。这一篇把扩展思考(extended thinking)的完整口径讲透。
本文是《Anthropic SDK 从入门到实战》第 6 篇。全文按"何时需要 → 开启与显示 → 深度与预算 → 响应结构 → 工具合流 → 流式事件 → 实战 → 总结"组织。
1、什么时候需要思考
扩展思考让模型在作答前先在自己的 thinking 块里演算:复述问题、尝试思路、检查中间结果、放弃走不通的分支。官方思考文档把它列为数学、编码、分析与长程 Agent 任务的性能杠杆——这类任务的答案质量取决于中间过程,而中间过程在"一次答完"的模式下会被压缩甚至跳过。
代价同样明确:思考 token 按输出计费、计入 max_tokens,响应时间随思考深度拉长。分类、抽取、格式转换这类一步可答的任务,思考换不来质量,只换来账单。是否开思考、开多深,决策路线如下:
解读:决策树第一问看任务形态,第二问看错误代价,两个"是"同时成立才值得为思考付钱。一个省心的事实是,自适应思考在简单请求上会自动跳过思考、连 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 后继续思考消化,然后才落笔正文。一个工具回合的块序列如下:
解读:从模型视角,这整个来回是一个 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 节的对照——你的任务值不值得开思考,十道题就有答案。下一篇讲提示缓存:重复输入最多省九成,顺带算清切换思考配置击穿缓存的那笔账。

