[控场AI]
· 8 分钟阅读· 4,117 字

Claude Code上下文被谁吃掉了?统计工具调用结果

Claude Code上下文被谁吃掉了?统计工具调用结果

用脚本统计Claude Code会话日志中工具返回的token量,找出吃额度的真凶再优化。

文章指出Claude Code每次请求都会重发完整对话历史,工具返回的大段文本会持续占用额度直到清除或压缩。作者建议先跑零成本的 `/context` 和 `/usage` 命令,再写一个本地脚本解析 `~/.claude/projects/` 下的JSONL日志,join `tool_use` 与 `tool_result` 并按工具名统计token。统计结果通常显示元凶是Bash测试日志、MCP服务器返回的冗长文本,或Read整文件读取。文中用CPython的 `decoder.py` 举例,说明只读一个函数却能省下5倍以上token。修复手段按成本从低到高:问更窄的问题、切换任务时 `/clear`、装代码智能插件或符号索引工具。优化后必须重跑脚本验证,若索引工具自身开销抵消收益则应撤掉。

用Claude Code时,如果你觉得额度消耗得比预期快,先别急着改用法。真正把session撑大的,往往不是你以为的那一类工具。Claude Code会把每次会话存成本地JSONL日志,写个小脚本统计各工具返回的token量,答案通常出乎意料。

会话日志与工具调用统计

为什么该统计工具调用结果

Claude Code的计费机制决定了:每次请求都会把完整对话历史发出去,模型每次调用工具后返回的结果,都会作为新一轮请求的一部分被再次发送。这一点来自Anthropic官方成本文档,不是猜测。

结果就是——一次Read读进一个大文件,这个文件内容就留在session里,后续每一次请求都带着它,直到你执行 /clear 或者上下文窗口触发压缩。缓存机制让重复读取变便宜,但并没有变成免费;在订阅制下,这些token依然占用你的使用额度窗口。

所以要看清楚"谁在占坑",工具返回结果是最该统计的对象。

关于上下文窗口与自动压缩:Claude Code所依赖的大模型(如Claude系列)有一个固定的上下文窗口上限,通常为200K token。当对话历史累积接近这个上限时,客户端会自动触发压缩(compaction),把较早的内容总结成更短的摘要再继续,而不是直接丢弃。压缩本身会消耗一次模型调用,并且摘要不可能保留所有细节,因此频繁触发压缩不仅增加开销,还可能让模型丢失早期关键信息。这也是为什么在无关任务之间主动执行 /clear 比被动等待压缩更划算——它把占用彻底清零,代价只是丢失当前session的短期记忆。理解这一点,才能明白为什么一次早期的大文件读取会“持续收费”到压缩发生为止。

先看内置视图,零成本

动手写脚本之前,有两个不花钱的命令值得先跑一遍:

/context 展示当前上下文窗口里正在占用空间的内容。

/usage 在Pro或Max订阅下,能按技能、子代理、插件、单个MCP服务器拆分最近的用量,并且当某项超过近期用量的10%时会给出标记,比如长上下文或缓存未命中。

这两个命令的盲区也很明显:它们不告诉你具体是哪些工具调用、哪些文件把文本塞了进去。那个信息藏在日志里。

会话日志:JSONL格式

Claude Code把每个session保存为 ~/.claude/projects/ 下的JSONL文件。工具调用是一个带id和name的 tool_use 块,它的结果在后续某一行以 tool_result 块返回,携带相同的id。把两者join起来,统计每个结果里的token数,按工具名分组即可。

脚本逻辑大致是:用 tiktoken 编码结果文本,遍历每行JSON,匹配tool_use与tool_result,累加到工具名对应计数器,同时对Read工具单独记录文件路径维度。读一个本地文件,打印一张表,所有计算都在你机器上完成——这些日志里包含你的prompt和源代码,数据不离本地是有实际意义的。

运行方式:

pip install tiktoken
python toolcost.py                        # 最近一次session
python toolcost.py path/to/session.jsonl  # 指定session

两个诚实的局限

cl100k_base 不是Claude的分词器,所以结果用来排序相对大小,不要当成账单数字。

每个结果只按它到达时的大小统计一次,不建模后续有多少请求携带了它。这意味着长session早期的一次大读取,实际代价比末期同样大小的一次读取更高。

另外日志格式没有公开文档,不同版本之间会变化。如果脚本输出空表,打开文件看几行实际的JSON结构。

JSONL是什么:JSONL(JSON Lines)是一种每行都是一个独立JSON对象的文本格式,扩展名常写作 .jsonl。相比一个包含数组的大JSON文件,它更适合流式追加写入——每产生一条事件就写一行,无需读取整个文件再重写。Claude Code正是用这种方式记录会话:用户消息、助手回复、工具调用、工具结果各占一行,行与行之间通过id字段关联。这也解释了脚本为什么可以逐行遍历、边读边统计。需要注意日志中可能包含多轮对话的交叉记录,以及一些非对话事件行,编写解析时应跳过缺少预期字段的行,避免因格式变动而报错。

