第 05 模块 · 2 节

Compaction 优化上下文

《Pi 生产级工程》05 安全与生产交付 · 本节时长 30 分钟

上下文窗口是生产级的第一成本

大语言模型有上下文窗口上限。会话一长,旧内容会占满窗口、成本飙升。Compaction(压缩)就是 pi 用来「总结旧内容、保留近期工作」的机制。对跑长任务的 md-tools 来说,这一课直接关系到你能跑多久、烧多少钱

贯穿项目md-tools 处理长文档、跑长会话时,上下文窗口会被 markdown 内容迅速撑满。**这一节学会 compaction 的设置和触发逻辑,你就能为 md-tools 配置合理的 reserveTokens / keepRecentTokens,让长任务自动压缩、不会爆上下文。** 这是生产级「持续跑」的能力保证。

两种总结机制

pi 有两种总结机制,注意别搞混:

机制 触发 目的
Compaction 上下文超阈值,或 /compact 总结旧消息、腾出上下文
分支总结 /tree 导航 切换分支时保留上下文

两者都用同一套结构化总结格式,并累积跟踪文件操作


Compaction 什么时候触发

自动压缩的触发条件是:

contextTokens > contextWindow - reserveTokens

默认 reserveTokens16384 tokens(可在 settings 里改)——给 LLM 的响应留位置。你也可以手动触发:

/compact [instructions]

可选的 instructions 能让总结更有针对性。


它是怎么工作的

流程可以概括为「找切点 → 摘消息 → 生成总结 → 追加条目 → 重建上下文」:

  1. 找切点:从最新消息往回走,累加 token 估算,直到 keepRecentTokens(默认 20k)被满足
  2. 摘消息:收集上一个保留边界(或会话开始)到切点之间的消息
  3. 生成总结:用结构化格式调 LLM 总结,有前一个总结就作为迭代上下文传入
  4. 追加条目:保存 CompactionEntry(含 summary 和 firstKeptEntryId)
  5. 重建上下文:下一次请求用「总结 + 从 firstKeptEntryId 往后的消息」重建

被压缩的内容不会发给 LLM,LLM 只看到总结 + 保留的近期消息。


切点规则

合法的切点是:用户消息、assistant 消息、BashExecution 消息、自定义消息

绝对不能切在工具结果上——工具结果必须跟它的工具调用待在一起。如果单个回合太大超过 keepRecentTokens,会形成「切分回合」(split turn),pi 会生成两份总结再合并。


设置:三个旋钮

~/.pi/agent/settings.json/.pi/settings.json 配置:

{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}
设置 默认 作用
enabled true 是否启用自动压缩
reserveTokens 16384 给 LLM 响应预留的 token
keepRecentTokens 20000 保留(不总结)的近期 token

enabled: false 会关掉自动压缩,但你仍能手动 /compact


结构化总结格式

压缩和分支总结用同一套格式,让总结可读、可续接:

## Goal
[用户想完成什么]

## Constraints & Preferences
- [用户提到的要求]

## Progress
### Done
- [x] [已完成任务]
### In Progress
- [ ] [进行中的工作]

## Key Decisions
- **[决策]**: [理由]

## Next Steps
1. [接下来该做什么]

## Critical Context
- [要继续需要的关键数据]

文件清单会被单独跟踪,跨多次压缩累积保留 read / modified 文件的完整历史。


落地练习:调一次 compaction 并观察触发

让 compaction 在你的会话里实际跑起来:

  1. 在 settings 里把 reserveTokenskeepRecentTokens 调小(比如 keepRecentTokens: 8000
  2. 起一个会话,发足够长的上下文,或直接 /compact "重点保留 md-tools 的脚本改动"
  3. 观察会话重建后:旧内容被总结成一块,近期消息仍在

怎么判断做对了?——能看到 compaction 发生(或手动 /compact 成功返回总结);重建后 LLM 仍能回答「之前做了什么」,说明总结没丢关键信息。

卡住了怎么办? 不触发?阈值还没到,把 keepRecentTokens 调小更容易触发。想确认有没有压缩?RPC / JSON 模式里能看 compaction_start / compaction_end 事件。


常见坑:关掉 auto-compaction,然后长会话直接爆

有人为了「省钱」或「怕总结丢内容」,把 enabled 设成 false 关掉自动压缩——然后长会话一路涨到超上下文,请求直接失败

关掉自动压缩 ≠ 没有压缩。正确姿势是:enabled 保持 true,通过调 reserveTokens / keepRecentTokens 来控制触发点;真要关,也要知道手动 /compact 还能救。别为了省小钱,让生产任务在关键时刻爆掉。


小结

  1. compaction 总结旧内容、保留近期工作,缓解上下文成本
  2. 触发条件:contextTokens > contextWindow - reserveTokens
  3. 默认 reserveTokens: 16384keepRecentTokens: 20000
  4. 切点规则:不切在工具结果上
  5. enabled: false 只关自动,手动 /compact 仍可用

下一节,模块 05 的收口:贯穿项目 md-tools 打包含发布。