Claude Code 状态行消耗 15-20% Token,社区发布禁用补丁

原标题:Claude Code burns ~15–20% of your tokens on a 3-word status line
78AI 研判分

Claude Code 后台代理每 30 秒向模型发送完整对话记录以生成简短状态描述,导致约 15-20% 的 Token 被无效消耗。开发者通过 OpenTelemetry 数据发现此异常并发布了基于 BUN_OPTIONS 预加载的禁用脚本,该问题自 2.1.232 版本起默认开启且无官方关闭选项。

报道正文

本文目录13 个章节

已在 Linux、Claude Code 2.1.257–2.1.312 版本上测试(参见“已测试版本”)。二进制文件未被修改(BUN_OPTIONS=--preload)。更新可能会悄无声息地撤销此更改,因此请参阅“验证”部分。

TL;DR

文本
curl -fsSL https://gist.githubusercontent.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce/raw/no-agent-summary.js -o ~/.local/share/no-agent-summary.js
claude daemon stop --any
BUN_OPTIONS="${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOME/.local/share/no-agent-summary.js" claude

若要使其对所有 Claude Code 进程永久生效,请参阅 安装。

问题所在

每隔约 30 秒,对于每个后台代理(agent),Claude Code 都会将该代理的完整对话记录(transcript)发送给该代理自身使用的模型(通常是 Opus),并要求它“用 3-5 个词描述你最近的操作”。这个答案仅用于更新代理面板中该代理的状态行;它永远不会传递给模型或你的对话上下文。目前没有任何设置可以关闭此功能,而且自 2.1.232 版本(2026 年 8 月)以来,它默认处于开启状态。

这一异常是由 DRIM 在 Claude Code 的 OpenTelemetry 导出数据中检测到的:按 query_source 对 claude_code.api_request 事件进行分组显示,agent_summary 的成本超过了主对话本身。

query_source = agent_summary挂钩(hook)前一周2 天后
占所有 Claude Code 支出的比例16.4 %(主对话:15.7 %)0 %
占所有 API 请求的比例19.7 %,约为五分之一0 %
其中花在 Opus 上的支出比例96 %—
输入 token 与输出 token 之比≈ 9,000 : 1—

这里的“支出”指的是 Claude Code 自身的每次请求估算值(cost_usd),而非账单金额:在订阅模式下,你损失的是速率限制(rate-limit)的余量。并非只有我们测量到了这一点:#86879 收集了四项独立测量结果,数值在 18–26 % 之间。

修复方案(工作原理)

no-agent-summary.js 是一个 Bun preload 脚本(约 80 行代码)。它包装了 setTimeout/setInterval;当回调函数的源代码文本包含摘要器锚点(例如 [AgentSummary] Timer fired 日志行)时,它会返回一个无效句柄而不是启动定时器。其他所有定时器均不受影响地通过。在 OpenTelemetry 导出中,被抑制的请求是带有 query_source = agent_summary 的 claude_code.api_request 事件。

为什么选择定时器 hook 而不是更简单的方案:该模块以预编译的 Bun 字节码形式发布,因此直接修改二进制文件中的 JS 文本不会生效,且 API 客户端并未通过被包装的 globalThis.fetch。但是,即使回调函数是从字节码运行的,Function.prototype.toString 仍然会返回其源代码,因此可以通过字符串匹配来识别定时器。惰性定时器使用的延迟恰好为 2³¹−1 ms:Node 和 Bun 会将更大的延迟值视为 1 ms,因此所谓的“无限”延迟实际上会立即触发。

安装

  1. 将 no-agent-summary.js 放在一个稳定的位置;记下其绝对路径。
  2. 全局设置 BUN_OPTIONS,以便每个 Claude Code 进程都能加载它。可选择以下任一方式:
文本
# ~/.profile, ~/.bashrc, ~/.zshrc
export BUN_OPTIONS="${BUN_OPTIONS:+$BUN_OPTIONS }--preload /abs/path/no-agent-summary.js"
文本
# ~/.config/fish/config.fish
set -gx BUN_OPTIONS "--preload /abs/path/no-agent-summary.js"
文本
# ~/.config/environment.d/50-no-agent-summary.conf (systemd 用户环境;也会影响
# 用户服务和定时器;在下次启动用户管理器时生效,通常是下次登录时)
BUN_OPTIONS=--preload /abs/path/no-agent-summary.js

或者,创建一个名为 claude 的 wrapper 脚本,并将其放置在 PATH 中较靠前的位置(使用 case 语句可确保当一个 Claude Code 进程启动另一个时保持幂等性):

