Agentpath
推演9 / 31 节 · 预计 40 分钟

上下文预算与 compaction

上下文是最贵、最先撞墙的资源。这一节把账算清楚:谁在吃、第几轮撞墙、压缩要压什么。

学完这节你能做到

  • 算出一个 agent 会话的上下文构成,并指出最先撑爆窗口的那一项
  • 预测在给定窗口下大约第几轮触发 compaction
  • 设计一个不破坏 prompt 缓存的压缩策略

上下文是 agent 里最贵、也最先撞墙的资源。它同时决定三件事:能不能跑完、跑得准不准、花多少钱。 这一节把账算清楚。

五个吃客

一次请求的输入由这几块组成,每一轮都要完整重发

吃客特点典型量级
系统提示词固定不变,缓存友好1–5k
AGENTS.md / 项目指令固定,但容易越写越长0.5–3k
工具声明固定,随工具数线性增长每个 100–400
对话历史线性累积每轮 0.3–1k
工具输出波动最大,一次就能吃掉半个窗口每轮 1k–50k

前三项是固定开销:不干活也占着,但内容稳定,prompt 缓存能把它的钱压下来。 后两项是增量,一路往上涨到撞墙。

i窗口是输入输出共享的

200k 窗口不等于能装 200k 输入。要给这一轮的输出留位置 —— 留 8k 输出,可用输入预算就是 192k。算账时别忘了减掉。

自己算一遍

改几个参数,看两件事:不压缩的话第几轮撞墙,以及窗口到底被谁吃掉了。 先按默认值看一遍,再点「工具堆到 40 个」那个预设 —— 注意固定开销那一行的变化。

计算器上下文预算推算
不压缩的话,撑到第几轮
38
可用输入预算 192k · 固定开销 5.8k3%) · 每轮增长约 2.6k
窗口被谁吃掉了
  • 系统提示词2.6k
  • AGENTS.md / 项目指令900
  • 工具声明2.3k
  • 对话历史9.8k
  • 工具输出77k

工具输出是最大的吃客,也是最容易压的一项。给工具的返回值加长度上限、加分页、加摘要 —— 一次 grep 全仓就能吃掉几万 token,而模型真正需要的往往只有几行。

逐轮窗口占用压缩 1 · 首次在第 30 轮
正常过触发线这一轮压缩已超预算
固定开销
5.8k(每轮重发,可被缓存)
40 轮总花费
$6.38(含缓存命中)
若完全不命中缓存
$13.9
缓存省下
$7.57 · 54%
单轮均价
$0.160

几个值得自己试出来的结论:

  1. 把「每轮工具输出」从 2200 调到 20000,撞墙轮次立刻掉到个位数 —— 工具输出是唯一值得优先优化的那一项
  2. 工具从 12 个加到 40 个、每个 320 token,固定开销直接吃掉可用预算的一大块, 而这部分每一轮都在付
  3. 把「压缩后保留历史」调到 60%,压缩次数明显变多 —— 压得浅等于没压。

compaction:省窗口是要花钱的

接近上限时,harness 会把早期历史摘要掉,换出空间。Pi 默认就做这件事,也允许用扩展替换策略。

代价常被算漏:压缩改变了 prompt 前缀,缓存全部失效。那一轮的输入按全价重算。 计算器里的「若完全不命中缓存」和「缓存省下」两行就是在说这笔账 —— 压缩越频繁,缓存的价值被冲刷得越厉害。

!压缩会丢信息,而且丢得很隐蔽

摘要不可能无损。被压掉的往往是「第三轮那个报错的具体行号」这类细节 —— 而它恰恰是后面排障要用的。所以压缩不是终点,是最后手段。

比压缩更该先做的四件事

  • 给工具输出设上限并分页 —— 一次 grep 返回三十行加一句「还有 812 条,缩小范围」, 比返回全部有用得多。
  • 按需读取,别预加载 —— 别把「可能有用」的文件提前塞进上下文,让 agent 自己去读。
  • 外置状态 —— 长任务的进展写进文件(TODO.md、笔记),需要时再读回来。 文件是无限大的上下文。
  • 工具分组注册 —— 四十个工具不必同时在场。这也是 skill 存在的理由: 用得上才装载。
缓存友好的排列顺序

把最稳定的放最前面:系统提示 → 项目指令 → 工具声明 → 历史。 任何在前面插入内容的行为(比如每轮往系统提示里注入当前时间)都会让整个缓存前缀失效 —— 一个动态时间戳就能让缓存命中率归零。

什么时候该换更大的窗口

不是「越大越好」。窗口变大,单轮输入的钱也线性上涨,而且长上下文里的信息检索质量会下降 (中段内容更容易被忽略)。判断顺序是:

  1. 先看计算器里排第一的吃客 —— 能压的先压。
  2. 压不动了,看压缩频率。40 轮压 4 次以上,说明预算结构本身有问题。
  3. 结构没问题、任务本身就需要长上下文(大仓库重构、长文档处理),再换大窗口。
检查点单选

一个 agent 在第 8 轮就报「上下文超限」。用计算器一算,占比最高的是工具输出(每轮平均 18k)。最该先做什么?

这一节的结论

  1. 窗口是输入输出共享的,算账先扣掉输出预留。
  2. 固定开销(提示词 + 指令 + 工具声明)每轮都付,缓存能省钱但省不了窗口。
  3. 工具输出是最大变量,也是最容易压的一项 —— 优化从这里开始。
  4. compaction 是最后手段:它用一次全价重算换空间,还会隐蔽地丢细节。
  5. 先压输出、按需读取、外置状态、分组注册;这四条都做完了再考虑换大窗口。

延伸资料