6 min read教程

M09. 压缩:对话超长之后如何继续工作

理解上下文压缩的触发、合法切点、摘要结构与恢复过程。

M09. 压缩:对话超长之后如何继续工作#

压缩不是把聊天记录“删掉一半”。它是一种上下文重建操作:选择一个合法切点,把较早历史交给模型总结,再把摘要和保留区重新接回当前会话。

1. 什么时候触发#

触发通常有两类:接近模型上下文上限时自动触发,或用户/宿主显式调用 compact。Pi 需要先估算当前上下文 token,再结合模型窗口、输出预算和安全余量判断是否进入压缩。

估算不必精确到最后一个 token,但必须稳定、保守且可观测。压缩成功后仍要留出下一轮模型输出和工具结果的空间。

2. 切点不能随便选#

如果把一个 assistant tool call 和它的 tool result 拆开,重建后的上下文可能包含没有对应调用的结果,或者让模型误以为某个工具已经完成。切点要尊重 turn 和消息配对关系。

当单个 turn 本身就很大时,Pi 需要允许更细的处理,并用类似 turnPrefix 的信息告诉摘要过程:这个 turn 被截断后,保留区从哪里开始、缺失部分是什么。

3. 摘要必须结构化#

自由发挥的摘要很容易漏掉文件路径、已完成动作和未解决问题。对编码 Agent,更有用的摘要至少要保留:

  • 当前任务目标和约束。
  • 已经做过的关键决策。
  • 文件的创建、修改和删除情况。
  • 测试、命令和结果。
  • 尚未完成的工作与风险。

结构化模板不是为了让文章好看,而是为了让下一轮模型能够稳定地把摘要当作工作记忆使用。

4. 文件跟踪是编码 Agent 的特殊要求#

仅有一段“我们修改了代码”的文字不够。摘要还需要累积文件操作,让后续模型知道哪些文件已经存在、哪些改动未验证、哪些路径是用户明确指定的。

这也是为什么 compaction 的结果除了 summary,还可能包含 details、usage 和文件相关信息。上下文压缩的对象不是纯聊天,而是一个带工作区状态的任务轨迹。

5. 物理形态:CompactionEntry#

压缩结果会作为 session entry 保存,而不是覆盖原来的 JSONL 行。典型字段包括摘要、保留区起点、压缩前 token 数和可选 usage。

ts
type CompactionEntry = {
  type: "compaction";
  id: string;
  parentId: string | null;
  summary: string;
  firstKeptEntryId: string;
  tokensBefore: number;
};

这是概念化的类型轮廓,具体字段以当前源码为准。追加 entry 的好处是可追踪、可重建、可审计,失败时也不会破坏旧历史。

6. 压缩之后如何重建上下文#

重建时,系统沿当前 session path 读取 entry:遇到 compaction entry,就使用摘要和保留区;继续追加的新消息仍然以正常 message entry 进入路径。这样,压缩后的上下文既包含历史摘要,也能继续承载最新工作。

7. 压缩失败怎么办#

摘要调用本身也可能超时、限流或返回无效格式。安全策略是保留旧上下文和旧 leaf,不把失败的半成品当成成功压缩;必要时提示用户减少任务范围、切换模型或手动开始新会话。

自动重试要有上限。压缩失败后无限重试,既不能解决窗口问题,还会制造额外成本。

8. 如何评估压缩质量#

不要只看摘要是否“通顺”。可以做恢复测试:压缩前设置一组事实、文件改动和待办,压缩后让 Agent 回答并执行后续动作,检查事实、路径、未完成项是否都还在。

特别测试边界:工具调用正在进行时压缩、刚发生错误时压缩、存在分支时压缩、摘要调用失败时重启。

9. 压缩的正确性条件#

一次压缩只有在三件事同时成立时才算成功:旧 entry 仍可追踪;新摘要能够重建后续工作所需的事实;下一轮上下文低于安全阈值。摘要“读起来通顺”并不是正确性标准。真正的标准是压缩后 Agent 能否继续识别目标、文件状态、已完成动作和未解决风险。

资料

本文依据 Pi Compaction 官方文档SessionManager 源码重新创作。

相关文章