乐于分享
好东西不私藏

对话越长,Claude 越笨?我从源码里找到了真相

对话越长,Claude 越笨?我从源码里找到了真相

系列:比别人多看一层(第二篇)

用 Claude Code 写代码,有个规律很多人都发现了:

刚开始用,又快又准。

对话一长,开始犯迷糊。让它改 A,它改了 B。说过的规范,它忘了。

你以为是模型变笨了。

其实是上下文窗口快撑不住了。

但真正的问题不是”窗口有多大”,而是——你以为的 200K,根本没有 200K 可以用。

我翻了 Claude Code 的源码,把这套上下文管理机制从头到尾拆了一遍。

你以为有 200K,实际能用的只有 167K

Claude Code 的默认上下文窗口是 20 万 token。

但源码里有一套精密的”缓冲区预留”机制,会在你不知情的情况下,把可用空间提前锁掉一大块。

src/utils/context.ts

exportconstCOMPACT_MAX_OUTPUT_TOKENS = 20_000// 为压缩摘要预留 20KexportconstAUTOCOMPACT_BUFFER_TOKENS = 13_000// 自动压缩的安全缓冲 13K

计算一下:

200K(总窗口)- 20K(为压缩摘要预留的输出空间)- 13K(自动压缩的安全缓冲)= 167K(实际触发自动压缩的阈值)

你填满 167K,Claude Code 就会自动压缩对话历史——不会提前问你。

这个 20K 的预留,来自 Anthropic 工程师的真实生产数据:

// Based on p99.99 of compact summary output being 17,387 tokens.// 基于生产环境 p99.99 的压缩摘要输出为 17,387 tokenconstMAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000

他们测了真实用户的压缩摘要,99.99% 不超过 17,387 token,所以预留了 20K 作为上限。这是真实数据,不是拍脑袋。

自动压缩发生时,早期对话会被”改写”

当上下文到达 167K,Claude Code 触发 auto-compact。

它会把整段对话历史压缩成一份摘要,然后用这份摘要替换原来的历史。

这个摘要最多 20K token。

也就是说,一段可能几万字的对话历史,被压缩进了 20K 的摘要里。

细节丢了,上下文丢了,你在对话开头说的那些”这个项目的背景是……”,大概率不完整了。

这不是 bug,是设计。 系统的目标是让对话能继续,而不是保留所有细节。

有个警告线,但很多人没注意到

src/utils/contextSuggestions.ts

constNEAR_CAPACITY_PERCENT = 80// 达到 80% 时触发警告

当上下文使用率到 80%,Claude Code 会在界面上给出提示。

但这个提示很容易被忽略——它不是弹窗,不会打断你的工作流,只是状态栏里一行字。

更重要的是,同一个文件里还定义了几个”浪费检测”阈值:

constLARGE_TOOL_RESULT_PERCENT = 15// 某个工具输出超过总上下文 15%,报警constREAD_BLOAT_PERCENT = 5// Read 工具结果超过总上下文 5%,报警constMEMORY_HIGH_PERCENT = 5// memory 文件占比超 5% 且超 5K token,建议裁减

这些阈值背后,是系统对”什么操作最浪费上下文”的判断。

最浪费上下文的三个操作,源码直接告诉你

src/utils/contextSuggestions.ts 里,系统对不同工具的”可节省 token 比例”有明确估算:

caseBASH_TOOL_NAME:savingsTokensMath.floor(tokens * 0.5)// Bash 工具输出:预估可节省 50%caseFILE_READ_TOOL_NAME:savingsTokensMath.floor(tokens * 0.3)// 文件读取:预估可节省 30%caseWEB_FETCH_TOOL_NAME:savingsTokensMath.floor(tokens * 0.4)// 网页抓取:预估可节省 40%

Bash 工具被认为是最浪费的,系统认为它的输出有一半是可以省掉的。

源码里还附了建议:

Pipe output through headtail, or grep to reduce result size.Avoid cat on large files — use Read with offset/limit instead.// 用 head/tail/grep 过滤输出,减少结果体积// 避免对大文件用 cat,改用带 offset/limit 参数的 Read 工具

cat 一个大文件,是吃上下文最快的操作之一。

另一个隐形杀手:反复读同一个文件

src/utils/contextAnalysis.ts

