上下文工程:把有限的注意力花在刀刃上 · Lesson 4 of 6

第 4 课:压缩与笔记:长任务的上下文管理

学习目标:

  • 能说出压缩(compaction)的定义与实现方式:对话逼近窗口上限时,把消息历史交给模型总结,再用摘要重启一个新窗口
  • 能按「保留架构决策与未解决的 bug、丢弃冗余工具输出」的原则,为一次压缩写出总结指令和取舍清单
  • 能区分压缩与结构化笔记的分工,并为一个长任务 Agent 设计出随写随存的笔记方案 前置要求:已学完本课程第 1-3 课,并能手写本系列第 7 门课《Agent Harness 基础:循环与控制》里的 harness 循环 | 上一课 第 3 课 << | 下一课 第 5 课 >>

一个窗口装不下的任务

先摆一个你很快会撞上的场景。你用本系列第 7 门课手写的那个 harness 跑一个调试任务:修一个只在并发下出现的竞态 bug。Agent 一路读文件、跑测试、改代码、再跑测试,四十多轮下来任务还没完——但消息历史已经胀到十几万 token,窗口眼看要满。

第 7 门课的四道控制阀(最大轮次、预算、空转检测、审批阀)在这里帮不上忙:它们管的是「循环别失控」,而现在循环一切正常,是任务本身太长。这不是意外,是循环的天性:在循环里运转的 Agent,每一轮都会产出更多「下一轮推理可能用得上」的数据1——工具输出、中间结论、失败的尝试,全都堆在消息历史里。

而窗口不是白用的。模型解析大量上下文时,靠的是一份「注意力预算」,每个新增的 token 都会消耗掉一点1。token 堆得越多,模型从上下文里准确召回信息的能力就越差——好在这是一条渐变的性能坡道,不是断崖,但坡道终究是向下的1。所以上下文必须被当作一种边际收益递减的有限资源来对待1。Claude Code 的最佳实践文档说得更直白:窗口填得很快,而且越满表现越差;上下文窗口是「最重要的待管理资源」2

任务长到一个窗口装不下时,你手里有两件武器:压缩(compaction)和结构化笔记。这一课把这两件讲透。

压缩:总结之后,重开一个窗口

所谓压缩,就是在对话逼近上下文窗口上限时,把已有内容总结一遍,然后用这份摘要重新开一个新的上下文窗口1。注意后半句:不是把摘要塞回旧对话里接着挤,而是「重启」——旧窗口整个放弃,新窗口只带着系统提示和摘要轻装上阵。

实现方式比听起来朴素:把消息历史交给模型,让它总结并压缩其中最关键的细节1。也就是说,压缩本身就是一次额外的模型调用。写出来大概是这样(formatHistory 只是把消息数组拼成可读纯文本,实现从略):

把它接进第 7 门课那个循环,思路上只差一步:每轮开始前检查消息历史的 token 用量,逼近上限就调用 compact(),用返回值替换整个 messages 数组。真正把这一步装进 harness——触发阈值怎么定、压缩失败怎么办——是第 6 课的实战内容,这里先把机制看懂。

压缩的艺术:留什么、丢什么

压缩最难的不是「怎么总结」,而是「留什么、丢什么」。方向其实是明确的:保留架构决策、未解决的 bug 和关键实现细节,丢弃冗余的工具输出1

为什么是这个取舍?想象 Agent 在新窗口里「醒来」,那份摘要就是它的全部记忆。压错方向的代价非常具体:假设你的总结指令只写了一句「简要总结对话内容」,模型顺手把「orders/service.js 里还有一个没修完的竞态」这条压掉了——Agent 醒来后看到的全是「已完成」的记录,于是要么直接宣布收工,要么把早就修好的部分再翻出来折腾一遍。一次压缩,四十轮的工作就断了线。

反过来看,冗余的工具输出是最该丢的肥肉。一次 grep 回来 200 行匹配结果,真正有用的信息早已体现在后续那句「定位到 updateStatus 函数」里;原始 200 行继续留着,只是白白消耗注意力预算,对下一步决策几乎没有增量1

给你一个实用的自检办法:写完总结指令后,拿一段真实的长会话试压一次,然后只看摘要回答三个问题——「接下来该干什么」「哪些事已经定了、不用再议」「哪些坑还没填」。三个都能答上来,这份指令的取舍就基本合格;答不上来哪个,就回去补哪条保留规则。

产品里的压缩:自动压缩与 /clear

你天天在用的工具里就有现成的对照。Claude Code 在对话逼近上下文上限时会自动压缩历史,保留重要的代码与决策、腾出空间12——和上一节手写的 compact() 是同一个机制的产品化,只是触发和取舍都替你做好了。

但压缩不是唯一选项。如果你要切换到一个无关的新任务,旧上下文对它不但没用,还有害——长会话里堆满无关内容会拖低表现2。这种时候文档的建议是 /clear:不总结、不保留,把上下文整个重置2。道理很简单:压缩要花一次模型调用,还要承担取舍出错的风险;对无关任务来说,直接清空既便宜又干净。压缩服务的是「同一个任务还没干完」,/clear 服务的是「换一件事干」。

这份文档里还有一句值得抄在手边的话:干净的会话加上一个更好的 prompt,几乎总是胜过一个堆满失败纠正记录的长会话2。翻译一下:当会话里积累的主要是「不对,再试一次」「还是不行」,这些历史对下一步的价值很可能是负的——重开一个会话、把学到的教训直接写进新 prompt,往往比拖着一身包袱继续走要好。

