
上周我让 coding agent 去查一个 PHP 项目里的 bug。它很勤快,连着开了十几个文件,中间还 grep 了两轮,折腾八分钟,最后回我一句:没定位到问题。
我不信,自己看了两个文件就找到了。
问题不在于模型笨。它读的东西太多了,真正相关的那几十行被大量无关代码冲淡了。这个现象有名字:context rot。上下文里的 token 越多,模型准确回忆起其中某段信息的能力越差。Anthropic 在那篇讲上下文工程的文章里直接引用了这个结论,还给了个解释:transformer 里每两个 token 之间都有注意力关系,n 个 token 就是 n 的平方个关系。上下文一长,这份注意力摊薄了。
所以过去一年真正在动的,不是「谁的模型更强」,而是「怎么让 agent 少读点」。十月十九号的 GitHub 趋势榜很能说明问题:前五名里有四个是给 agent 补能力的项目,其中两个直接冲着 token 账单去的。
一、grep 的贵,贵在你读完才知道没用
xerj 这个项目在 README 里把账算得很清楚。WordPress core 的 PHP 代码大约 520 万 token,相当于 26 个满上下文窗口。任何 agent 都不可能把整个仓库塞进去。grep 看着像解法,其实不是:grep 一次命中的是一行,而你要判断这一行有没有用,就得把整个文件打开读进上下文。
他们的实测:一个 grep 驱动的 agent 在一个语料上,输入 token 峰值到了 106 万,钱花完了,问题还在。
xerj 换了个路子。xerj autoindex 把任意文件夹走一遍,靠内容嗅探(不信扩展名)识别出 13 类格式,代码过 tree-sitter 拆出符号和行号,然后建索引,返回的是 passage 加 file:line,不是整个文件。他们拿这个做法和「凭记忆」「grep 驱动」两条基线对比,8 个任务、4 种语言、每种 16 次运行,统计的是真实计费的 token:
| 做法 | 输出 token | 成本 | 解出题数 |
|---|---|---|---|
| 纯凭记忆 | 260,916 | $11.18 | 11/16 |
| grep 驱动 | 26,477 | $3.27 | 16/16 |
| xerj 检索 | 9,982 | $1.58 | 16/16 |
在同样的解出率下,输出 token 是 grep 的约四分之一,成本大约是 grep 的一半。在模型完全没见过的 Rust 库上,检索那条路 9 题全对,凭记忆是 0 题。
这个项目的自我限定也写得直白。他们在另一个实验里,拿 518MB、25 种格式的文件夹出了 10 道题,纯 grep 加 python 的 agent 做到 10/10,xerj 是 9 对加 1 个部分正确。所以他们的结论写得很收敛:小规模、单文件阅读上 grep 完全够用,autoindex 赚的是全库定位速度、百万行级别的聚合查询、以及处理格式混乱的数据。这种肯把「我什么时候不管用」写进 README 的做法,比任何压缩率数字都更值得看。
二、压缩的前提是能还原
headroom 走的是另一条路。它不改变 agent 怎么找数据,而是在数据送给模型之前压一遍:工具输出、日志、RAG 分块、文件、对话历史,全都过。
README 里的那个演示,一份 10,144 token 的日志压到 1,260 token,而里面那行 FATAL 一字不差地保留了。他们给的四个场景 benchmark 分别是:代码搜索 17,199 降到 13,597(省 21%),SRE 事故排查 55,957 降到 24,340(省 57%),代码库探索 58,801 降到 33,895(省 42%),GitHub issue 分类 46,067 降到 32,429(省 30%)。
架构上分了三段。ContentRouter 先判断这段内容是 JSON、源码还是散文,分给对应的压缩器:JSON 走 SmartCrusher,代码走 CodeCompressor(基于 AST),散文走一个在 HuggingFace 上开源的专门小模型 Kompress-v2-base。前面挡了一层 CacheAligner,专门标出会打乱厂商 KV 缓存前缀的易变内容,它只标记,不改写 prompt。
真正有意思的是 CCR。它把原文缓存在本地,模型需要细节时可以调 headroom_retrieve 把原始文本取回来。压缩是有损的,而你不知道损失落在哪一句。一次事故复盘里丢掉的恰好是关键堆栈,agent 后面所有的推理都建立在错的前提上。可逆性是这个方案能用的前提,不是加分项。
性能上也不构成瓶颈。10K token 的 JSON 结果压缩耗时 0.21 毫秒(p50),100K 是 1.4 毫秒。
他们的边界同样写得老实:重复度高的内容(重复 JSON 数组、重复日志行)能省 90% 以上,散文和本来就密的输出几乎压不动。想省钱的人最容易忽略这句,因为省钱效果完全取决于你的负载长什么样,而不是工具宣传的数字。
三、模型厂商自己在做的三个动作
上面两个是第三方补丁。Anthropic 的路线是往模型和 runtime 里做。
那篇文章里列了三种手法:
- compaction:上下文快到顶的时候,把历史交给模型自己总结,保留架构决策、未解决的 bug、关键实现细节,丢掉冗余的工具输出,然后用这份摘要重启一个新的上下文窗口。Claude Code 里是在用到 95% 时自动触发。
- 结构化笔记:agent 定期把笔记写到上下文之外的文件里,需要时再读回来。相当于让它自己管一块外部存储。
- 子 agent 架构:主 agent 只保留高层计划,具体的探索交给子 agent。子 agent 可能烧掉几万 token,但只往上返回 1000 到 2000 token 的浓缩结论。
配套的还有平台侧的 context editing:自动清掉过期的工具调用和结果。他们内部一个 agentic search 的评测里,只用 context editing 提了 29%,叠上 memory 工具提了 39%。
方向不是把窗口做大,是承认窗口里有一部分内容注定要被扔掉,然后设计一套扔得有章法的规则。等更大的窗口是没用的,因为 context rot 说的是同一个道理,装得越多,找得越不准。
四、这三层其实是一个东西
Karpathy 说过,大模型像一个新的操作系统,模型是 CPU,上下文窗口是 RAM。放在这个比喻里,上面三层正好对应内存管理的三个位置。
- 检索层(xerj)对应的是减少缺页:别把整个文件搬进内存,先查页表拿到需要的那一段。
- 压缩层(headroom)对应的是换页加压缩:内存放不下的部分压一压,但要留一条换回来的路。
- 编排层(compaction、笔记、子 agent)对应的是进程隔离和内存回收:每个任务一个干净的地址空间,用完就释放。
三层在做同一件事:把「磁盘上的原始数据」和「内存里正在工作的数据」解耦。
这个解耦一旦成立,很多以前的做法就要重写。比如工具的设计目标不再是「返回尽量完整的信息」,而是「返回刚好够用的信息,并且告诉模型去哪拿剩下的」。Anthropic 那篇文章里专门讲了这一条:工具要 token 高效,功能不能重叠,参数要无歧义。听着像常识,但大部分我们自己写的 MCP server 都是奔着「信息越全越好」去的。
五、两个不太乐观的判断
第一,这类工具的商业价值会被上游吃掉一部分。上下文窗口在扩容,prompt 缓存的价格在往下走,压缩类产品最直观的卖点会被持续削弱。xerj 那个数据其实已经说明了:模型记得住的东西(流行的公开库),检索带来的收益接近零,甚至为负。省 token 的生意,只存在于模型不熟的地方,比如你公司的私有代码、内部文档、刚发布还没进训练集的框架。
第二,调试链路变长了。agent 出错的时候,你要查的到底是它看到的那份压缩后的内容,还是原始的?headroom 的 CCR 给了取回原文的能力,但没有解决「怎么确认这次失败是压缩导致的」这个问题。可观测性会成为这类中间层的下一个瓶颈。
那么真正值得押注的是什么?我认为是可以验证性,不是压缩率。
判断一个「省 token」的工具靠不靠谱,看它敢不敢回答三个问题:什么情况下不管用,压缩或检索之后答案有没有变,出了问题能不能回溯到原文。xerj 把「小规模上打平 grep」写在文档里,headroom 把「散文压不动」和「原文可还原」做进架构里,这两个动作比 57% 这个数字更能说明它们是认真的。
结语
如果你手上有一个天天在跑的 agent,先别急着换模型,也别急着上压缩工具。先给它装个能数 token 的东西,看清楚钱是在哪一步烧掉的。我自己的经验是,大部分浪费发生在「读了一堆不相关文件」和「把整份日志塞进上下文」这两步,而不是在推理本身。
量完再优化,顺序反了会白花钱。
参考来源
- xerj-org/xerj 与 官方文档 :autoindex 的实现、token 对比实验与自我限定
- headroomlabs-ai/headroom :压缩架构、CCR 可逆设计、四场景 benchmark
- Effective context engineering for AI agents ,Anthropic
- Managing context on the Claude Developer Platform ,Anthropic
- Context rot ,Chroma Research
- Context Engineering ,LangChain(Claude Code auto-compact 触发点、四种上下文工程手法)