// Calculate duplicate file reads// 计算重复读取文件造成的 token 浪费if (data.count > 1) {const duplicateTokens = averageTokensPerRead * (data.count - 1)  stats.duplicateFileReads.set(path, { count: data.counttokens: duplicateTokens })}

系统会追踪同一个文件被读了多少次,累计”重复读”浪费的 token。

每次 Read 工具读取一个文件,读取结果会完整保留在对话历史里

你让 Claude 改了三次同一个文件,每次改之前它都读一遍——这个文件的内容就在对话历史里存了三份。

这是长对话后上下文快速被填满的主要原因之一,也是很多人没意识到的。

系统提示词本身也要占地方

还有一个很多人忽略的事:Claude Code 的系统提示词不是免费的,它也要占上下文空间。

源码里把系统提示词分成了两段:

exportfunctionsystemPromptSection(name, compute): SystemPromptSection {return { name, compute, cacheBreakfalse }// 静态区:工具定义、编程规范等,可跨会话缓存}exportfunctionDANGEROUS_uncachedSystemPromptSection(  name, compute, _reason): SystemPromptSection {return { name, compute, cacheBreaktrue }// 动态区:当前目录、session 时间、用户语言偏好等,每轮重新计算}

分界线前的内容(工具定义、规范等)可以被 prompt cache 缓存,多次会话共享,API 费用更低。

分界线后的内容(当前目录、时间、你的 CLAUDE.md 里的规则)每轮都要重新计算,每次都会打破缓存

这意味着:每次你修改 CLAUDE.md,下一次请求会比平时贵——因为缓存失效了,所有动态内容要重新计算。

那 1M 上下文呢?不是所有人都有

源码里关于 1M 上下文的开启逻辑:

exportfunctionmodelSupports1M(modelstring): boolean {const canonical = getCanonicalName(model)return canonical.includes('claude-sonnet-4') || canonical.includes('opus-4-6')// 只有 Sonnet 4 和 Opus 4.6 支持 1M}exportfunctiongetSonnet1mExpTreatmentEnabled(modelstring): boolean {returngetGlobalConfig().clientDataCache?.['coral_reef_sonnet'] === 'true'// 通过 A/B 实验(coral_reef_sonnet)逐步开放}

1M 上下文不是对所有人默认开放的,需要:

  • 使用 Sonnet 4 或 Opus 4.6
  • 模型名带 [1m] 后缀,或通过 A/B 实验分配到实验组
  • 企业环境下没有被管理员用 CLAUDE_CODE_DISABLE_1M_CONTEXT 关掉(HIPAA 合规场景会强制关闭)

同一个模型,不同账号可能上下文容量不一样,原因就在这里。

几个能直接用的结论

1. 新任务开新会话,不要在一个对话里做多件事。

对话历史越长,上下文消耗越快,自动压缩触发越早,细节丢失越多。重要的项目背景写进 CLAUDE.md,而不是依赖对话历史。

2. 不要 cat 大文件,用带参数的 Read

让 Claude 读文件时,告诉它你需要哪部分。Read offset=100 limit=50 比 cat 整个文件省几十倍 token。

3. 注意重复读同一个文件。

如果你在反复修改一个大文件,每次修改前让 Claude 先读,读取结果会在对话历史里堆叠。必要时用 /compact 手动压缩一次。

4. 改 CLAUDE.md 有成本,不要频繁改。

每次修改 CLAUDE.md,都会打破 prompt cache,下一次请求的 API 费用会更高。把规则写稳定,而不是随时调整。

5. 看到 80% 的警告,立刻 /compact

不要等系统自动压缩,手动 /compact 的缓冲区只需要 3K,比自动压缩的 13K 缓冲节省了 10K 可用空间。

最后

200K 上下文,听起来很大。

但系统提示词要占一块,工具调用结果要占一块,对话历史要占一块,自动压缩的缓冲区要占一块。

真正留给”有效工作”的空间,比你想的少得多。

上下文不是无限的水缸,是一块有限的白板。

每次工具调用、每次文件读取、每次 Claude 的回复,都在往上面写字。

写满了,就要擦掉重写——擦掉的那部分,就永远消失了。


下一篇:Claude Code 凭什么决定哪些操作直接帮你执行、哪些要弹窗问你——背后是 6 种权限模式、28 个危险命令黑名单、加上 Anthropic 服务端的远程开关。