用 AI 写了快一年代码,今天聊个最容易被忽略、一旦忽略就很吃亏的坑。

前阵子我让 Claude Code 查一个 bug,一开始是我把方向带偏了。后来我反应过来,纠正它:别按刚才那条路查,应该从另一个入口看。它当时也听懂了,按新方向把问题定位出来了。

麻烦出在后面。

我懒得开新会话,还在同一个 Context Window 里继续干活。中途 Context Window 触发了自动 compact。再后来我让它分析另一个 bug,它居然又回到了我最开始那套错误思路,开始按那个已经被否掉的方法往下查。

第一反应:模型今天抽风了?

其实不是。是压缩后的上下文把"这条路已经被证明是错的"这件事弄丢了。Claude 显示 context 用量的小进度条,已经悄悄爬到 80% 多。而且中间还自动压缩过好几轮。中途我不只是查 bug,还掺杂了别的任务。

Claude Code 状态栏里的 context 占用进度条

状态栏最左边写着 Opus 4.8 (1M context),紧跟那个小条就是当前 context 占用,图里是 25%。这个状态栏是 claude-hud 插件提供的,Claude Code 默认没有,装法见文末 Reference。左边这个百分比,就是后面要反复盯的"预算表"。

Context Window 为什么越满越蠢?

现在的大模型动不动就吹百万 token,Claude 的 Opus、Sonnet 都开到了 1M。听上去你可以把整个项目一股脑塞进去,随便聊。

但能塞进去,跟塞进去还好用,是两回事。

打个比方。Context Window 就像一张办公桌,桌上摊十份文件,找一份一眼就扫到;摊两百份,你就得翻半天,还经常拿错。模型也是这样,塞得越多,它每次找重点就越费劲,越容易抓错。

Context Window 标着能装多少,那是给你看的数字。模型真正用得顺手的,其实就前面那一小段。

而且这不是我一个人的错觉,是有人专门量过的。Anthropic 的 Thariq 给过一个数字:哪怕是号称 100 万 token 的模型,token 一旦堆到三四十万,它就开始明显变笨。所以那种真要动脑子的活,千万别让一个会话一路漂过这条线。

更要命的是,这条线你消不掉,只能管着点。这篇后面会讲到的 /compact、/clear、handoff、subagent,说穿了都在干同一件事:让真正喂进模型的那段上下文,一直保持干净、聚焦。

那多少算满?三四十万 token,摊到 100 万的进度条上,也就三四成的位置。所以我给自己定了个规矩:到 40% 就当红线,过了就提高警惕,绝不等它飙到八九十,到那时候模型早就在犯傻了。

别无脑回车,每一轮都是一个分叉点

Thariq 还有一条心法,我印象很深:Claude 给你回完话,先别急着回车让它接着说。这一轮结束的地方,其实是个分叉口,你手上至少有五个方向能选。

这一轮答完,接下来什么时候用
直接继续还在同一个任务主线上,上下文也还干净
/rewind(连按两下 Esc)上一轮带它走歪了,退回出错前重新说一遍,比在错的基础上硬纠强
/clear要切去一个完全不相关的任务了
/compact <hint>主线还得接着干,但历史太长,带上提示词压一压
丢给 subagent这一步要翻一大堆文件,或者跑脏活累活,扔进一个子会话去办

最后那个 subagent 可能有点陌生。说白了就是开个分身替你干某一段:它在自己的会话里翻文件、试错、忙活,干完只把结论递回来,过程里那一堆垃圾全留在分身那边,脏不到你的主线。

真正关键的是这句:默认动作不该是回车继续,而是每一轮都停半秒,问自己一句,这一步我到底该选哪个。

这半秒,就是会话会不会越聊越蠢的分水岭。后面几节,其实就是把这张表里最常用的几招,一招一招拆开讲。

别等它自动 compact,等到那会儿黄花菜都凉了

Claude Code 是有自动 compact 的。Context Window 快满时,它会把前面的对话压成一段摘要,腾出空间。听上去挺贴心。

问题是它出手太晚,默认得快顶到天花板才动。等它动手,你早在"降智区"里泡了半天了。而且它压出来的摘要还会丢细节,你辛辛苦苦对齐的上下文,一句话就给你带过去。

别等。到 40% 左右,自己敲一下:

1
/compact

笔者小技巧分享: 下面这个设置可以让 Claude Code 在上下文达到 40% 的时候自动 compact。

1
2
3
  "env": {
    "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "40"
  },

更重要的是, compact 时是可以有额外提示词的:

1
/compact 保留鉴权模块的改动方案和已确认的命名约定,调试过程都能丢

主动压的时候你脑子还清醒,知道哪些该留;真等它被动触发,留什么就由模型自己挑了,挑的往往不是你最想留的那部分。

一个会话,我现在只干一件事

这是我现在使用任何 agent coding 工具的习惯。

以前图省事,一个会话从早开到晚:上午加新功能,下午修 bug,晚上顺手再重构。心想反正上下文都在,多方便。

