Anthropic《Effective context engineering》系统解读:注意力预算与 PPT Master 的上下文纪律
原文:Effective context engineering for AI agents
实践项目:PPT Master
前置阅读:《Building effective agents》系统解读 · 《Agent Skills》系统解读
上一篇解读讨论的是知识如何 分层存放——渐进式披露解决的是"总量无限而窗口有限"。这一篇讨论的是另一半问题:已经进入窗口的 token,如何在整个任务过程中被管理。
这两件事经常被混为一谈,但失败方式完全不同。分层存放失败,表现为该读的没读到;上下文管理失败,表现为读到了却用不好——模型在长上下文里丢失早期约束、重复读取同一份文件、或者被无关细节挤占了判断空间。
PPT Master 是检验这套方法的好样本。以 Default Generate 的长篇任务为例:例如一次生成 20 页 SVG,每页都要参考前面所有页面,还要在整个过程中保持一份完整设计契约有效;Quick 和原生 PPTX 路线则有不同的上下文负担。
一、基本概念:从 Prompt 到 Context
原文对两者的界定是:
Context engineering refers to the set of strategies for curating and maintaining the optimal set of tokens (information) during LLM inference, including all the other information that may land there outside of the prompts.
区别不在范围大小,而在 时间维度:
| 维度 | Prompt Engineering | Context Engineering |
|---|---|---|
| 对象 | 系统提示与用户提示 | 系统指令、工具、外部数据、消息历史等全部状态 |
| 形态 | 离散任务,写好即固定 | 迭代过程,每一轮都要重新决策 |
| 问题 | 怎么把话说清楚 | 这一轮该让哪些 token 在场 |
当 Agent 从单次任务走向多轮循环和长周期运行,"写好一段提示"就不再是主要工作。真正的工作变成:在每一轮推理前,决定什么进来、什么留下、什么被丢弃。
二、为什么上下文是有限资源
这一节是全文的立论基础,也是最容易被"反正窗口够大"心态忽略的部分。
Context Rot
原文指出,随着上下文窗口中 token 数量增加,模型准确召回其中信息的能力会下降。不同模型程度不同,但这个特性普遍存在。
注意力预算
Every new token introduced depletes this budget by some amount, increasing the need to carefully curate the tokens available to the LLM.
原文把它归因到架构层面:Transformer 需要为每个 token 建立与其他所有 token 的关系,即 n² 对关系。上下文越长,捕捉这些成对关系的能力被摊得越薄。同时训练数据中短序列更常见,模型对长距离依赖的经验本就更少。
结果是 性能梯度而非断崖:长上下文下模型仍然可用,但信息检索与长距离推理的精度在持续降低。
这一点为什么重要
"没有报错"不等于"上下文健康"。上下文腐化的表现是准确率缓慢下滑——模型开始忽略早期确定的约束、在细节上产生偏移,而这些不会触发任何异常。
由此得出的总原则
原文反复回到同一句话:
Find the smallest set of high-signal tokens that maximize the likelihood of your desired outcome.
最小的高信号集合。这是后面所有策略的判据。
三、什么是好的上下文
System Prompt 的"高度"
原文用 altitude 描述系统提示的抽象层级,并指出两种失败模式:
| 失败模式 | 表现 | 后果 |
|---|---|---|
| 过度脆弱 | 在提示里硬编码复杂的 if-else 逻辑 | 维护困难,遇到未列举情况就失效 |
| 过度模糊 | 只给含糊的高层指导,假设共享背景 | 模型缺少可执行的判断依据 |
理想状态在两者之间的 Goldilocks zone:
Specific enough to guide behavior effectively, yet flexible enough to provide the model with strong heuristics.
组织形式上,原文建议用 XML 标签或 Markdown 标题分隔不同部分。
工具
工具应当自包含、对错误鲁棒、用途极其清晰,并返回 token 高效的信息。原文给出的检验标准很实用:
人工工程师能否确定性地说出,某个场景下应该用哪个工具?
如果人都要犹豫,模型必然会错。常见病症是 bloated tool sets——工具集覆盖过多功能,或造成模糊的选择决策。
示例
原文不主张把所有边界情况堆进提示,而是精选一套多样的、规范的示例:
For an LLM, examples are the "pictures" worth a thousand words.
四、长任务的三种策略
这是原文最具操作性的部分。三种策略解决同一个问题——任务长度超过单个上下文窗口——但机制和适用场景不同。
Compaction(压缩)
机制:接近窗口上限时,把消息历史交给模型总结,用摘要重启一个新窗口。
保留架构决策、未解决的 bug、实现细节;丢弃冗余的工具输出和消息。Claude Code 的实现会保留最近访问的 5 个文件。
原文强调,压缩的 艺术在于取舍:过度压缩会丢掉那些后来才显出关键性的细微上下文。建议的调参顺序是先最大化 recall 确保捕捉全部相关信息,再迭代提高 precision 消除冗余。
适用:需要大量来回互动的任务。
Structured Note-taking(结构化笔记)
机制:Agent 定期把笔记写到上下文之外的持久存储,需要时再拉回来。
原文举的例子里,Claude 玩 Pokémon 的案例最能说明问题——跨越数千步游戏仍能维持精确计数:"for the last 1,234 steps I've been training my Pokémon in Route 1, Pikachu has gained 8 levels toward the target of 10"。而且这些笔记行为是自发涌现的:绘制已探索区域地图、记录已解锁成就、维护战斗策略。
适用:有清晰里程碑的迭代式开发。
Sub-agent Architectures(子代理)
机制:不由一个 Agent 维持整个项目状态,而是让专门的子代理在 干净的上下文窗口 里处理聚焦任务。主 Agent 负责高层协调,子代理做深层工作。
关键在返回值的比例:
子代理可以使用数万 token,但只返回 1000–2000 token 的压缩摘要。
适用:复杂研究与分析,尤其是并行探索回报丰厚的场景。
五、即时检索与混合策略
原文对比了两种获取数据的方式。
预先检索:任务开始前把所有可能相关的数据处理好放进上下文。
即时检索(just-in-time):只维持轻量标识符——文件路径、查询语句、链接——运行时用工具动态加载。
原文的类比很到位:
We generally don't memorize entire corpuses of information, but rather introduce external organization and indexing systems like file systems, inboxes, and bookmarks to retrieve relevant information on demand.
元数据本身就是信号
文件名、目录层级、命名约定、时间戳都携带信息。原文的例子:test_utils.py 出现在 tests 文件夹和出现在 src/core_logic/ 里,含义完全不同。 读文件名就能做出的判断,不需要读文件内容。
权衡与混合
即时检索的代价是运行时探索比预计算慢,而且需要精心设计工具与启发式——否则 Agent 会把上下文浪费在工具误用和死胡同上。
Claude Code 采用的是混合模型:CLAUDE.md 直接预先放入上下文,而 glob、grep 这类原始工具支持即时导航。原文认为内容变化少的场景(法律、金融)更适合预先检索,并给出一条总结性建议:
Do the simplest thing that works.
从概念转入实践
以上五节是对原文的梳理。以下用 PPT Master 检验:它在哪些地方独立发明了原文描述的机制,在哪些地方做出了更细的区分,以及哪些策略被有意拒绝。
六、design_spec 与 spec_lock:预先设计的可回溯投影
原文说 compaction 的艺术在于取舍,而取舍发生在运行时——模型临时决定什么该留。
Default Generate 的做法不同。它在流程设计阶段固定了两份制品;Quick profile 会跳过 Strategist、确认流程以及这两份文件,不能套用下面的链条。
| 制品 | 消费者 | 内容 |
|---|---|---|
design_spec.md | 人类审阅 + 上游判断 | 完整设计叙事、沟通目标、内容大纲、§IX 页面清单、资源计划 |
spec_lock.md | Executor 执行 | 颜色、字体、每页节奏(page_rhythm)、路由锚点、资源锚点 |
在上一篇解读里,我把这对制品理解为"面向不同消费者的必要重复"。放在上下文工程的视角下,还能看到第二重意义:
笔者观点
spec_lock.md 本质上是 一次预先设计的、schema 固定的执行投影。它把经最终确认并写入 design_spec.md 的上游信息,收敛成 Executor 需要稳定遵守的跨页约束;区别在于投影规则是提前写好的,而不是运行时由模型即兴决定。
这带来两个直接好处:
- 可预测。运行时 compaction 每次保留什么取决于模型当时的判断;
spec_lock.md的字段和消费者是明确的,结构缺失可以被校验发现。 - 可回溯。
spec_lock.md自身是有意有损的紧凑投影,但完整的design_spec.md仍在磁盘上。原文警告的"过度压缩丢掉后来才显关键的细节",在这里的解法不是声称 lock 无损,而是 保留可回溯的权威源。
这条权威链是“最终确认状态 → 经审计的 design_spec.md → spec_lock.md”。lock 服务于 Executor,不是独立权威来源,也不是列出所有允许实现细节的白名单。
executor-base.md 里那条规则正是这个设计的运行时体现:
Uncertain:consult the retained lock first, then only the owning Design Spec fragment.
先查压缩版,不够再回原件——而且只读相关片段,不是整份重读。
七、split mode:在需要时用干净重启替代有损压缩
PPT Master 的 Default Generate 默认采用连续执行。split mode 只有在用户显式选择,并最终写入 design_spec.md §I 后才启用;保留上下文偏重只会触发一条 split 建议,不会由系统自动切换。它提供了原文三种方法之外的第四种选择:经用户选择后,在窗口耗尽前主动切断会话。
机制是这样的:
规划会话(Step 1–5)
→ 产出 design_spec.md + spec_lock.md + images/ + templates/
→ 会话在此终止
执行会话(全新窗口)
→ 输入「继续生成 projects/<项目名>」
→ resume-execute 从磁盘校验并重建上下文
→ 执行 Step 6–7(SVG 生成 + 导出)resume-execute.md 明确声明自己的性质:
This stage is context-independent: it owns the execution session starting from a fresh chat — no upstream conversation context required. Persisted project artifacts replace the planning session's confirmation dialogue and image-acquisition history.
持久化制品替代了规划会话的对话历史。 这句话是对原文 structured note-taking 的一个更强版本:笔记不只是"需要时拉回来的补充",而是 完整取代了上游会话。
触发判断
generate-pptx.md 根据推荐页数、源材料体积以及研究后仍留在主会话中的上下文判断是否建议切换;用户也可以显式要求 split。成功隔离在研究 worker 中、没有进入主上下文的原始抓取内容不计入这一压力信号;导入后必须完整读取的两份研究制品则属于主上下文的一部分。
因此 split 不是每次 Generate 的固定中断点,而是一次主动的注意力预算管理:在确有压力时于腐化发生之前切断,而不是等自动压缩介入。
与 compaction 的关系
PPT Master 并没有忽略 compaction,只是把它当作 需要检测和恢复的外部事件,而非主动策略。executor-base.md §2.1 定义了三种上下文状态:
| 状态 | 条件 | 应对 |
|---|---|---|
| Valid | 完整 Design Spec 与 lock 仍在未改变、未压缩的活动上下文中 | 直接复用,不重读也不轮询 |
| Invalid | 全新/恢复/重启的会话、压缩或仅剩摘要、外部或未知变更 | 完整重读 design_spec.md,再读 spec_lock.md,加上被触发的引用 |
| Uncertain | 局部疑问 | 先查 lock,再读对应的 Design Spec 片段 |
笔者归纳
这是一份 上下文有效性契约,粒度比原文更细。原文讨论"何时压缩",这里回答的是压缩之后"如何判断手上的信息还能不能信"。任何依赖长期约束的 Agent 都需要类似的三态判断,否则模型无法区分"我记得这件事"和"我以为我记得"。
其中"Valid 时不重读也不轮询"这条同样重要——它防止的是另一种浪费:为了确认信息还在,反复把同一份文件读进上下文。
八、子代理:双向判据
PPT Master 对 sub-agent 的态度经常被误读为"不用"。实际情况是 分任务的,而且两个方向都有明确规定。
禁用:SVG 页面生成
⚠️ Main-agent only: SVG generation MUST stay in the current main agent
— page design depends on full upstream context. Do NOT delegate to sub-agents.executor-base.md 给出的理由是"pages share upstream context for cross-page visual continuity"。
启用:主题研究与视觉审查
topic-research 在宿主支持时默认交给一个隔离 worker:原始网页正文与抓取记录留在 worker 上下文,250 词上限只约束 worker 的聊天回执,不约束或替代 research supplement 与 fact-provenance JSON。两份制品导入后,Strategist 或 Quick 主代理必须在规划或直接绘制前完整读取,不能用聊天回执或结构校验摘要代替研究内容。宿主不能提供子代理或网页访问时,流程才退回主上下文执行。
用户触发 visual-review 阶段后,在宿主支持并行子代理时也会采用分批审查;不支持时则顺序回退。其设计包括:
| 设计点 | 具体做法 |
|---|---|
| 批量划分 | N 个页面分成 ceil(N/K) 批,默认 K=5,每批一个子代理 |
| 并行派发 | 宿主支持时在同一轮并行派发批次;否则顺序回退 |
| 提示自包含 | 每个子代理的提示不依赖任何先前会话上下文,绝对路径全部内联 |
| 固定上下文摊薄 | 子代理开场读一次固定输入,然后顺序处理批内每一页 |
| 权限分层 | 品牌级问题(spec_lock.md 定义的 token)不由单页子代理修改,上交 orchestrator 聚合处理 |
其中"固定上下文摊薄"这条,原文给出了明确的 token 经济学:
This is the core token-saving move — fixed context is read N/K times instead of N times.
判据是什么
把两个方向放在一起,可以提炼出比原文更具体的判据:
笔者归纳
子代理的适用性,不取决于任务能否并行,而取决于 任务的质量是否来自共享上下文。
- 页面生成:后一页的视觉决策依赖前面所有页面 → 上下文是质量来源 → 必须串行同窗口。
- 主题研究:原始抓取留在 worker,两份整理后的研究制品完整进入主上下文 → 隔离检索污染,但不压缩研究结论。
- 页面审查:每页对照固定 rubric 独立评分,页与页之间依赖较弱 → 适合在宿主允许时分批隔离。
这条判据与上一篇解读中"为什么不用 Parallelization"的结论是同一件事,但现在有了双向证据:同一个系统在两个阶段做了相反的选择,而选择依据完全一致。
九、即时检索的三处实证
PPT Master 在多个位置独立实现了原文描述的 just-in-time 模式,其中三处特别典型。
1. 图像:用元数据决策,只在歧义时看原件
Do not bulk-open images. Strategist starts from context, filenames, records, and
image_analysis.csv; inspect only a specifically ambiguous asset.
这是教科书级的即时检索:文件名和 CSV 记录承担常规判断,只有具体某张图产生歧义时才真正打开它。Executor 侧的权限更窄——只能为解决裁切、焦点或文字对比度而查看一张已选定的图,不得借此重新选图。
这一条同时呼应了上一篇解读中的 selection 与 realization 分权:即时检索的授权范围,是按角色职责切分的。
2. 两级索引:source_profile.json
处理多份 PPTX 源文件时:
Read it once for the
decks[]digests, then open a specific deck's<stem>.identity.json/<stem>.slide_library.jsononly if you need its full raw facts.
先读索引摘要,再按需下钻到单份原始事实。而且强制读取契约只覆盖 紧凑的结构化数据文件(.json / .csv),其他可能存在于 analysis/ 下的产物明确不批量读取。
3. 工具输出:区分"读全"与"不读"
质量检查器的使用规则里有两条看似矛盾的要求:
- 运行时 不得 用
tail/head/grep过滤输出——"One run already reports all pages"; - 成功时 不要 把完整 JSON 读进上下文——"use the exit status and terminal summary"。
两条其实指向同一个目标。过滤输出会导致"发现一个问题、修一个、再跑一次"的往返循环,每一轮都在累积上下文;而成功时读完整 JSON 是纯粹的浪费。
笔者归纳
Token 高效的工具输出不等于"输出越少越好",而是 一次给全需要的,一点不给不需要的。判断标准是这次调用要不要基于输出做决策——要,就完整给;不要,退出码就够了。
十、Default Generate 的第一页作为运行时 few-shot
原文讨论示例时讲的是预先精选的规范样例。PPT Master 的 Default Generate 有一个变体值得记录;Quick profile 跳过第一页门,不采用这一节的节奏。
executor-base.md 规定的生成节奏是:
P01 → 首页质量门 → 不间断生成剩余页面 → 最终门首页完成后立即跑检查器,把 P01 暴露的全部问题做一次合并修复,验证通过后再连续画 P02 到最后一页,中途不再调用检查器。
这实际上让 P01 承担了双重角色:它既是交付物的一部分,又是后续所有页面的 运行时生成的示例。后续页面参照的不是文档里的抽象规则,而是一个已经通过检验的具体页面。
原文说"examples are the pictures worth a thousand words"——这里的示例不是预先准备的,而是在任务内部生长出来的。
十一、对照原文校准现行边界
以下为笔者基于原文对 PPT Master 现状的检查结论,其中部分早期改进方向已经落地,不能再按缺口描述。
1. topic-research 已采用隔离优先
这一项已经从改进建议变为现行机制。
宿主允许时,系统使用恰好一个研究 worker,把原始网页正文和抓取记录留在隔离上下文。worker 写入 research supplement 与 fact-provenance JSON;不超过 250 词的聊天回执只报告状态、路径和缺口统计,两份研究制品本身不受该上限限制。
导入后,Strategist 或 Quick 主代理必须完整读取这两份制品,使研究结论进入主上下文。聊天回执和结构校验摘要都不能替代正文内容。因此,这套机制隔离的是检索噪声,而不是把研究本身压缩成 250 词。
如果宿主不支持子代理或 worker 无法访问网页,才回退到主上下文研究。这个 fallback 保证功能可用,但此时原始研究内容仍可能增加 split 的必要性。
这验证了第八节的判据:研究任务的质量主要来自检索与证据整理,而不是与页面绘制共享整段上下文,因此适合隔离;核心页面生成则继续留在主 Agent 中串行执行。
2. 运行时上下文只有局部遥测
需要先说明一个已经存在的东西:PPT Master 有 scripts/prompt_audit.py,一个面向维护者的 prompt 预算审计工具,检查语料总量与热点文件的 token 上限、各加载集(路由/阶段场景)的预算、加载覆盖率与引用完整性,超预算直接报 error。它的预算策略也很克制——上限一旦设定就不允许因为语料增长而调高,只有真正溢出时才提升到下一个取整档位。
所以 静态语料这一侧,上下文预算已经被量化并纳入了 lint。
运行时一侧也不再是完全没有度量。project_manager.py page-context --record-usage 会记录输入哈希、紧凑投影 stdout 的精确字节量和估算 token,并写入 analysis/page-context/P<NN>.usage.json,可用于单页上下文的遥测和调试。
但这只是 局部遥测:它没有测量一次性加载的引用、源材料读取、完整会话历史或宿主自身的 compaction 状态,也不是每页生成前的强制门。因此 split 的判断仍结合客观制品规模与定性上下文状态,而不是由一个完整的 token 仪表自动决定。
笔者观点
prompt_audit.py 管静态加载预算,page-context --record-usage 提供单页投影遥测。两者已经覆盖了可稳定测量的部分,但都不能代表宿主中的完整运行时上下文;解释数据时必须保留这条边界。
3. 上下文有效性靠模型自我判断
executor-base.md §2.1 的三态契约设计得很完整,但状态判定本身由模型完成——它需要自己意识到"我的上下文被压缩过了"。
这是一个已知的薄弱点:模型对自身上下文是否被压缩的感知并不可靠。当前输入哈希能发现投影制品变化,却不能证明宿主会话未被压缩。若未来出现可复现的失忆故障,适合增加独立的上下文有效性信号;不应为了检测会话状态而扩大 spec_lock.md 的职责。
十二、我的理解:上下文管理的三个层次
把原文与 PPT Master 的实践放在一起,我对上下文工程的理解可以分成三层。
第一层:减少进入
即时检索、元数据决策、token 高效的工具输出,目标都是 让不必要的信息从一开始就不要进来。PPT Master 的"不批量打开图片"、两级索引、成功时只看退出码,都属于这一层。这是成本最低的一层,因为没进来的 token 不需要任何后续管理。
第二层:管理留存
上下文有效性契约、Valid 时不重读、spec_lock.md 作为执行投影视图,处理的是 已经进来的信息如何被复用而不是被反复重建。这一层的核心问题是判断"手上这份信息还能不能信"。
第三层:跨越窗口
条件式 split mode、持久化制品、研究 worker 隔离,解决的是 任务长度超过单窗口时如何保持连续。原文的三种策略都在这一层,PPT Master 补充了一种可选的干净重启,并用 schema 固定的制品保证重启后能恢复执行。
核心观点
上下文工程的本质,是 为每一个 token 找到它的付费理由。
进入上下文的信息应当回答一个问题:它是否会改变模型接下来的某个决策。改变不了的,就不该进来;改变过一次已经落到制品上的,就不该继续留着;跨越窗口仍然需要的,就必须以可校验的形式写到磁盘上。
而 PPT Master 的经验补充了原文没有强调的一点:最可靠的压缩是设计出来的,不是运行时算出来的。当你能提前定义"执行阶段究竟需要哪些字段",就不必把这个判断留给模型在窗口告急时临时决定。
十三、下一步
- Effective harnesses for long-running agents— 本文第七节的 split mode 与制品链,正是一个手工搭建的 harness,需要和官方设计对照(系统解读);
- Writing effective tools for agents— 深化第九节讨论的工具输出设计(系统解读);
- Demystifying evals for AI agents— 第十一节涉及的现行机制与剩余边界,需要用评估手段区分已验证事实和设计判断(系统解读)。