Claude Code 2.1.260:那个让每个使用工具的 Fable 5.1 Agent 都在多付钱的提示词缓存 Bug

Fable 5.1 的提示词缓存悄悄地把工具结果之后的上下文排除在缓存覆盖范围之外,导致每次工具调用的后续回合都按未缓存价格全额计费。Claude Code 2.1.260 修复了这个问题,同时带来缓存未命中诊断、Read() 权限回滚以及受管 CLAUDE.md 治理变更。

发布于 来源 Claude Code Changelog

归档条目:由 AI 根据所引信源辅助生成,发布时未经逐篇审阅。责任编辑:Joe Werner。

技术示意图,展示工具结果边界前后的提示词缓存覆盖范围,以及计费 token 的差异

Claude Code 2.1.260 没有带来新的模型能力。它关闭的是一个计费漏洞——自 9 月 1 日 Fable 5.1 上线以来,每一个使用工具的 Fable 5.1 部署都在往这个漏洞里填钱。

具体的 bug:Fable 5.1 的提示词缓存没有覆盖工具结果之后附加的上下文。每一次后续的工具调用回合,这部分上下文都会以未缓存的输入 token 发回给 API。对于频繁调用工具的 Agent——也就是大多数生产环境中的 Agent——这个问题会快速累积。一个进行了 20 次工具调用、带有 10K token 系统提示加历史记录的会话,每一次工具调用都在按未缓存价格付费。Fable 5.1 未缓存输入价格是每百万 token $10,缓存读取是 $0.25,差距达到 40 倍。2.1.260 修复了这个问题,但从 9 月 1 日到各部署完成升级之间的窗口期已经产生了真实的账单损失。

这里有一点值得明确说:这个 bug 不影响纯对话 Agent。只有在会话中包含工具结果追加的情况下才会触发,而这恰好描述了大多数生产环境的编码 Agent 和 API 网关工作流。

工具结果上下文为何落在缓存边界之外

Anthropic 的提示词缓存设计要求缓存前缀在各轮回合之间保持稳定。工具结果在每次调用后被追加到消息列表,从而更新上下文。正确的行为是:缓存到当前稳定前缀的位置——即新工具结果之前的全部内容——并在下一轮回合中对这部分按缓存价格计费。Bug 的表现是:实现层将工具结果之后的区块标记为不可缓存的输入,导致每次工具调用的往返都按全价重新计费。

跨提供商对比在这里有实际意义:这个失效模式是 Anthropic 提示词缓存机制特有的。OpenAI 的 API 对工具结果的续接处理方式不同——客户端不需要显式跟踪缓存边界。运行混合提供商路由(部分会话走 Anthropic,部分走 OpenAI)的运营者,可能会观察到等价的 Fable 5.1 和 GPT-5.6 工具使用会话之间存在成本差异,并将其归因于模型定价差异,而不是缓存缺陷。这个计费差异从本次发布起将得到纠正。

对于启用了 Fable 5.1 的 TheRouter 部署:如果你记录了 API 响应 usage 字段中的逐回合 token 明细,可以通过汇总 9 月 1 日到升级至 2.1.260 之间、Fable 5.1 工具调用回合的 uncached_input_tokens 来计算暴露窗口。其中 cache_read_input_tokens 为零但此前上下文非空的回合,就是受影响的回合。升级后修复自动生效,无需任何配置变更。

/cost 现在告诉你缓存为何未命中

与 Bug 修复并行,2.1.260 在 /cost 和状态栏的 prompt_cache 字段中增加了缓存未命中原因注释。报告的原因包括:工具定义已更改、系统提示已更改、空闲时间超过 TTL。

此前,运营者发现异常成本时,必须手动逐回合比对 API 响应元数据才能还原缓存失效的原因。工具定义变更是受管 Claude Code 部署中的常见触发因素:如果一次插件更新修改了工具 schema,缓存前缀从那个时间点起就会失效。空闲超 TTL 导致的未命中,在计费日志中与系统提示变更看起来完全一样,除非你有时间戳可以对比。

这一功能开启的运营工作流:在一次可疑会话结束后运行 /cost,检查注释的未命中原因,再决定是否开票。原因是"工具定义已更改",调查从受管设置或插件推送开始;原因是"系统提示已更改",从任何 CLAUDE.md 更新或运行时注入开始;原因是"空闲超 TTL",说明会话有过足够长的暂停——对 Fable 5.1 来说值得关注,因为长时间运行的编码 Agent 在等待人工审核时自然会出现这种暂停。