后来发现这是真不行。这三件事要的上下文压根不是一回事:重构要看的是代码现在到底长什么样;修 bug 的时候,你拖进来一堆试错走过的死胡同;加新功能,塞进来的又是一堆还没落地的想法。全堆在一个 Context Window 里,模型自己都分不清,哪些是现实,哪些是你随口一提的草稿。

这就跟做饭一样,你总不能拿切过生肉的刀,直接去切水果。

所以现在,我是加功能开一个会话,修 bug 另起一个,重构再单开一个。Context Window 干净了,模型的脑子也跟着清楚。

这其实就是上面那张表里 /clear 的放大版:任务一换,就把场子清干净,别让上一个任务影响你后面的任务。

开新会话前,用 handoff 把对齐过的内容递过去

一个会话只干一件事,是挺好,但有个很现实的麻烦:开个新会话,它啥都不知道。

你刚在旧会话里跟它对齐了半天,项目结构、踩过的坑、定好的方案,新 Context Window 一开,全清零。从头再讲一遍?那点 token 又白烧了,而且还讲不全。

这时候就轮到 handoff 了。它不在刚才那五个选项里,是个更省事的搬法。说白了,就是把当前会话压成一份交接文档,让下一个会话(也就是下一个 agent)接着干。

它跟 compact 不一样。compact 是在原会话里腾地方,handoff 是干脆打包行李,搬去一个全新的会话。

拿重构走一遍:

1
2
3
4
5
6
# 旧会话(bug 修完,Context Window 已 60%):
"handoff,下一个会话用来重构鉴权模块"

# 它生成一份 handoff 文档,存到系统临时目录
# 你开一个全新会话,把文档喂进去:
"先读这份 handoff,然后开始重构"

新会话拿到的就是一份干净的上下文,调试时的垃圾、半截的想法都没带进来,只剩重构真正用得上的那些。Context Window 从 0% 重新开始。

handoff 的来源、原理和用法,见文末 Reference。

两条不太有人讲的省 Context Window 野路子

再送两条平时不太有人提、我自己却天天在用的省 Context Window 野路子。

一条是,独立的活尽量单开一个 agent 去干。这个 agent 有自己独立的 Context Window,跟你的主会话是隔开的;活干完,它只把结果塞回你这边,中间翻了多少文件、试错了多少回,那些吃 Context Window 的过程全留在它那儿,一点不占你主线的预算。所以凡是能拆出去的独立任务,我都尽量丢给一个单独的 agent,而不是在主会话里亲自下场。这其实就是前面那张表里 subagent 那一项,被我放大成了日常习惯。

另一条,给用 Codex 的朋友:别一上来就在干活的会话里聊方案。我的习惯是先开 Codex 的网页版,把方案、思路、可能踩的坑先聊明白,聊完存成一份 markdown,再拿这份干净的 markdown 喂给 Codex 去动手。好处有两个:一是干活那个会话从头就干净,没有"聊歪了"的废话垫底;二是省钱,网页版不吃你那 20 刀套餐的额度,等于把"想清楚"这件最烧 token 的事,挪到不花钱的地方去做了。

把 Context Window 当预算来花

写到这儿,其实就一点: Context Window is everything。 我们写提示词,封装 skill,或者记 memory,本质上都是在管理上下文。 上下文不是越大越好,它更像预算,花一点少一点,过了一半,性价比就开始往下走。

我现在的几条习惯,你可以拿去抄:

  • Context Window 到 40%,要么 /compact,要么 handoff 换新会话,别硬撑;
  • 一个会话只干一件事,加功能、修 bug、重构,各开各的;
  • 换新会话之前先 handoff,别让对齐过的上下文白白蒸发;(这一条可以省 token,而且非常好用,handoff 的文档还是你的项目应该积累的优质文档)
  • 别指望自动 compact 来救你,等它出手,你已经在降智区泡很久了;
  • 每一轮答完别急着回车,先想想是接着继续、该 /clear,还是丢给 subagent;
  • 能拆出去的独立任务,单开一个 agent 去跑,省下主会话的 Context Window。

说到底,模型"降智",十有八九不是模型的锅,是我们自己把 Context Window 喂撑了。

你呢?Context Window 平时用到多少,会感觉它开始飘?评论区聊聊,也欢迎把你的 Context Window 管理土办法甩出来,互相抄抄作业。


Reference

  • Claude HUD:文中那个状态栏插件,在输入框下方实时显示 context 占用、活跃工具、待办进度。仓库 https://github.com/jarrodwatts/claude-hud 。在 Claude Code 里依次执行就能装好:

    1
    2
    3
    4
    
    /plugin marketplace add jarrodwatts/claude-hud
    /plugin install claude-hud
    /reload-plugins
    /claude-hud:setup
    
  • handoff:把当前会话压成一份交接文档的 skill,Matt Pocock 出品。https://github.com/mattpocock/skills/blob/main/skills/productivity/handoff/SKILL.md