历史压缩
长会话会被有策略地压缩,而不是简单粗暴地截断。有两个触发点,一个开销很小、在每次调用模型之前都会跑 一遍,还有一条针对 provider 直接拒绝请求时的应急恢复路径。
| 触发点 | 检查时机 | 自动模式下的默认值 |
|---|---|---|
轮次之间(--compact-threshold) |
每次给 provider 发消息之前 | 上下文窗口的 75% |
批次内部(--compact-batch-threshold) |
每个工具调用批次之后、同一轮里下一次调 provider 之前 | 85%——故意设得更高,不像轮次之间的检查那么急于打断一个工具调用循环 |
两者语义一致:0 = 自动(用上面那个百分比,可以用 --compact-auto-pct 调)、<0 = 关闭、
>0 = 一个具体的 token 数。token 计数优先用 provider 实际报告的用量,而不是 octo 自己的估算——
一旦有真实数字就用真实数字。
不管是自动触发还是手动跑 /compact,永远是这个顺序:
- 回收陈旧的工具结果——不调模型。 最近的 6 个
tool_resultblock 永远原样保留。 更老的、超过 4KB 的会被替换成一个占位符,说明内容被省略了,重新跑一遍工具就能再看到。 如果这一步就已经把估算 token 数拉到阈值以下,压缩到此为止——不调用总结模型,不花钱。 - 总结最老的那些轮次——调一次模型。 只有回收之后还是超阈值才会走到这一步。从最新往最老走, octo 会把完整的用户轮次不断加进“保留”的尾巴里,直到再加下去就会超过一个 keep 预算(窗口的 30%,同时还有个上限,不超过阈值本身的一半)。比这更老的内容会被折叠成一份摘要。 拆分点永远不会落在一个轮次中间,所以一次工具调用和它的结果不会被拆散。
不管会话涨到多大,最近的小块工具输出永远不会被动——只有“旧 + 大”这个组合才会被回收。
如果要折叠的那部分占历史总量不到 15%,自动路径会直接跳过这次总结,而不是为了折叠一小撮内容 去花一次模型调用——这种情况通常说明真正的体量卡在保留的尾巴里(一个带了一大堆工具调用的巨型轮次), 而不是老历史里,如果不加这个防抖,接下来几乎每一轮都会重新触发一次收效甚微的压缩。
这个防抖只在自动路径生效:手动跑 /compact 永远会尝试折叠——既然你主动要求了,哪怕收益很小也是收益。
从“上下文太长”错误里恢复
Section titled “从“上下文太长”错误里恢复”如果一次发送因为 provider 认为请求太长而失败,octo 会在同一轮里重试,按需要逐级升级:
- 先回收,再原样重试——如果错误信息里带了具体差多少(很多 provider 会给),并且光靠回收 就腾出了足够的空间,直接重试,不调用总结模型。
- 弹出最新一条消息,总结剩下的,重试——只丢最新那一条(尽量保住 provider 的 prompt cache),压缩剩下的部分,把弹出的那条重新接回去。
- 弹出大约一半的历史,总结,重试——只有第 2 步还是不够时才会走到这一步;牺牲 cache, 换取这次重试一定能成功。
这条路径是被动触发、每轮最多一次——和上面的主动触发不同,它只在真正发送失败之后才会启动, 并且它瞄准的是错误信息里解析出来的确切差额,而不是笼统的 30% keep 预算。
被折叠的历史去了哪
Section titled “被折叠的历史去了哪”不管是哪个触发点,还是手动 /compact,只要发生了一次真正的总结调用,并且是在一个会持久化的会话里
(CLI 或 Web UI,headless 单发和 IM 不会)——被折叠的原始消息会先被存成一份可读的 Markdown 记录,
然后才被摘要替换掉。留在历史里的那份摘要会带一个指向归档路径的提示,之后如果某个细节真的重要,
模型可以用 read 工具把原文读回来。回收(上面第 1 步)和“上下文太长”恢复都不会归档——只有真正
跑了一次总结调用才会。
你会看到什么
Section titled “你会看到什么”/compact 和自动压缩在所有传输方式上推的都是同一套三个事件(开始、进度、完成),所以 TUI、
Web UI、IM 渠道看到的表现完全一致:
- 只做了回收:
✦ reclaimed stale tool output · ~Xk → ~Yk tokens - 真的总结了:
✦ compacted context · folded N message(s) · ~Xk → ~Yk tokens - 没有明显效果:什么都不打印,而不是假装做了什么有用的事。
在 config.yml 里设置 lite_model,总结调用会先跑在这个更便宜
的模型上,只有 lite 调用失败才回退到你的主模型——这是在一个长会话里控制压缩本身开销的主要手段。
下一步:一次回复本身(不是输入)超长导致的中途错误是另一套机制——见 Agent 循环里“从截断的回复里恢复”一节。