三词状态栏吃掉 15–20% 的 token:Claude Code 的 agent_summary 是怎么被关掉的
Flamerix AI 在自己的 OpenTelemetry 里发现,agent_summary 占了 Claude Code 全部花费的 16.4%,比主对话还多:每 30 秒把整个 agent 会话记录发给 Opus,只为生成三五个字的进度描述。他们用一个 80 行的 Bun preload 把它关掉,并逐版本给出了验证数据。
中文
复制
在 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 都会把该 agent 的完整对话记录发送给该 agent 自己的模型(通常是 Opus),并要求它“用 3-5 个词描述你最近的操作”。这个回答只会更新 agents 面板中该 agent 的状态行,永远不会到达模型或你的对话。没有任何设置可以关闭它,而且自 2.1.232(2026 年 8 月)起,它默认开启。
这个异常是由 DRIM 在 Claude Code 的 OpenTelemetry 导出数据中发现的:按 query_source 对 claude_code.api_request 事件分组后显示,agent_summary 的花费超过了主对话本身。
query_source = agent_summary | 钩子生效前一周 | 2 天后 |
|---|---|---|
| 占 Claude Code 总花费的比例 | 16.4 %(主对话:15.7 %) | 0 % |
| 占所有 API 请求的比例 | 19.7 %,约每 5 个中有 1 个 | 0 % |
| 其中花在 Opus 上的部分 | 96 % | — |
| 每个输出 token 对应的输入 token | ≈ 9,000 : 1 | — |
这里的“花费”指 Claude Code 自己的逐请求估算(cost_usd),不是账单:在订阅制下,你损失的是速率限制的余量。并非只有我们测量到了这一点:#86879 收集了四项独立测量,结果为 18–26 %。
修复方案(工作原理)
no-agent-summary.js 是一个 Bun 预加载脚本(约 80 行代码)。它包装了 setTimeout/setInterval;当回调的源文本包含摘要器锚点(例如 [AgentSummary] Timer fired 日志行)时,它返回一个惰性句柄,而不是启动定时器。其他所有定时器都原样通过。在 OpenTelemetry 导出数据中,被抑制的请求是 claude_code.api_request 事件,带有 query_source = agent_summary。
为什么用定时器钩子而不是更简单的办法:该模块以预编译的 Bun 字节码形式发布,所以修改二进制文件内的 JS 文本不会产生任何效果,而且 API 客户端不经过被包装的 globalThis.fetch。但 Function.prototype.toString 即使从字节码运行,仍然会返回回调的源代码,因此可以通过其中的字符串识别出这个定时器。惰性定时器正好使用 2³¹−1 毫秒:Node 和 Bun 会把更大的延迟当作 1 毫秒,所以“无限”延迟会立即触发。
安装
-
把
no-agent-summary.js放到一个固定位置,记下它的绝对路径。 -
全局设置
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 user env; also reaches # user services and timers; applies when the user manager next starts, usually next login) BUN_OPTIONS=--preload /abs/path/no-agent-summary.js或者在
PATH上更靠前的位置放一个名为claude的包装脚本(这样当一个 Claude Code 进程启动另一个时,case能保证它幂等):#!/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 "$@" -
重启 daemon 和所有已在运行的会话,然后从带有
BUN_OPTIONS的环境启动 Claude Code:claude daemon stop --any
环境变量
| 变量 | 取值 | 作用 |
|---|---|---|
CC_AGENT_SUMMARY_MODE | off(默认)、pass | off 会关闭摘要定时器。pass 是对照组:照常加载并记录日志,所有定时器原样保留。其他任何值都会在 stderr 上告警,并按 off 处理。 |
CC_AGENT_SUMMARY_LOG | 文件路径,可选 | 加载时追加 preload active mode=… bun=… hooked=…,每个命中的定时器一行(前 20 个),外加一个退出计数。 |
CC_AGENT_SUMMARY_MARKER | 文件路径,可选 | 加载时用 preload active … 那一行覆盖写入。 |
细节
- 安装后要重启 daemon。 daemon、pty host 和预热好的
bg-spare会话继承的是启动 daemon 的那个客户端的环境,所以在设置BUN_OPTIONS之前启动的 daemon 会继续拉起没有挂上 hook 的会话。所以要claude daemon stop --any。 - 无头运行同样需要:
claude -p、cron、CI、编辑器集成。 - 平台: Linux 上测过,macOS 和 Windows 未测。在 Windows 上,
/$bunfs/这道关卡多半根本不会安装 hook。 - A/B:
CC_AGENT_SUMMARY_MODE=pass会加载 hook,但所有定时器照常启用;摘要又回来了,说明off的结果是 hook 造成的。 - 后台进程: daemon、pty host 和
bg-spare的环境里必须带上--preload(Linux 上是/proc/<pid>/environ);如果没有,就重启 daemon。
验证(每次更新后都做)
-
预加载生效:
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没传到进程,要么路径不是绝对路径。 -
定时器已关闭: 在同样的
BUN_OPTIONS和CC_AGENT_SUMMARY_LOG=/tmp/nas.log下,跑一个后台子代理一两分钟。日志里会出现summary timer #1 suppressed (setTimeout, delay=30000),并且该代理的面板状态停止更新。如果你导出了 OpenTelemetry,query_source = agent_summary应保持为零;这是唯一能覆盖所有失败模式的检查。
失败即放行(fail-open)
匹配依据是源文本:锚点必须位于传给定时器的回调内部。一旦某次更新破坏了这一点,钩子就匹配不到任何东西,摘要会在无声无息中重新出现。可能的情形有:
- 日志行或 query-source id 被改名;
- 回调经由
fn.bind(...)传入(toString中没有 source),或经过其他包装; - 字符串被移到回调之外的共享常量里;
- 定时器被移到未包装的 API(
timers/promises、调度循环)。
因此每次更新后都要重新跑一遍 Verify。
效果
- 本机每个 Claude Code 进程(2.1.301)下都启用了它。此后
agent_summary请求为零,也没有其他旁路查询增长起来顶替它的位置。 - 子代理、后台代理、fork 出来的 skill 和后台会话都正常运行,只有摘要 fork 被跳过。
- 可见的变化:每个代理的面板状态不再更新;SDK
task_progress没有summary。 - 开销:每个不同的定时器回调一次
Function.prototype.toString,缓存在WeakMap中。claude --version:约 10 ms → 约 17 ms(单次测量)。
测试过的版本
来自 npm @anthropic-ai/claude-code-linux-x64 的 Linux x64 二进制(其 sha256 与原生安装器的 manifest.json 校验和一致)。静态: 解包后的 bundle 中,摘要定时器回调带有钩子锚点。实测: claude -p --model haiku 配 CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1,一个子代理执行 8 步、每步约 20 s,统计 --debug-file 中的 [AgentSummary] Forking。
| 版本 | 发布日期 | sha256 | 静态 | 约 2.5 分钟代理运行中的摘要 fork 数:无钩子 / 钩子 pass / 钩子 off | 备注 |
|---|---|---|---|---|---|
| 2.1.312 | 2026-10-27 | 4f4178ce41d8 | OK | 5 / 5 / 0 | latest 于 2026-10-28;交互式:6 次无钩子,0 次 off |
| 2.1.310 | 2026-10-23 | b73676430af1 | OK | 5 / 5 / 0 | |
| 2.1.308 | 2026-10-22 | 356733f185fa | OK | 5 / 6 / 0 | stable 于 2026-10-28;pass 那次运行的代理走了 9 步而非 8 步 |
| 2.1.301 | 2026-10-15 | bc1aad9982c7 | OK | 5 / 5 / 0 | 与更早的钩子构建结果相同;交互式 off 也是 0;自 2026-10-26 起日常使用,遥测中 0 次 agent_summary 请求 |
| 2.1.280 | 2026-09-22 | 1e08503dbdf3 | OK | 1 / 1 / 0(定时器触发 11 / 11 / 0) | 交互式:-p 在此版本上无法启用摘要。代理卡在权限提示上,因此后续定时器跳过了未变化的对话记录 |
| 2.1.257 | 2026-09-01 | 9a64bda9d872 | OK | 1 / 1 / 0(定时器触发 11 / 11 / 0) | 与 2.1.280 相同 |
5 / 5 / 0 = 分叉就发生在这个版本上,仅仅加载 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确实能停掉它,但代价是连分叉子代理或后台代理一起停掉。 - 提示缓存让它更便宜,但并没有变便宜。 这些调用里约 98 % 从缓存中重读对话记录;命中冷缓存的那约 2 % 承担了超过三分之一的成本。
- 读取对话记录的成本工具看不到它。 这些请求不会写进磁盘上的会话日志(#104358),只有 OpenTelemetry 导出能看到。文档中关于后台 token 用量的那一节也没有列出它,而 Agent View 的行摘要按文档说明是由一个 Haiku 级模型每隔几分钟刷新一次。
- 更便宜的变体早就有了,只是关着。 2.1.310 提供了
short模式:用约 2k token 的笔记代替完整对话记录,还可以选择由更小的模型来写。默认仍然是完整对话记录,只有服务端标志能改变这一点。
时间线(2026)
| 版本(发布日期) | 变化 |
|---|---|
| 2.1.32(2 月 5 日) | 代码首次出现;在公开构建中是死代码 |
| 2.1.72(3 月 9 日) | 可通过 SDK 主动开启 |
| 2.1.117(4 月 21 日) | 在交互式 CLI 中可通过分叉开关或服务端灰度开启 |
| ≤ 2.1.220(7 月 24 日) | 分叉的 skill 一律生成摘要 |
| 2.1.232(8 月 13 日) | 对交互式后台代理默认开启 |
| 2.1.300(10 月 14 日) | 间隔可由服务端调整,30–240 秒 |
| 2.1.310(10 月 23 日) | 更便宜的 short 模式上线,处于关闭状态 |
在我们的遥测数据中,它的每周支出占比从 7 月的 3.5–7%(当时 fork 开关已向我们开放)升至 7.3%,在我们接入 2.1.233 的那一周升至 11.8%,到 10 月中旬约为 16%。
许可证与免责声明
MIT © 2026 Flamerix AI。按原样提供,不附带任何形式的担保;使用风险由你自行承担。对于因使用本软件而产生的任何损害、数据丢失或账号处置,Flamerix AI 概不负责。改变客户端的运行时行为可能与你所用服务的条款相冲突;核查这一点由你负责。
“Claude”和“Claude Code”是 Anthropic, PBC 的商标。本项目独立开发,与 Anthropic 无关联,也未获其赞助或认可。