安全审计可能漏掉的 Read() 权限回滚

2.1.260 回滚了 2.1.259 的一项变更。2.1.259 将 Read() 拒绝规则应用于 Bash 参数,导致 npm run build 在任何模式下都会被 Read(./**/build/**) 规则拒绝。此次回滚移除了这一行为。

安全影响是双向的。在 2.1.259 之前,Read() 拒绝规则只控制文件读取,不控制恰好引用匹配路径的 Bash 参数。2.1.259 的变更试图填补这个空缺——一条"不从 build 目录读取"的规则,原本不会阻止 cd build && grep 的执行。回滚意味着这个空缺重新出现。

对于从 2.1.258 经 2.1.259 快速升级到 2.1.260 的运营者,权限审计要点是:你曾期望 Read() 拒绝规则同时覆盖 Bash 路径参数的那些配置,现在不再以这种方式生效。Read(./**/build/**) 会阻止从 build/ 读取文件,但不会阻止 grep -r build/ 命令。如果这个区分对你的安全态势很重要,需要在 Read() 拒绝规则之外单独添加明确的 Bash() 拒绝规则。

配套修复值得关注:2.1.260 同时修复了 macOS 上 permissions.blockReadsOutsideWorkingDirectories 隐藏了工作树隔离子 Agent 自身 checkout 内容的问题。如果你运行隔离子 Agent 工作树并启用了这一设置,在大范围部署更新前,测试 Agent 是否能读取自己 checkout 的文件。

受管 CLAUDE.md 不再需要运营者安全审批

2.1.260 变更了受管设置的行为:通过 claudeMd 在服务端受管设置中推送的 CLAUDE.md,不再触发用户安全审批对话框——而 hooks、shell 命令、沙箱规则和不安全的 env 项目仍然需要审批。

对于通过服务端受管设置集中管理 Claude Code 机队并集中推送 CLAUDE.md 内容的运营者,这意味着日常 CLAUDE.md 更新不会在生效前弹出用户审批提示。Hooks 和 shell 层面的规则仍需审批。这一区别很重要,因为 CLAUDE.md 在提示词层面控制 Agent 行为——Agent 对项目的了解、风格偏好、对指令的响应方式。即使没有 shell 访问权限,这些内容也能显著影响行为走向。

跨提供商背景:对于 OpenAI 或 DeepSeek 后端,Claude Code 中不存在等效的治理层。这一审批区分仅适用于 Anthropic 受管设置路径。如果你将部分 Claude Code 会话路由到 Anthropic API,其余路由到非 Anthropic 后端,CLAUDE.md 治理变更只影响 Anthropic 受管路径。非 Anthropic 路径继续从工作目录下的本地文件读取 CLAUDE.md,没有集中推送或审批流程。

对运营团队的实际影响:如果你的受管 CLAUDE.md 包含路由提示、模型偏好或行为覆写,以往会在暂存环境测试后才正式推出,而审批步骤是天然的门控节点。没有了这道门控,从受管设置更新到活跃用户会话生效的路径更短了。考虑单独记录受管 CLAUDE.md 部署日志,以便在事后回溯时能够检索到任何给定会话期间生效的版本。

面向 IdP 特殊刷新行为的 oidc.scope_on_refresh

Claude apps gateway 新增 oidc.scope_on_refresh,支持那些只在刷新请求中明确包含 openid scope 时才返回 id_token 的 IdP。这是一个范围窄但影响精准的修复:只在 Claude apps gateway 后端对接了刷新时不自动包含 openid scope 的 IdP 时才有用。

运营后果:如果 Claude Code 会话在使用你的 gateway 时悄悄从已认证状态退回到未认证状态——尤其是长时间运行的编码会话——而你的 IdP 符合上述模式,这个设置能在刷新时恢复 id_token。没有 id_token,使用 OIDC claim 验证会话身份的 gateway 在刷新时会看到 claim 校验失败,并根据你的失败策略拒绝会话或回退为匿名处理。

如果你的 IdP 已经默认在刷新时包含该 scope,不要设置 oidc.scope_on_refresh,多余的参数只会增加一次无意义的往返。

帮助与联系