Claude Code 的 100 万 Token 上下文窗口听起来像是一次升级,但在实践中,把窗口限制在 20 万 Token 并提前压缩上下文,反而能获得更好的结果和更低的成本。

为什么我把 Claude Code 的上下文窗口缩回 20 万 Token

雨水覆盖的玻璃后方是模糊的暖色灯光,信号被噪声遮蔽

信号被噪声遮蔽。照片由 c g 拍摄,来自 Unsplash

今天早上,我像往常一样进行每日的“大模型领域动态追踪”,看了一段有关 Claude Code 上下文窗口管理的视频。内容很好,对问题的诊断也很扎实。不过,视频中的解决方案全是人工干预:在合适的时机触发上下文压缩、使用结构化交接、出现偏差时回退而不是继续纠正。这些方法都有效,但真正的问题埋得更深。文档中其实隐藏着两个设置,可能足以解决其中的大部分问题。

更大的空间有什么问题

从 Opus 4.6 开始,Claude Code 默认提供 100 万 Token 的上下文窗口,是上一代 20 万窗口的五倍。听起来完全是一次升级,太棒了!

但事实并非如此。使用大模型时,最重要的一件事就是上下文管理:在信息尽可能相关的前提下,把上下文控制得越小越好,不要塞进任何多余内容。Anthropic 的工程团队建议我们“努力找出一组最精简、但足以完整说明预期行为的信息”。他们的文档也承认:“随着 Token 数量增加,准确率和召回率会下降,这种现象被称为上下文腐化。”根本原因是注意力机制的平方复杂度:上下文扩大一倍,模型需要处理的两两关系就会变成四倍。

遇到这种问题的并不只有我。简单搜索一下,就能看到不少类似观点。Aider 的作者 Paul Gauthier 发现:“似乎每种模型在输入超过约 2.5 万到 3 万个 Token 后都会开始迷糊。”他把这称为用户反馈最多的问题。JetBrains Research 测试了观察结果遮蔽,也就是隐藏旧的工具输出,结果发现成本降低了 52%,任务解决率反而提高了 2.6%。上下文更少,结果更好。NoLiMa 基准测试则发现,12 个受测模型中有 11 个,在上下文仅为 3.2 万 Token 时,表现就已经跌到短上下文水平的 50% 以下。不是 20 万,更不是 100 万,而是只有 3.2 万。

在实际使用中,这意味着更多幻觉、遗忘指令、目标漂移和前后不一致的决策。而且这些问题并不是到 90 万 Token 才出现,而是早得多。

我反复看到的情况

我看到的许多建议都把重点放在人工干预上:自己判断合适的时机触发压缩;切换任务时新开会话或执行 /clear;清空前把状态保存到 JSON 文件;让 Claude 定期总结;或者使用子智能体,避免中间过程进入主上下文。

这些方法都有效。我也大量使用子智能体,因为它们拥有独立、干净的上下文窗口,而这正是避免上下文腐化最有效的架构模式;处理不相关任务时,我也会执行 /clear。但人工干预只是针对窗口过大的权宜之计,并没有解决根本问题。它要求你一边完成实际工作,一边盯着上下文使用量。这种负担本应由工具承担。

Amp 走得更远:他们彻底放弃了上下文压缩,转而围绕短线程和干净的任务交接来设计系统。高级工程师 Dan Mac 直言不讳地说:“基本上永远都不应该使用上下文压缩。”

更简单的解决办法

通过两个环境变量,就能解决这个问题,无需在工作过程中持续分心关注。

CLAUDE_CODE_DISABLE_1M_CONTEXT 设为 1,会把上下文窗口重新限制为 20 万 Token,并从模型选择器中彻底移除 100 万上下文的模型版本。

CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 设为 1 到 100 之间的数值,可以控制自动压缩在上下文容量达到百分之多少时触发。默认值约为 95%。这意味着使用 100 万窗口时,要等到上下文达到 95 万 Token 才会开始压缩,远远超过质量开始下降的临界点。

可以在项目设置或用户设置中同时配置这两个变量:

{
  "env": {
    "CLAUDE_CODE_DISABLE_1M_CONTEXT": "1",
    "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"
  }
}

把 20 万窗口的阈值设为 70%,意味着上下文会在约 14 万 Token 时开始压缩,远早于质量明显下降的时候。压缩会更频繁,但每次需要总结的上下文更少,所以摘要也会更好。Andrej Karpathy 把上下文工程描述为“用下一步恰好需要的信息填充上下文窗口”。信息太多时,“性能可能会下降”。限制窗口大小,会自动迫使系统遵守这条原则。

