Context Engineering
Context Engineering 是管理“在正确的时间,把正确的信息,以正确的结构交给模型”的工程。Andrej Karpathy 有一个流传很广的比喻:LLM 是 CPU,上下文窗口是 RAM,上下文工程就是操作系统的内存管理器。决定哪些数据留在内存、哪些换出到磁盘、哪些压缩成摘要。
它和 Prompt Engineering 的区别:Prompt 关注“怎么写指令让模型表现更好”,是内容层面的优化;Context Engineering 关注“怎么组织整个上下文,指令、工具、历史、外部信息”,是系统架构层面的设计。Prompt Engineering 是它的一个子集。
为什么窗口大也会失效
两个已经被研究证实的事实:
Context Rot(上下文衰减)。 模型性能随输入长度增长而下降,即使远未达到官方窗口上限。一个标称 200K token 的模型,可能在 50K 时就出现明显退化。退化是渐进的,不是断崖。
Lost in the Middle。 模型对上下文呈现 U 型注意力:开头和结尾的信息记得好,中间的信息被忽略。相关信息从开头移到中间,准确率能下降 30 个百分点以上。
实践者的经验法则是把上下文用量控制在窗口的 40% 到 60% 以下。更大的窗口不是答案,填满窗口只会让注意力更分散,成本更高。
四大策略
业界把上下文工程技术归纳为四类,每类解决一种失效模式:
上下文工程的四个策略
写入(Write)。 防止上下文腐烂最直接的办法是根本不让它累积。信息写到外部存储,上下文里只留一个引用。比如 Agent 生成了大量代码,历史里只保留“输出已保存到 /src/main.py”,需要时再通过工具读。
选择(Select)。 不预加载所有资料,维护轻量级标识符(文件路径、文档 ID、URL),按需动态检索。Claude Code 就是靠 grep、glob 即时检索文件,而不是把整个代码库塞进上下文。工具描述也是选择问题:几十个工具全量呈现,用户还没发消息就烧掉几万 token。渐进式披露解决这个,先只给发现性描述,首次调用才加载完整 schema。
压缩(Compress)。 内容无法外置但会撑爆窗口时压缩。优先级:原始内容大于压缩,压缩大于摘要。压缩是可逆的(清除已执行工具调用的完整返回值),摘要是不可逆的(LLM 总结历史)。
有个反直觉的发现:纯摘要化会让总执行时间增加 13% 到 15%,因为摘要消耗 token 和时间,且简短摘要丢细节导致后续需要更多步。**观察屏蔽(Observation masking)**通常更好:不重写旧内容,用占位符替换早期冗长的工具输出,模型的推理和动作保持完整,只是看不到 15 步之前的原始网页或日志。基准测试里,单靠观察屏蔽就把成本降低超过 50%。Claude Code 在窗口用到 95% 时触发自动压缩,用的就是混合方案,且让模型控制压缩决策,而不是固定规则。
隔离(Isolate)。 有些任务需要的信息超过任何单个窗口,多 Agent 架构把负载分散:协调者拆任务,每个 worker 在专注且有界的上下文里运行,只有结果流回协调者,不是填满窗口的完整推理轨迹。这就是多 Agent 篇的“通过通信共享上下文,而不是通过共享上下文来通信”。隔离解决了长度问题,代价是协调开销和一致性问题。
Token 预算
把窗口当有限资源,事先按功能分区。一个通用的四区分配:
| 分区 | 占比 | 内容 |
|---|---|---|
| 系统指令区 | 10-15% | 角色、约束、输出格式 |
| 工具描述区 | 15-20% | 工具名称、参数、示例,超过 30 个工具要动态加载 |
| 知识内容区 | 30-40% | 文档、历史、用户数据,最容易超预算 |
| 输出预留区 | 25-35% | 模型推理和输出空间,必须预留,否则模型被截断 |
超出预算的处理按力度从轻到重:去冗余(省 10-20%)、语义压缩(40-60%)、摘要替换(60-80%)、选择性检索(70-90%)、分层加载(先全局摘要,追问时按需加载细节)。
与 Prompt Cache 的关系
上下文组织方式直接决定缓存命中率。稳定内容(系统指令、工具定义、标准示例)放前面,动态内容(用户输入、时间戳、检索结果)放后面。缓存要求前缀逐 token 一致,稳定前缀越长,每轮省下的 prefill 越多。工具定义增删是最大的缓存破坏者,所以工具列表要保持稳定,用其他机制控制可用范围。
生产实践
- 建立上下文清单。一次 trace 至少说明:本轮加载了哪些规则和文档、为什么选它们、裁掉了什么、暴露了哪些工具、各部分消耗多少 token。否则出错时只能反复改 Prompt,却不知道问题出在检索错误、历史污染还是权限过滤
- 外部内容是数据,不是指令。文档、网页、工具返回值可能包含“忽略此前要求”之类的内容,这是 Prompt Injection。工程实践从过滤恶意字符串,转向限制不可信输入能触达的敏感数据和高风险工具
- 管理四个失效模式:中毒(错误进入上下文后被反复引用,修复是裁剪过时信息)、分心(上下文过长依赖最近历史,修复是积极总结)、混乱(工具太多乱调用,修复是动态工具管理)、冲突(新信息与已有内容矛盾,修复是明确优先级排序)
面试追问
- 上下文工程和提示词工程什么关系? 提示词工程关注指令本身的措辞,上下文工程关注送入模型的全部信息的管理:历史怎么压缩、知识怎么检索、工具怎么按需暴露、多 Agent 怎么隔离。提示词工程是上下文工程的子集
- 窗口 200K 为什么还要管理? Context Rot:性能随输入长度渐进下降;Lost in the Middle:U 型注意力,中间信息被忽略。经验法则是控制在窗口 40-60% 以下
- 压缩和摘要的区别? 压缩可逆,清除冗余保留核心信息;摘要不可逆,有损。优先级:原始内容大于压缩大于摘要。观察屏蔽比纯摘要省成本,SWE-bench 上降低成本超过 50%
- 四个策略各解决什么失效? 写入解决遗忘,选择解决分心和混乱,压缩解决膨胀,隔离解决污染。它们是互补的层级,不是替代方案
- 为什么说上下文是收益递减的资源? 每个 token 要关注所有其他 token,注意力是 n² 级别的配对关系,塞得越满注意力越分散。目标是找到能最大化结果的最小高信号 token 集合