文本
#!/bin/sh
HOOK=/abs/path/no-agent-summary.js
case " ${BUN_OPTIONS-} " in
  *" --preload $HOOK "*) ;;
  *) export BUN_OPTIONS="${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOOK" ;;
esac
: "${CC_AGENT_SUMMARY_LOG:=${XDG_RUNTIME_DIR:-/tmp}/no-agent-summary.log}"
export CC_AGENT_SUMMARY_LOG
exec /path/to/the/real/claude "$@"
  1. 重启守护进程以及所有已运行的会话,然后在包含 BUN_OPTIONS 的环境中启动 Claude Code:
文本
claude daemon stop --any

环境变量

变量取值效果
CC_AGENT_SUMMARY_MODEoff(默认)、passoff 会抑制摘要定时器。pass 作为 A/B 测试对照组:加载并记录日志,但保留所有定时器不变。其他任何值都会在 stderr 发出警告,并按 off 处理。
CC_AGENT_SUMMARY_LOG文件路径,可选在加载时追加 preload active mode=… bun=… hooked=…,每匹配到一个定时器记录一行(前 20 个),并在退出时记录计数。
CC_AGENT_SUMMARY_MARKER文件路径,可选在加载时被覆盖为 preload active … 行。

细节说明

  • 安装后重启守护进程。 守护进程、pty host 和预热的 bg-spare 会话会继承启动守护进程的客户端环境,因此在设置 BUN_OPTIONS 之前启动的守护进程将继续生成未挂钩的会话。因此需要执行 claude daemon stop --any。
  • 无头运行也需要此配置: claude -p、cron、CI、编辑器集成。
  • 平台: Linux 已测试,macOS 和 Windows 未经测试。在 Windows 上,/$bunfs/ 门控很可能永远不会安装钩子。
  • A/B 测试: CC_AGENT_SUMMARY_MODE=pass 加载钩子但启用所有计时器;摘要会返回,这表明 off 结果是钩子作用所致。
  • 后台进程: 守护进程、pty host 和 bg-spare 必须在其环境中携带 --preload(Linux 上的 /proc/<pid>/environ);如果没有,请重启守护进程。

验证(每次更新后)

  1. 预加载加载成功:
文本
BUN_OPTIONS="--preload $HOME/.local/share/no-agent-summary.js" CC_AGENT_SUMMARY_LOG=/tmp/nas.log claude --version && cat /tmp/nas.log

预期输出 preload active mode=off …。若无此行:说明 BUN_OPTIONS 未传递到进程,或路径不是绝对路径。 2. 计时器被抑制: 使用相同的 BUN_OPTIONS 和 CC_AGENT_SUMMARY_LOG=/tmp/nas.log,运行一个后台子代理一两分钟。日志应显示 summary timer #1 suppressed (setTimeout, delay=30000),且代理的面板状态停止更新。如果你导出 OpenTelemetry,query_source = agent_summary 应保持为零;这是唯一能捕获所有失败模式的检查项。

故障开放(Fail-open)

匹配基于源文本:锚点必须位于传递给计时器的回调内部。如果某次更新破坏了这一点,钩子将匹配不到任何内容,摘要会静默恢复。可能的情况包括:

  • 日志行或查询源 ID 被重命名;
  • 回调通过 fn.bind(...) 传递(toString 中无源代码)或其他包装器传递;
  • 字符串移至回调外部的共享常量中;
  • 计时器移至未包装的 API(如 timers/promises、调度循环)。

因此,每次更新后都要重新运行“验证”步骤。

效果

  • 我们在机器上的每个 Claude Code 进程(2.1.301)中运行它。此后,agent_summary 请求数量为零,也没有其他侧查询取而代之。
  • Subagents、后台 agents、forked skills 和 background sessions 均正常运行;仅跳过 summary fork。
  • 可见变化:每个 agent 的面板状态停止更新;SDK task_progress 中没有 summary。
  • 开销:每个不同的 timer callback 执行一次 Function.prototype.toString,并缓存在 WeakMap 中。claude --version:约 10 ms → 约 17 ms(单次测量)。

测试版本

来自 npm @anthropic-ai/claude-code-linux-x64 的 Linux x64 二进制文件(其 sha256 等于原生安装程序 manifest.json 中的校验和)。静态检查: 提取出的 bundle 中的 summary timer callback 包含 hook anchor。实时测试: 使用 CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1 运行 claude -p --model haiku,其中一个 subagent 执行 8 步,每步约 20 秒,统计 --debug-file 中的 [AgentSummary] Forking。