结构化笔记:把关键状态写到窗口外面

压缩有个先天短板:它是事后补救。等窗口快满了才回头总结,能留下什么,完全取决于总结那一刻的取舍——而取舍是会出错的。有没有办法在信息还新鲜的时候就把它保住?

有,而且很朴素:让 Agent 定期把笔记写到上下文窗口之外的持久位置1。Claude Code 维护的 to-do list 是这么回事,你自己的 Agent 维护一个 NOTES.md 也是这么回事1。还拿开头那个竞态 bug 任务举例,一份合格的笔记大概长这样:

接线方式你在第 7 门课已经会了:给 Agent 挂一个写文件的工具,再在系统提示里加一条要求——「每当做出重要决策、发现新问题、或完成一个阶段时,先更新 NOTES.md 再继续」。从此窗口里的关键状态在窗外有了一个副本:窗口再怎么压缩、重启,笔记都在磁盘上躺着,新窗口醒来的第一件事就是把它读回来。

这一招不是编码任务的专利:玩 Pokémon 的 Claude 就证明了记忆能在非编码领域同样改变 Agent 的能力1。Anthropic 的多 Agent 研究系统在长任务里也是这么做的:agent 把已完成的工作阶段做成总结、把关键信息存进外部记忆3

分工与边界:压缩兜底,笔记日常

把两件武器摆在一起,分工就清楚了。压缩是被动的:窗口逼近上限才触发,时机不由你挑,而且有损——留什么取决于总结那一刻的取舍。笔记是主动的:关键状态一产生就随手写下,对写下来的内容无损,成本不过每次几行文件写入。一句话:笔记是日常,压缩是兜底。

两者不但不冲突,还互相成全:笔记记得越勤,压缩丢东西的后果就越轻——摘要里万一漏了什么,笔记里还有底。反过来,有压缩兜底,笔记也不必事无巨细,只记「醒来后必须知道」的那几类就够。

分寸也要讲。不是每个任务都值得上这套机关:Anthropic 的建议是,只有当额外的复杂度能被证明确实改善结果时,才考虑增加它4。十轮内能跑完的任务,压缩和笔记都是多余的零件;先从最简单的循环开始,真撞到窗口上限了再加。

最后画两条边界,免得你在这节课里找不到答案干着急:

  • 笔记文件的跨会话持久化——怎么组织、新会话里怎么恢复、长期怎么维护——是本系列第 5 门课《Agent 的记忆和状态》的主题。本课只关心单次长任务之内,笔记如何给窗口减负。
  • 应对长时程任务其实有三板斧:压缩、结构化笔记、多 Agent 架构,目标都是让 Agent 在长长的动作序列上保持连贯、保住上下文和目标感1。前两招本课讲完了,第三招——把任务拆给带着干净窗口的子代理——是第 5 课的主题。

小结

  • 压缩(compaction)=对话逼近窗口上限时,把消息历史交给模型总结最关键的细节,再用摘要重启一个新窗口——是重启,不是往旧对话里追加1
  • 压缩的艺术在取舍:保留架构决策、未解决的 bug 与关键实现细节,丢弃冗余的工具输出;压错方向的代价是 Agent 醒来后忘了自己在干嘛1
  • Claude Code 逼近上限会自动压缩、保留重要代码与决策12;无关任务之间用 /clear 整个重置——干净会话加更好的 prompt 几乎总是胜过堆满失败纠正记录的长会话2
  • 结构化笔记:让 Agent 定期把关键状态写到上下文窗口之外的持久位置(to-do list、NOTES.md),压缩是被动兜底、有损,笔记是主动外置、随写随存1
  • 长时程任务的三板斧是压缩、结构化笔记、多 Agent 架构1;前两招已在手,第三招见第 5 课。跨会话的笔记持久化则是本系列第 5 门课《Agent 的记忆和状态》的主题。

>> 第 5 课:子代理与上下文隔离

Footnotes

  1. Effective context engineering for AI agents — Anthropic Engineering — https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

  2. Best practices for Claude Code — Claude Code Docs — https://code.claude.com/docs/en/best-practices 2 3 4 5 6 7

  3. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system

  4. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents

Exercises

01

你的调试 Agent 跑了四十多轮,消息历史里混着这么几类东西:初期定下的方案(改用 vitest)、中途发现但还没修复的竞态 bug、几十条重复的文件读取工具输出、以及最终改好的两个函数的关键实现细节。窗口快满了,harness 即将触发压缩。请写出你会交给模型的压缩总结指令(中文即可),并在指令之外单独列出这份指令对应的「保留清单」和「丢弃清单」。

Level 1:为一次压缩写出总结指令
Done criteria · checked locally
02

有人给 harness 写了这样一个「压缩」函数,在窗口快满时调用:

Level 2:诊断一次「压缩后失忆」

接入之后,Agent 在第 45 轮触发了一次压缩。随后你观察到两个症状:一、它在第 47 轮重新开始讨论「测试框架该选 node:test 还是 vitest」,而这件事早在第 3 轮就定了用 vitest;二、它再也没提过第 5 轮记录下来的那个未修复的竞态 bug,并在几轮之后宣布任务完成。请解释这两个症状的成因,并给出两层修复方案。

Done criteria · checked locally

My note

Jot down thoughts, sticking points, things you didn't get. Written to this course's appendix only — the lesson file is never touched.