思考块被锁死:Fable 5.1 反蒸馏新规与开发者迁移
关键词:Fable 5.1、Preserved Thinking、反蒸馏、thinking block、Claude API、上下文一致性
适读人群:直接调用 Claude Messages API、自己维护多轮对话历史的开发者;以及想搞清楚「Anthropic 这次改 API 到底在防什么」的人。
本文概览:9 月 1—2 日(美国当地 9 月 1 日,国内报道为 9 月 2 日)Anthropic 随 Claude Fable 5.1 上线了 Preserved Thinking——回传思考块时,API 会用签名校验「产生它的上下文有没有被动过」,改了就报错或丢弃。这篇先把蒸馏攻击的手法讲清楚(不理解攻击就看不懂防御),再拆三道校验、两种模式的取舍,然后给出会踩雷的写法清单和官方替代方案,最后谈三个我保留意见的地方。
目录
- 一、9月2日发生了什么:Anthropic 给思考块上了锁
- 二、攻击者是怎么套走思考块的?先把手法讲清楚
- 三、Preserved Thinking 的三道校验,到底验什么
- 四、严格模式还是非严格模式:两个参数、两种后果
- 五、哪些常见写法会踩雷?官方给了替代方案
- 六、意外收益:prefix 稳定之后,缓存命中率带来的成本下降
- 七、这套机制防得住吗?三个值得保留的疑问
- 给你的迁移清单
一、9月2日发生了什么:Anthropic 给思考块上了锁
先把事实摆清楚。9 月 1—2 日,Anthropic 在发布 Claude Fable 5.1(与 Mythos 5.1 同一底层权重、两套安全策略)的同时,给 API 加了一条规则:你回传的 thinking block,必须和当初产生它的那套上下文严格绑定;上下文被动过,这段思考就不作数。
官方叫它 Preserved Thinking。落到 API 行为上是两句话:
- 默认(严格模式):系统提示、工具集或任一历史消息被改动,请求直接 400 invalid_request_error;
- 可选(非严格模式):不报错,但静默丢弃受影响的 thinking block 及其之后的所有思考块,模型在「看不到之前推理过程」的状态下继续作答。
生效范围很关键:目前只对 2026 年 8 月 31 日 00:00 UTC 之后新建的 API 账户强制执行。老账户暂不受影响,Claude Code、claude.ai、Cowork 等官方产品的终端用户也不受影响。Anthropic 同时表态,这套机制会在未来模型版本中推广到所有账户——现在的豁免是迁移窗口,不是永久豁免。
这一条规则的信息量比表面大。它既是一次安全动作,也顺带改变了长对话 Agent 的成本结构(第六节讲)。而在讨论「该不该支持」之前,得先弄明白它在防什么。
这一节的一句话版本
Anthropic 把 thinking block 和它的「出生上下文」焊死了,改上下文就等于作废这段思考。目前只卡新账户,但全面执行只是时间问题。
二、攻击者是怎么套走思考块的?先把手法讲清楚
不理解攻击手法,就看不懂这套防御为什么长这样。
Claude 这类推理模型在给最终答案前,会在内部走一段思维链(CoT)。API 会把这段过程以 thinking block 的形式返回给调用方——注意,返回的是加密形式,正常开发者看不懂内容,只是把它原样存着,下一轮连同系统提示、工具定义、历史消息一起传回去,用来保持上下文连贯。这是官方推荐的标准用法。
漏洞恰恰出在这个「回传」动作上。
图一:蒸馏攻击手法
(图一:正常用法是原样带回;攻击者在中间偷偷改写上下文,诱导模型解密并复述推理过程,批量收集明文 CoT)
攻击者发现:只要在多轮对话中修改 thinking block 之前的系统提示或历史消息,模型的上下文就自相矛盾了。在逻辑错乱的状态下,它可能把原本加密的推理过程「解密」并打印出来。这类手法在圈内被叫做上下文注入(context injection)和对话重写(conversation rewriting),效果类似给模型催眠——不是攻破加密,而是让模型自己把答案说出来。
拿到明文 CoT 之后能干什么?这正是让 Anthropic 紧张的地方:
- 能力被复制:用成千上万条高质量推理链去训练一个参数小得多的模型,它不必重复那套从零开始的算力堆叠,靠背下顶尖模型的解题思路就能把 benchmark 刷上去。
- 对齐没被继承:这是更要命的一半。Claude 的「能力 + 安全护栏」是花大量成本做 RLHF 和红蓝对抗才绑在一起的。蒸馏者只提取了高智商的那部分推理能力,底层的安全保障根本没法一起复制过去。
用 Anthropic 自己的表述,这叫能力与安全的脱钩(capability-safety decoupling)。一个继承了高级逻辑和代码能力、却没有对应护栏的小模型,会更乐意响应生成攻击脚本或协助生物武器研发这类请求。这个判断合乎我们对蒸馏机制的理解:蒸馏迁移的是行为分布,不是训练时那套约束。
规模上,按原文与多家转述,Anthropic 安全团队观测到的不是「一个账号问几个问题」,而是数以千计的虚假账户 + 自动化脚本昼夜不停地薅。这也是它为什么宁可冒着误伤合法开发者的风险也要上强校验。
攻击面的准确位置
攻击面不在加密本身,而在「回传时上下文可篡改」。想堵住它,就得让「这段思考是在什么上下文下产生的」这件事变得可验证——这正是 Preserved Thinking 的设计目标。
三、Preserved Thinking 的三道校验,到底验什么
API 收到一个带着 thinking block 的请求时,会做三件事。这三道校验来自 Anthropic 官方开发者文档,比公众号转述的「原路返回一字不差」要精确得多。
图二:Preserved Thinking 三道校验
(图二:三道校验分别是模型版本、prefix 逐字节一致、思考链完整;任一不过就按预设策略处理)
| 校验 | 官方规则 | 工程含义 |
|---|---|---|
| ① 模型检查 | block 由产生它的模型及更新版本可读;换到更旧模型则该 block 被丢弃 | 对话升级到更新版,思考保留;回退到旧模型,思考作废 |
| ② prefix 未变 | 顶层 system、tools 集合、该 block 之前的每条消息,逐字节一致 | 这是最容易被日常写法破坏的一条,见第五节 |
| ③ 思考链完整 | 每个 thinking block 记录前一个(跨轮次);从中间删掉一个,其后全部失效 | 可以从头部丢弃旧 block,但不能从中间抽 |
两个容易忽略的细节:
- 服务端 compaction 会重设起点:官方提到,若使用了服务端压缩,被校验的 prefix 从最近的 compaction block 开始算。这意味着官方托管的压缩是合法的,不会误伤——但也意味着想省事就必须用官方那条路。
- 被丢弃的 block 不计费:非严格模式下被 drop 掉的思考块不计入账单,这点在成本敏感的场景里值得知道。
我的解读:这三道校验的设计思路是把「思考」变成一个有来源、可验证的对象,而不是一段可以自由搬运的文本。它不改变模型能力,改变的是 API 对历史的态度——从「你说了算」变成「我可以验」。
记住这一条就够了
三道校验 = 模型版本 + prefix 逐字节 + 思考链完整。理解「prefix 逐字节一致」这一条,就理解了后面所有踩雷写法。
四、严格模式还是非严格模式:两个参数、两种后果
prefix 校验失败时怎么处理,由 prefix_mismatch_behavior 决定(位于 thinking.block_binding 下,需要 thinking-binding-controls-2026-08-01 这个 beta header 才能设置)。
import anthropic
client = anthropic.Anthropic()
# 严格模式(默认):上下文被动过 → 400 报错,请求直接失败
resp = client.messages.create(
model="claude-fable-5-1",
max_tokens=16000,
system="你是客服助手。",
messages=history, # history 中带着之前的 thinking block
betas=["thinking-binding-controls-2026-08-01"],
thinking={"type": "enabled",
"block_binding": {"prefix_mismatch_behavior": "error"}},
)
# 非严格模式:不报错,静默丢弃受影响的 thinking block 及其之后的所有思考块
resp = client.messages.create(
model="claude-fable-5-1",
max_tokens=16000,
system="你是客服助手。",
messages=history,
betas=["thinking-binding-controls-2026-08-01"],
thinking={"type": "enabled",
"block_binding": {"prefix_mismatch_behavior": "drop_block"}},
)
# 被丢弃的 block 会在响应的 input_transformations 数组里列出(流式时出现在 message_start 事件)
上面是依据官方文档描述的写法示意,具体字段嵌套以 Anthropic 官方文档为准——beta 参数在 SDK 各版本间可能微调,落地前请对着
platform.claude.com的 Preserved Thinking 文档核一遍。
两种模式怎么选?我的判断是看你能不能接受「模型忘掉前面的推理」:
严格模式 error | 非严格模式 drop_block | |
|---|---|---|
| 请求结果 | 400 失败 | 成功 |
| 模型状态 | — | 看不到被丢弃的思考过程 |
| 适用 | 推理链是核心资产、宁可失败也不能错 | 对话不能中断,可接受质量下降 |
| 风险 | 线上直接报错,需要兜底 | 静默降级,可能悄悄变差而你不自知 |
这里有个工程上的坑值得单独提醒:非严格模式是「静默」的。请求照常返回 200,你从结果上很难判断这段回答是在有思考还是没思考的状态下产生的。生产环境如果走这条路线,我建议必须把 input_transformations 记录下来做监控,否则你会在某次线上效果下滑时完全找不到原因。
我的建议
强烈建议:开发期用严格模式(让问题暴露),生产期若必须保可用性再切 drop_block,并配套监控被丢弃的 block 数量。
五、哪些常见写法会踩雷?官方给了替代方案
这一节是本文对开发者最直接有用的部分。官方文档明确列出了一批会破坏 prefix 的常见模式——坦白讲,这里面好几条是业内非常普遍的写法,包括很多团队正在用的「优化」。
图三:会踩雷的写法与官方替代方案
(图三:左侧是官方点名会破坏 prefix 的写法,右侧是对应的一等公民替代方案)
| × 会破坏 prefix 的写法 | √ 官方推荐替代 |
|---|---|
| 每次请求重建 system prompt(塞当前时间、token 预算、模式开关) | mid-conversation system message:追加新指令,不动前面的 |
| 裁剪或丢弃旧轮次 | server-side compaction:官方托管压缩 |
| 客户端总结旧轮次,只保留近期 | 官方 compaction + 保留最近 block |
| 往早期轮次注入提醒,下一轮再删掉 | turn-scoped system message:仅本轮生效 |
会话中途增删 tools | mid-conversation tool changes |
| 整段对话固定同一思考深度 | per-message effort:按轮调整思考深度 |
第一条我要重点说一下,因为它太常见了:「每次请求重建 system prompt,把当前时间塞进去」。这几乎是所有 Agent 框架的标准操作(你是助手,当前时间 2026-09-05 10:30)。在过去这完全无害,但在新规下,system 每轮都变 → prefix 每轮都变 → 之前所有 thinking block 全部失效。如果你的应用正好这么做,升级到受影响账户后会立刻感受到。
好在官方给的是一等公民替代而不是「你自己想办法」——mid-conversation system message、turn-scoped system message、per-message effort 都是正式支持的特性,覆盖的场景比想象中全。
判断口诀只有一句:把 messages 数组当成「只追加」(append-only)。
# × 每轮重建 system(时间变化导致 prefix 失效)
system = f"你是客服助手。当前时间:{now}"
# √ system 固定,易变信息走 mid-conversation 追加
system = "你是客服助手。"
messages = history + [
{"role": "user", "content": [
{"type": "system", "text": f"当前时间:{now}"}, # 追加,不改动已有前缀
{"type": "text", "text": user_input},
]}
]
谁能松一口气:用 Claude Code、claude.ai、Claude Managed Agents 或 Claude Agent SDK 的,官方 SDK 会帮你维护 prefix 完整,不用改代码。需要自查的:直接调 Messages API、自己写 agent loop 管历史的团队。
三步自查:先别改代码,测出自己踩没踩雷
与其通读代码找可疑点,不如直接量。这个流程不需要任何改造,十几分钟能跑完:
- 抓两轮真实请求的 payload:挑一个多轮会话,把第 N 轮和第 N+1 轮发给 API 的完整请求体(system、tools、messages)落盘成 JSON。
- 逐字节 diff:比对的粒度是「第 N 轮请求体」与「第 N+1 轮请求体中、最后一个 thinking block 之前的全部内容」。只要有一个字节不同,prefix 就断了。
- 用受影响账户实测一次:拿一个 8 月 31 日后新建的账户,开严格模式跑同样的会话,看是否返回 400;若走非严格模式,则看响应的
input_transformations是否非空。
第 2 步可以直接脚本化,比人眼可靠:
import json, hashlib
def prefix_fingerprint(req: dict) -> str:
"""只取「最后一个 thinking block 之前」的内容做指纹"""
msgs = req["messages"]
cut = len(msgs)
for i in range(len(msgs) - 1, -1, -1):
blocks = msgs[i].get("content")
if isinstance(blocks, list) and any(
b.get("type") == "thinking" for b in blocks
):
cut = i # 该条消息之前的所有内容都算 prefix
break
payload = json.dumps(
{"system": req.get("system"), "tools": req.get("tools"),
"messages": msgs[:cut]},
ensure_ascii=False, sort_keys=True, separators=(",", ":"),
)
return hashlib.sha256(payload.encode()).hexdigest()[:16]
# 同一个 session 的连续两轮,指纹必须一致;不一致 = prefix 已被破坏
print(prefix_fingerprint(turn_n), prefix_fingerprint(turn_n_plus_1))
指纹不一致,就回到第五节那张表找对应的写法。这个方法的好处是能在改造前就定位问题,而不是等线上 400 或者静默降质之后倒推。
这一节的行动项
把 messages 当 append-only。最隐蔽的雷是「重建 system prompt 塞当前时间」,最省事的出路是改用官方的 mid-conversation / turn-scoped 系列特性。
六、意外收益:prefix 稳定之后,缓存命中率带来的成本下降
这次规则变更有个官方没怎么宣传、但对合法开发者实打实有利的副作用。
提示词缓存(prompt caching)生效的前提是请求前缀逐字节不变。过去开发者自由改写上下文,模型每次收到的都像是一个「新请求」,缓存命中率上不去。现在新规反过来强迫所有人保持 prefix 一致——技术上正好创造了近乎完美的缓存命中条件:服务器可以缓存之前的对话上下文,下一轮请求直接命中。
结果是延迟下降、成本下降。这组数字可以和 Fable 5.1 的定价变化对照着看:
- 缓存读取价格从每百万 token 1 美元降到 0.25 美元,降幅 75%;
- 输入/输出价格维持每百万 token 10 / 50 美元不变;
- Anthropic 官方测算:典型工作负载成本比 Fable 5 低约 25%,高度 agent 化的负载最高可降约 45%。
(以上定价与测算均为官方口径,来自 Anthropic 发布材料。)
我的看法:这两个动作是配套的,不是巧合。一边把缓存价格打到原来四分之一,一边用规则逼出高缓存命中率——等于用真金白银补偿那些愿意遵守新规的开发者。对长对话 Agent 这种「反复重放同一段上下文」的场景,省下来的钱可能比很多人想象的要多。
不过别高兴太早:这要求你的应用真的能做到 prefix 稳定。如果你因为业务需要频繁改写上下文而走了 drop_block,那缓存收益会打折,同时还要承担静默降级的代价——等于两头不讨好。
收益的前提条件
缓存降价 75% 与 prefix 强制稳定是组合拳,长对话 Agent 受益最明显。前提是你能做到 append-only,否则收益打折。
七、这套机制防得住吗?三个值得保留的疑问
原文标题写的是「大模型蒸馏时代结束」。我的判断是:这个说法夸张了。以下三点是我认为需要保留意见的地方,也是接下来值得观察的指标。
疑问一:只卡新账户,老账户的空子有多大?
限制只对 8 月 31 日后新建的账户生效。而按原文描述,工业级蒸馏者手里是「数以千计的虚假账户」——这类账户池大概率是在新规之前就批量注册好的。真正有组织的攻击者,恰恰最可能持有老账户。那么这套机制短期内挡住的是「后来者」,还是「正在干的人」?这个答案目前没有公开数据支撑,值得观察。
疑问二:堵住的是 CoT 蒸馏,不是蒸馏本身。
蒸馏不一定要拿推理链。用海量输入-输出对做 response distillation(输出蒸馏),照样能迁移相当一部分能力——只是拿不到那份「高价值、带推理过程」的数据。所以更准确的说法是:Anthropic 堵住了质量最高的那条蒸馏路径,让蒸馏的成本和门槛都变高了,但远没到「时代结束」。
疑问三:合法开发者的上下文压缩是刚需,替代方案够不够用?
长对话 Agent 不压缩上下文,成本就是指数级的。官方给了 server-side compaction 等替代,这在方向上是对的,但覆盖面需要实测:复杂的自定义压缩策略(比如按业务语义保留关键轮次)能不能被官方方案完整替代?如果不能,那等于把一部分成本转嫁给了开发者。这一点我不会靠猜,建议有长对话场景的团队尽快做一次对照测试。
还有一个更根本的问题:这到底是安全动作还是商业动作? 我认为两者兼有,而且不必阴谋论。「能力与安全的脱钩」这个理由在技术上站得住,蒸馏确实会破坏对齐的完整性;同时它也客观保护了 Anthropic 的商业利益。承认两者并存,比把它单纯解读成「垄断」或单纯当成「无私的安全举措」都更接近事实。
保留意见汇总
防住了最高质量的 CoT 蒸馏路径,但没有终结蒸馏;老账户豁免与压缩刚需是两个尚未闭合的口子。「蒸馏时代结束」是媒体表述,不是工程结论。
给你的迁移清单
按优先级排序,可以直接照着做:
- 先确认自己受不受影响:账户是否创建于 2026-08-31 00:00 UTC 之后?是否直接调 Messages API 自己维护
messages?两个都是「是」才需要处理。用官方 SDK / Claude Code 的可以直接跳过。 - 全局搜一遍 system prompt 的构造位置:凡是每轮动态拼接(当前时间、预算、模式开关)的,优先改造成固定 system + mid-conversation 追加。这是最隐蔽也最普遍的雷。
- 审计历史裁剪逻辑:自己做的 summarize / trim / 注入提醒再删除,逐个迁到官方的 server-side compaction、turn-scoped system message、mid-conversation tool changes。
- 开发期开严格模式:让
prefix_mismatch_behavior: "error"把问题全暴露出来,别一上来就用drop_block把问题藏起来。 - 生产环境若必须用
drop_block,配套监控input_transformations:静默降级是最难排查的故障类型,必须有可观测性兜底。 - 顺带把缓存账算一遍:如果应用属于「长对话、重复读上下文」型,75% 的缓存降价叠加高命中率,可能是一笔不小的成本改善,值得单独测一次。
- 关注全面执行的时间点:Anthropic 已明确未来版本会推广到所有账户,现在的窗口期有限,别把豁免当成永久状态。
#Fable5.1 #PreservedThinking #反蒸馏 #ClaudeAPI #thinkingblock #提示词缓存