解析Agent会话转录,属性每条消息到system/tool_definitions/user等分段,统计各段token消耗,识别可避免的浪费。
Token 面板只告诉你账单金额,不告诉你为什么是那个数。当一个 coding agent 变慢、变贵、还变傻的时候,通常是因为它的 context window 悄悄填满了垃圾:同一个文件读了六遍、一个 12k-token 的工具结果只在一个 turn 有用、工具 schema 在每一步都重新发送。你可以看见总量在涨,却看不见 token 去哪儿了,所以也没法有把握地删掉任何东西。
我想要一个针对这个的 profiler。不是聊天 UI,不是实时代理,只是一个我能指向会话记录然后问:这个 context window 里有什么,其中多少是本来可以避免的?这就是 ctxlens。
ctxlens 用处理 CPU profiler 同样的方式处理一个 agent 会话。Profiler 不评判你的代码好不好,它告诉你时间花在哪儿,好让你知道该往哪儿看。ctxlens 对 token 做同样的事。它解析会话记录,把每条消息归属到一个 segment,统计每个 segment 和每个 turn 的 token 数量,然后运行基于规则的检查来标记那些确实被浪费的部分。
每条消息都会落入以下某个桶:system、tool_definitions、user、assistant、thinking、tool_call、tool_result。一旦每个 token 都有了归属,有趣的问题就能回答了。哪个 segment 占主导?Context 什么时候飙升?每个 turn 付出的代价与一次性代价分别是什么?
它读取 Claude Code JSONL 会话(~/.claude/projects/*/*.jsonl 下的那些)、OpenAI/Codex rollout 会话,以及通用的 OpenAI chat 数组。格式通过嗅探文件自动检测,如果需要也可以用 --format 强制指定。
基础运行是一条命令:
pip install ctxlens-cli
ctxlens analyze session.jsonl
你会得到一个摘要面板、按 segment 分解的 context 组成、一组展示 context 在运行过程中如何增长的 sparkline,以及一份建议列表。组成视图是我最先看的地方,因为它立刻回答了"这个 window 是由什么构成的":
Context composition by segment
Segment Tokens % Msgs Share
tool result 6,204 49.7 22 ██████████████·······
assistant 2,110 16.9 14 ██████···············
system 1,540 12.3 1 ████·················
Tool results 吃掉一半 window 是我最常见的现象。这就把我们带到了这个工具的第二部分:浪费报告。
waste_ratio = total_waste / total_tokens,而 total waste 是四个互不重叠来源的总和:
重复 token。同一个文件或工具结果出现超过一次,按引用匹配(例如 Read:file_path=config.py)或按精确正文匹配。第一次之后的每次复制都计入浪费。
工具结果膨胀。单次工具结果中超出每结果上限的 token(--tool-result-cap,默认 400)。只有超过的部分算数,且每个唯一正文只计一次费,所以重复的巨大结果不会在这里被算一次浪费、再在重复项里又被算一次。
陈旧工具输出。当同一引用被读取多次,且后一次读取取代了前一次时,被取代的老副本仍然是死重,依然坐在 context 里。
工具定义超限。工具 schema token 超出预算的部分(--tool-def-budget,默认 800)。这个很伤人,因为你在每个 turn 都要付这个代价。
每条发现都带有严重程度和预估 token 节省量,所以建议读起来像是"'Read:file_path=config.py' 出现了 6 次,约 2,410 tokens",而不是"更好地管理你的 context"这种通用建议。这个估算是精确的算术运算,不是猜的。
因为这一切都是确定性的,它可以嵌入 CI。当一个捕获的会话浪费太多时,你可以让构建失败:
ctxlens analyze session.jsonl --fail-over-ratio 0.30
退出码 0 正常,2 表示超过了阈值,1 是错误。加 --json 获取机器可读输出,或者用 ctxlens diff before.jsonl after.jsonl 来对比基线和候选版本,在修改 prompt 或工具时捕捉回归。还有一个 HTML 报告器,通过 ctxlens report session.jsonl --html -o report.html,用于当你真正想仔细看的时候。
关于计数:默认情况下 ctxlens 使用确定性启发式分词器,不走网络、没有重型依赖,这是刻意设计的。对于相对 profiling 和 CI 阈值,你大多数时候关心的是比例和趋势,稳定的启发式方法让你在所有地方都能得到可复现的数字。如果你安装了 tiktoken,--tokenizer auto 会自动启用它,你得到精确的 BPE 计数。整个项目有 55 个测试,覆盖解析器、分析器、分词器、报告器和 CLI。
一个诚实的局限性
启发式分词器是一个近似值,应该被当作近似值来看待。它的 token 计数不会和你提供商的账单完全一致,所以摘要面板里的绝对数字都是估计值,除非你安装了 tiktoken。不装 tiktoken 依然可靠的是图的形状:哪个 segment 占主导、哪个引用重复了、哪里有峰值。如果你需要报告的 token 数字和实际账单对上,就装那个 extra 用精确计数。我宁愿发一个默认诚实说明自己是快速近似的工具,也不愿意发一个暗示自己有计费级精度但实际没有的工具。
ctxlens 诞生于我厌倦了猜测一个臃肿的 agent 会话里哪部分是安全可裁剪的。有了按 segment 分解的 window,加上重复和陈旧读取都按名称和 token 数量被指出来,这就从猜测变成了编辑。如果你跑 agent 且觉得你的 context window 比应有的更重,拿一个真实会话来跑一下,看看会吐出什么。