versionpublishedsha256staticsummary forks in a ~2.5-min agent run: no hook / hook pass / hook offnotes
2.1.3122026-10-274f4178ce41d8OK5 / 5 / 02026-10-28 为 latest;交互式模式:无 hook 时 6 次,off 时 0 次
2.1.3102026-10-23b73676430af1OK5 / 5 / 0
2.1.3082026-10-22356733f185faOK5 / 6 / 02026-10-28 为 stable;pass 运行时 agent 执行了 9 步而非 8 步
2.1.3012026-10-15bc1aad9982c7OK5 / 5 / 0早期 hook 构建结果相同;交互式模式下 off 也为 0;自 2026-10-26 起日常使用,遥测数据显示 agent_summary 请求数为 0
2.1.2802026-09-221e08503dbdf3OK1 / 1 / 0 (timer fired 11 / 11 / 0)交互式模式:在此版本上 -p 无法启用 summaries。由于 agent 停留在权限提示处,后续 timers 跳过了未变化的 transcript
2.1.2572026-09-019a64bda9d872OK1 / 1 / 0 (timer fired 11 / 11 / 0)与 2.1.280 相同

5 / 5 / 0 = 在该版本上发生 fork,仅加载 hook 不会改变任何行为,而 off 会将其移除。

为什么没有相关设置

  • 文档中记载的开关只能将其打开。 SDK 选项 agentProgressSummaries 默认为 false,但在打包文件中它是 OR 逻辑的一项,且所有写入该值的代码路径都将其设为 true。因此,设置为 false 没有任何作用,而未公开的 CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES 环境变量也只能用于启用摘要功能。
  • 时间间隔由服务器控制。 它来自针对特定模型系列的服务器端标志位,并被限制在 30–240 秒之间。默认值已经是最小值,且没有哪个值可以禁用计时器。
  • 显而易见的环境变量无效。 某些打包者设置的 DISABLE_NON_ESSENTIAL_MODEL_CALLS 在二进制文件中根本不存在。CLAUDE_CODE_FORK_SUBAGENT=0 和 CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 确实能阻止它,但代价是同时也禁用了 fork 子代理或后台代理。
  • 提示缓存使其更便宜,而非廉价。 大约 98% 的这些调用会从缓存中重新读取转录记录;那约 2% 命中冷缓存的请求承担了其成本的三分之一以上。
  • 读取转录记录的成本工具看不到它。 这些请求不会写入磁盘上的会话日志(#104358);只有 OpenTelemetry 导出会显示它们。文档中关于后台令牌用量的章节也未列出此项,而 Agent View 的行摘要被记录为每隔几分钟刷新一次 Haiku 级模型。
  • 已存在一个更便宜的变体,但处于关闭状态。 2.1.310 版本引入了 short 模式:使用约 2k 令牌的笔记代替完整转录记录,并可选地由较小的模型生成。默认仍然是完整转录记录,只有服务器标志位才能更改此设置。

时间线 (2026)

版本 (发布)变更内容
2.1.32 (2月5日)代码首次出现;在公开构建中处于死代码状态
2.1.72 (3月9日)作为 SDK 的 opt-in 选项可达
2.1.117 (4月21日)在交互式 CLI 中可达,需通过 fork 门控或服务器灰度发布
≤ 2.1.220 (7月24日)forked skills 始终会被生成摘要
2.1.232 (8月13日)默认开启,适用于交互式后台代理
2.1.300 (10月14日)时间间隔变为服务器可调,范围 30–240 秒
2.1.310 (10月23日)更便宜的 short 模式上线,但处于关闭状态

在我们的遥测数据中,其每周支出占比从 7 月的 3.5–7%(当时 fork 门控对我们已经开放)上升到我们升级到 2.1.233 那一周的 7.3%,随后升至 11.8%,并在 10 月中旬达到约 16%。

许可证与免责声明

MIT © 2026 Flamerix AI。本软件按“原样”提供,不附带任何形式的担保;使用风险由您自行承担。对于因使用本软件而导致的任何损害、数据丢失或账户处罚,Flamerix AI 概不负责。更改客户端的运行时行为可能会与您所使用的服务条款相冲突,请务必自行核实合规性。

"Claude" 和 "Claude Code" 是 Anthropic, PBC 的商标。本项目为独立项目,与 Anthropic 无任何关联,也未获得其赞助或背书。