重开会话,还是压缩历史?Pi 是如何解决模型上下文自动压缩问题的

一次长任务不会因为模型“记性好”而持续。它只是不断把过去重新塞进下一次请求;装不下时,Pi 会重写自己的工作记忆。

作者:Earendil Engineering08-17 · 10 分钟⚑ 研究论文
🎧 听播客版0:00 / --:--
0:00 / --:--
研究解读 · 小互解读
一分钟速览
  • Compacting context 是把较早工作改写成更短的状态摘要,为长任务腾出上下文空间。
  • Pi 默认保留最近约 20k tokens,给下一次回答预留 16384 tokens;这些数值是 Pi 的实现,不是行业通则。
  • Compaction 是有损迁移:它保住工作连续性,却会丢细节,并让旧 prompt cache 暂时失效。

如果你让 Pi、Claude Code 或 Codex 连续改代码、查日志、跑测试,任务足够长时,多半见过类似 compacting context 的提示。

它不是在压缩代码,也不是在删除文件。它表示当前会话快要装满模型一次能够读取的信息空间,coding agent 正在把较早的工作记录改写成更短的版本,好让任务继续。

这个有限空间叫 context window,也就是上下文窗口。模型每次回答时,只能参考被放进这个窗口里的内容。长任务不断加入新消息、代码、日志、工具调用和执行结果,窗口便会越来越满。compacting context 出现,是因为 agent 必须在撞上容量上限之前,为后续工作腾出空间。

Earendil Engineering 专门写了一篇《How Compaction Works in Pi》,拆开 Pi 的处理方式。Pi 是一款运行在终端里的 coding agent。下面讲的是它的具体实现;其他 coding agent 也面对同一个通用问题,但触发阈值、保留量与摘要方法未必相同。

模型没记忆,只重放上下文

假设你让 agent 修一个支付故障,第一句只说“查一下 checkout 为什么返回 502”。模型真正收到的却不止这句话:还有 system prompt、工具定义、项目里的规则文件。它调用日志工具后,工具结果也会进入历史;你再补一条线索,下一次请求又会带上此前全部内容。

Agent 不是在某个隐藏脑区里永久记住了工作。它是在每次请求时,把目前的工作现场重新摆到模型面前。

容量不是记忆工作每前进一步,下一次请求就要多带一段历史柱高表示上下文占用的累积趋势,不代表 Pi 的固定测量值。
规则工具结果新消息代码与日志继续追加

每一步都合理,但累积结果只有一个:历史单向增长,任务却可能还没完成。上下文装不下时,agent 不能继续把过去原封不动地带走,它必须决定哪些内容还值得保留。

重开会话,还是压缩历史

一旦现有会话无法原样继续,agent 有两条基本路线。它可以新开会话,立刻拿回全部空间,但目标、已做工作、关键决定和未解决问题也一起离场。它也可以做 compaction:把一部分旧历史换成更短的表示,再沿着当前任务继续。

Pi 选择第二条路。这里的“压缩”并不像压缩包,之后还能无损还原。Pi 通常让模型为旧历史写摘要,空间来自取舍:细节会丢失,措辞会被概括,某些只出现过一次的信息可能逐渐变淡。

所以 compaction 更准确的名字是有损的工作状态迁移。它不追求保留每一句话,而是让下一次模型仍然知道:目标是什么、已经做了什么、哪些决定不能推翻、现在卡在哪里、下一步先做什么。

Pi 如何切分新旧上下文

Pi 不会把整段对话一股脑概括。它先从最新消息往回数,划出一条切线。按当前官方文档和固定源码版本,当 contextTokens > contextWindow - reserveTokens 时会触发自动压缩。默认给即将生成的回答预留 16,384 tokens,并尽量让最近 20,000 tokens 保持原样。

20k 是预算,不是固定消息数。若最近几轮塞了大量日志和代码,能留下的轮数就会更少。原文将它粗略换算为 5–20 轮,但这不是保证。

同一条切线,两种处理旧历史被概括,最近工作原样穿过Compaction 不是把整段上下文一起缩小。
压缩前
切线以前:概括切线以后:保留
压缩后
有损发生在摘要处最近工作仍按原顺序进入新请求,但较早历史只能保留摘要器识别到的目标、决定、进度与关键上下文。