成本因素

Claude Code 每次交互都会把完整的对话上下文发送到 Anthropic 的服务器。如果窗口中已经堆积了 60 万个 Token 的工具输出、文件内容和旧对话,那么下一条消息仍然会重新发送这些内容,并再次计费。

按照 Opus 4.6 每百万输入 Token 5 美元的价格,60 万 Token 的上下文仅输入成本一项,每轮就要花不少钱。严格来说并不是 3 美元,因为还存在缓存。相比之下,14 万 Token 的上下文,也就是触发压缩之前的大小,理论成本为 0.70 美元。一段包含几十轮交互的长会话下来,差距会迅速累积。Hacker News 上有位用户说,Opus 4.6“陷入一段胡扯式的推理循环”后,很快烧掉了 100 美元额度。因此,更小的窗口不仅质量更好,也更便宜。

上下文压缩不是安全网

常见建议是依靠上下文压缩来保持内容整洁。它确实有用——某种程度上,有时候有用。压缩会总结对话,腾出空间,但这种过程必然会损失信息。哪些内容重要、哪些内容可以丢弃,都由模型判断,而模型的取舍未必与你一致。我经常感觉,压缩之后不得不重新解释正在处理的问题中那些细微之处。

另一个问题是压缩之后会发生什么。昨天,我在一个会话中修改整个仓库的多处代码,自动压缩在任务进行到一半时触发。压缩完成后,Claude 做的第一件事竟然是提交所有修改——我根本没有要求它提交。它丢失了足够多的上下文,以至于忘记我从未要求创建提交;看到工作区里有未提交文件,便自行提交了。

Claude Code 团队的 Thariq Shihipar 建议,在上下文达到容量的 50% 到 60% 时主动压缩,不要等自动压缩。这是个好建议。但如果把窗口限制为 20 万 Token,并把阈值设置为 70%,就能自动获得大致相同的效果,无需紧盯 Token 数量,也不用在合适的时机手动执行 /compact

更小的窗口还有一个好处:压缩本身也会变得更准确。如果在 70% 时触发压缩,并缩减到大约 30%,那么 100 万窗口需要总结并丢弃 40 万个 Token 的对话;20 万窗口只需要丢弃 8 万个 Token,要保留的信息量少了五倍。上下文越小,摘要通常越准确。(如果你熟悉信息论,我知道实际情况比这复杂得多,请原谅这里的简化。)

使用新会话,而不是维持长会话

环境变量可以改善单次会话内的情况,但收益更大的做法,是从一开始就避免长会话,尽量不要走到需要压缩的地步。

我的工作流会把每个阶段拆成单独的会话:研究用一个会话,规划用另一个,实现再用第三个,绝不把它们串在同一段对话中。我还开发了一个编排器来自动完成这件事:每个步骤都会启动一个独立的 Claude Code 实例。整个设计的出发点就是隔离上下文。每个阶段都从干净状态开始,只携带启动该阶段所需的信息。

这与子智能体背后的原理相同,只是规模更大。Claude Code 启动子智能体时,子智能体会获得一份全新的独立上下文。所有中间过程,包括文件读取、grep 输出和失败的尝试,都留在子智能体的上下文中,只有最终结果返回主会话。Morph 的研究发现,相较于单智能体,子智能体架构能带来 90% 的性能提升。原因很直观:每一份没有进入主上下文的文件内容和工具输出,都是一份不会争夺注意力的噪声。

反直觉的结论

100 万 Token 的上下文窗口提升的是容量,而不是质量。空间更大,意味着能装下更多噪声、产生更高账单,并在越过性能退化阈值后输出更差的结果。Steve Smith 把它称为“一个巨大的杂物抽屉”。Glen Rhodes 认为上下文应该被看作工作内存,而不是存储空间;应该像对待资源受限系统中的 RAM 一样对待它:有意识地决定加载什么,并对任何迟迟不肯离开的内容保持警惕。

我使用 Claude Code 时得到的最佳结果,都来自三个习惯:保持较小的窗口、尽量完全避免上下文压缩,以及不让一个阶段污染下一个阶段。两个环境变量,再加上经常新开会话的习惯——诀窍就这么简单。


如果你也遇到了上下文问题,或者正在维护自己的 Claude Code 配置,欢迎联系我交流经验。

资源

Claude Code 文档

研究与分析

开发者观点

相关文章