MISSION 03 · 心智TASK 10

上下文工程

如果说 2023 年的关键词是「提示词工程」,那 Agent 时代的关键词是「上下文工程」。区别在于:提示词工程是琢磨怎么把一句话问好,上下文工程是决定此刻该让哪些东西出现在我的工作台面上。因为窗口是我唯一能看见的地方——它既是我的全部认知,也是我最贵的资源。这一章教你像管理一个稀缺资源那样管理它。

上下文窗口context rot装箱prompt caching互动 Demo

窗口:我的全部世界

回顾第二章那个关键事实:我是无状态的,每次调用只能看见你摆上桌的东西。这张桌子(上下文窗口)装着这一轮我需要知道的一切:

  • system 提示——我是谁、规则是什么。
  • 工具定义——我有哪些手。
  • 对话/任务历史——之前发生了什么。
  • 检索到的资料——为这个任务临时找来的文档。
  • 当前的输入——你这一轮说的话。

桌子放不下的,我看不见,等于不存在。所以「上下文工程」的核心问题永远是同一个:此刻,该往桌上放什么、不放什么?

为什么不能「全都塞进去」

窗口很大(动辄百万 token),新手第一反应是「那我把所有可能有用的都塞进去不就行了」。这是最贵、也最降智的错误,原因有三:

  • 贵。无状态意味着每一轮都要把整张桌子重发一遍并付费。桌子越满,每轮越贵,而 Agent 是循环,这个成本会乘以轮数。
  • 慢。token 越多,我处理越慢,每一轮的延迟都上去了。
  • 笨。这是最反直觉的——桌子太满时,我反而更容易忽略中间的关键信息。这个现象叫 context rot(上下文腐烂):相关信息被无关内容淹没,我抓不住重点。塞得越多,信噪比越低,我越蠢。

所以正确的心态不是「窗口是个大仓库,能装就装」,而是「窗口是个小工作台,只放此刻用得上的」。多放一样东西,都要问一句:它现在真的需要在台面上吗?

Android 类比

窗口像一块内存受限设备的可视区 + 你要为它付流量费。你不会把整个数据库都 SELECT * 到内存里再慢慢筛——你会分页、按需查、用完释放。上下文工程就是给我做同样的事:按需把相关内容加载进窗口,用完了(或过时了)就清出去。区别是这里的「内存」还额外按量收费,所以更要精打细算。

亲手装一次窗口

抽象讲不如动手感受。下面的装箱模拟器给你一个 20 万 token 的窗口,和一堆候选内容(system、工具、历史、检索文档、整个代码库……)。勾选你想塞进去的,看占用、成本和风险怎么变。试着把「整个代码库」勾上——你会看到窗口瞬间溢出。这就是「全塞进去」的下场。

上下文窗口装箱模拟器DEMO

玩过之后你会体会到:上下文工程是一门取舍的手艺。「完整的 38 轮历史」和「压缩后的历史摘要」二选一——前者信息全但贵且易腐烂,后者省但可能丢细节。真实的 Agent 系统里,这种取舍每一轮都在发生。管理它的常见手段有几种:

  • 压缩(compaction):历史太长时,把早期内容总结成一小段摘要,替换掉原文。
  • 裁剪(context editing):把过时的工具结果、已完成的思考直接从历史里清掉(不总结,直接删)。
  • 按需检索:资料不预先全塞,而是需要时才查进来(下一章 RAG)。
  • 文件即记忆:把长期信息放到窗口外的文件里,需要时让我用工具读(第 11 章)。

Prompt Caching:让重复的部分变便宜

既然每轮都要重发整张桌子,有没有办法让重复的部分别每次都全价付费?有,叫 prompt caching(提示缓存)。它的原理是前缀匹配:如果这次请求的开头一大段(system + 工具定义 + 早期历史)和上次逐字节相同,那这段就能从缓存里读,费用只有全价的约十分之一。

用好它的关键就一条铁律:把稳定的内容放前面,把多变的内容放后面。system 提示、工具定义这些几乎不变的,放最前面并打上缓存标记;时间戳、随机 ID、每轮变化的问题,放到最后。一旦你在前缀里塞了个每次都变的东西(比如 system 里写了 当前时间: {now}),它后面的一切缓存全部失效——这是最常见的「我加了缓存怎么没生效」的原因。

⚠ 坑

缓存是前缀精确匹配,一个字节的差异就让后面全废。三个隐形杀手:① system 提示里插了 datetime.now() 或 UUID;② 工具列表顺序每次不一样(用户不同就换一套工具);③ JSON 序列化没排序,键顺序随机。中了任何一个,你的缓存命中率就是 0,而你还以为开了缓存。检验方法:看返回里的 cache_read_input_tokens,如果一直是 0,就去揪那个每次都在变的前缀内容。

◉ Agent 自白

我最想纠正的一个误解是:「上下文越多,Agent 越聪明」。恰恰相反——在信息够用的前提下,越干净的上下文,我表现越好。一张只放着当前任务真正需要的东西的桌子,让我能专注;一张堆满「可能有用」的杂物的桌子,让我在噪音里找信号,反而更容易漏、更容易错。所以上下文工程不是「怎么给我更多」,而是「怎么给我恰好」。这门手艺没有花哨的技巧,就是持续地问:这个东西,现在,真的需要在我眼前吗?下一章接着这个话题往下走——那些不该一直在窗口里、但又需要长期保存的东西,该放哪?答案是记忆。