默认给新回答预留 16,384 tokens;最近 20k 是预算而非固定轮数,两项均属于 Pi 的可配置实现。

切线以前的内容被抽取、序列化并总结;切线以后仍按原顺序进入新请求。Pi 通常等一轮结束后再检查自动压缩。如果模型已经在一轮中途返回超限错误,它才可能当场压缩并重试。

用户也可以主动运行 /compact [instructions],在需要时提前压缩,并用附加说明提醒摘要器保留某些信息。这个入口能改变时机和重点,却仍不能把有损摘要变成无限记忆。

摘要是一份工作交接

普通摘要关心“刚才聊了什么”。工作交接关心的是:下一棒能不能直接开工。

Pi 会发起一次独立的模型请求,把角色改成 context summarization assistant,要求它不要继续当前对话,而是输出结构化 checkpoint。当前提示词要求保留六类信息:

  • Goal:用户最终要完成什么。
  • Constraints & Preferences:不能违反的限制与偏好。
  • Progress:已完成、进行中与阻塞项。
  • Key Decisions:关键选择及其理由。
  • Next Steps:接下来按什么顺序推进。
  • Critical Context:路径、函数、报错和其他精确信息。

生成结果会作为纯文本 CompactionEntry 写回 session。新的请求因此变成:

[system] [tools] [summary] [recent turns] [new user]

纯文本的好处很朴素:人可以检查摘要有没有漏掉关键决定,Pi 也能切换模型后继续使用它。被迁移的是可见的工作状态,不是某个模型内部不可检查的记忆。

但这份交接单只能保住摘要器识别到的东西。重要约束如果只在很早以前出现过一次,仍可能在反复压缩中变淡。真正不能丢的项目规则,最好写进 AGENTS.md、任务状态、代码或其他持久记录。

前缀一变,旧缓存就失效

Compaction 腾出了空间,也改动了每次请求最敏感的部分:前缀。

模型供应商的 prompt caching 通常依赖精确前缀匹配。压缩前,下一次请求大多只是沿着旧历史继续追加;压缩后,较早历史被一份新摘要替代。即使最近几轮一个字没变,它们前面的内容已经不同,旧缓存也无法从原位置继续复用。

缓存比较的是完整前缀尾巴完全相同,开头换了,指纹仍然不同Prompt cache 不是只看最近几轮,而是从请求开头检查精确匹配。
压缩前前缀指纹A91C
旧缓存从这里断开
压缩后前缀指纹5F2E
最近消息相同 ≠ 前缀相同摘要替换了更早的内容,因此后面的相同消息也要重新计算。

失效不是永久的:压缩后的结构会成为新前缀,后续请求可以在它上面重新积累缓存。

这不是永久失去缓存。压缩后的结构会成为新的稳定前缀,后续请求又能在它上面继续积累缓存。这也解释了 Pi 为什么尽量在一轮结束后压缩:先让本轮连续请求享受旧前缀的缓存收益,再改写上下文。

压缩后能否直接开工

Pi 的取舍现在完整了:保留全部历史,会撞上上下文上限;压缩历史,会丢失细节并付出一次缓存重算;彻底新开会话,则连工作连续性也放弃。

判断一次 compaction 好不好,可以问四个问题:最近多少工作保持原样?摘要是否保留目标、决定、进度和下一步?结果能否被人检查并在模型之间迁移?触发时机是否在剩余空间、溢出风险和缓存成本之间取得合理平衡?

好的 compaction 不是“记住所有过去”。它是接受遗忘不可避免,然后尽量把真正影响下一步的工作状态交给下一棒。

来源
How Compaction Works in PiEarendil Engineering·2026-08-13·查看主材料
本站说明
Pi 的默认阈值与摘要字段由官方文档及文章引用的固定源码版本核对;这些实现细节不能外推到其他 coding agent。
↑ 正文结束
✓ 本篇全文免费开放

会员可复制、下载全文 Markdown,还能让你的 AI 订阅和提问。

阅读原文 ↗
📮 新解读第一时间进邮箱

读完觉得有用?留个邮箱,每篇新解读发布就送达,随时退订。