cl100k_base与分词差异:tiktoken 是OpenAI开源的tokenizer库,cl100k_base 是它用于GPT-3.5/4系列的一种编码方案。Anthropic的Claude使用自研分词器,两者对同一段文本切分出的token数并不相同,通常差异在个位数到十几个百分点之间,对代码和特殊符号可能更大。因此用 cl100k_base 得到的绝对值不能直接对应Claude账单,但因为它对各类文本的切分是稳定的,用来横向比较“哪个工具返回得更多”仍然可靠。文中提到的“5倍”“95%”等比例结论,都是基于同一分词器的相对比较,受这一偏差影响很小。

怎么读这张表

Bash 排第一?通常是测试输出或构建日志。在模型看到之前就过滤掉(Anthropic成本页提供了只保留失败行的hook方案),或者把嘈杂任务交给子代理,只让摘要返回。

MCP工具排第一?说明那个server习惯性返回大段文本。看看能不能要求它少返回些;如果不行,考虑是否需要让它全天挂着。

Read 排第一,下面列着一串源文件路径?这说明代理在整文件读取、只为了看其中一部分。这正是下面要展开的场景。

整文件读取代价 vs 实际需要的那部分

用一个标准Python就能验证的例子:在CPython 3.12.4中,json/decoder.py 全文是3,024个token(tiktoken cl100k_base),而其中单个函数 py_scanstring 只有557个token。

用 ast 模块定位函数并提取源码段,对比两者编码长度,结果是:代理只需要那个函数却读了整个文件,摄入量是实际所需5倍以上。这还只是一个356行的文件。文件越大,差距越明显;小文件则基本没有差距。

修复手段,按成本从低到高

问更窄的问题。 "改进这个代码库"会让任何代理大范围扫描。指定文件名和函数名,多花十秒打字。

切换无关工作时执行 /clear。 session里还留着的所有内容,都会被重新发送。

如果用的是带类型系统的语言,安装对应的代码智能插件。 Anthropic文档指出,一次go-to-definition调用就能替代"grep一遍、再读几个候选文件"的流程。这是一方能力,免费。

或者使用符号索引。 jCodeMunch就属于这类工具:索引一次仓库,之后按字节偏移把单个函数或类交给代理,替代整文件读取。安装只需两条命令,运行时不需要API密钥:

pip install jcodemunch-mcp
jcodemunch-mcp init

其作者公布的benchmark显示,在3个真实仓库、15个任务上,代码读取token平均减少95%(tiktoken cl100k_base测量)。主页上99.8%那个数字是fastapi/fastapi单次查询的结果:读整棵源码树要214,312个token,走索引只要约480个。但没有任何代理会读整棵树,所以那个数字应当视为理论上限。

选了什么方案,就在做同类工作的session上重跑一次 toolcost.py。索引工具自己的返回结果也会出现在表里,同样计入消耗。如果 Read 行的下降幅度没有超过这些新增行的量,那它就没帮上忙,撤掉。

什么是符号索引与MCP:MCP(Model Context Protocol)是Anthropic推出的开放协议,让Claude Code等客户端以统一方式接入外部工具和数据源,一个MCP server就像给代理挂载的一个能力插件。符号索引类工具(如文中提到的jCodeMunch)是MCP的一种具体应用:它预先扫描代码库,建立函数、类等符号到文件字节偏移的映射表。当代理需要查看某个函数时,工具直接返回该符号对应的源码片段,而不是把整个文件塞进上下文。相比“grep定位、再读候选文件、最后筛选”的传统流程,符号索引把多轮往返压缩成一次精确取用,从而显著降低了进入session的token总量。这类工具的返回结果同样会被后续请求反复携带,所以收益要减去它自身的开销后再评估。

几个常见问题

token少就意味着账单小吗? 用API key的话,输入支出大致同步下降。Pro或Max订阅下,付费不变,改变的是你能用多久才碰到限额。

tiktoken对Claude够准吗? 用来在session内比较不同工具的相对大小,够用;要精确计数,不够。

这套方法能用在Cursor或Codex上吗? 脚本只读Claude Code的日志。方法本身适用于任何记录工具结果的代理,但它们的格式没有验证过。

我的文件都很小怎么办? 那文件读取大概不是你的问题,索引也帮不上什么。去看表里其他行。

先测量,再决定

整篇文章的核心其实只有一句话:别凭感觉判断是谁在吃额度,日志就在你硬盘上,跑个脚本就有答案。工具本身的作者也承认自己有立场(他写了jCodeMunch),但也正因为如此,他反复强调"先测量,不用信我或者任何人的话"。

换工具之前,先用数据确认问题出在哪。你的表里如果出现了本文没覆盖的模式,那才是真正值得讨论的东西。

#Claude Code#工具调用统计#上下文窗口优化#token消耗分析#会话日志#MCP工具#符号索引#AI编程工具
分享:

相关推荐