Pi 中的压缩工作原理
How Compaction Works in Pi

原始链接: https://earendil.com/posts/compaction-in-pi/

像 Pi 这样的编码代理(Coding agents)具有有限的上下文窗口,随着对话和工具调用的积累,窗口会逐渐被填满。一旦达到上限,模型将无法再处理请求。为了解决这个问题,Pi 使用了“压缩”(compaction)机制:通过总结对话历史中较早的部分来释放空间,同时保留关键的上下文信息。 当上下文接近上限时,系统会自动触发压缩,或者用户也可以通过 `/compact` 命令手动触发。Pi 会创建一个独立的 LLM 请求,由一个“总结助手”角色生成一份包含目标、进展和关键决策的结构化简报。这份摘要将取代原始的旧历史记录,确保会话在不丢失过往工作记录的情况下继续进行。 虽然压缩能有效管理内存,但它会暂时中断“提示词缓存”(prompt caching),因为这改变了对话的前缀。不过,一旦摘要集成完毕,模型在后续消息中将继续受益于缓存的提示词。由于 Pi 具有可扩展性,用户甚至可以通过调整总结提示词来自定义压缩过程。总之,压缩就像是一场“换班”简报,使开发者能够在 LLM 固有的内存限制下维持长期的编码会话。

关于“Pi 中压缩机制如何运作”的 Hacker News 讨论,凸显了管理长上下文大模型(LLM)交互时面临的持续挑战。 压缩——即通过总结或修剪对话历史以保持在上下文限制内的过程——是一个充满争议的话题。虽然 Pi 使用单独的大模型调用来生成摘要,但参与者认为这种方法通常效率低下,因为它往往会造成完全的“缓存未命中”,从而导致高延迟和成本增加。 讨论的主要结论包括: * **替代策略:** 用户建议采用“修剪”(删除低价值信息,如嘈杂的工具调用)而非全面总结,以保留意图并提高上下文质量。 * **硬件与延迟:** 压缩速度受硬件影响很大;用户注意到高端 GPU 和统一内存系统之间存在显著的性能差距。 * **优化关注点:** 许多贡献者认为当前方法不够理想,因为它们丢弃了整个键值(KV)缓存。为在模型智能与 Token 效率之间取得平衡,人们提出了多种创新方案,例如“乒乓”缓存(在后台进行总结)或基于自定义扩展的压缩。 总的来说,用户希望能够更精细地控制总结内容,以便在剔除“噪音”的同时保留关键数据。
相关文章

原文

If you have ever had a long coding session in a coding agent like Pi, Claude Code, or Codex, you will have triggered a compaction. In this post we explain how compaction works and when Pi needs to compact.

An LLM conversation

Large language models (LLMs) have limited context windows. The context window is what the model can "see" while producing a response. The transformer architecture used by LLMs limits how much input they can process. The input for a coding agent session includes all the previous messages and tool calls, and this keeps growing as you work. Once it exceeds the context window, the LLM rejects the request.

When working interactively with a coding agent like Pi, the agent sends requests to an LLM and receives responses. Each request includes a system prompt, loaded files such as AGENTS.md, tool definitions, and the conversation history.

A coding agent's first LLM request contains this initial context, along with a first user message.

request 1:
[system][tools][user]

This starts a turn. The LLM may first return an assistant message containing tool calls. The agent program executes them and sends a new request to the LLM containing the complete conversation, now including the tool results. We get back another assistant message. The turn is finished when the assistant has completed generating output.

after request 1:
[system][tools][user][assistant: tool call][tool result][assistant]
                     <------------------->     ^        <--------->
                     returned by LLM           |        returned by LLM
                                               |
                                     produced by the agent

We continue working, and send another message.

request 2:
[system][tools][user][assistant: tool call][tool result][assistant][user]
                                                                     ^
                                                               new user message

Each turn expands the conversation. Eventually, the history exceeds the context limit. The next request then returns an error such as Request exceeds the maximum size.

[system][tools][user][assistant][....][tool result][user]
                                                      ^
                                             exceeds context window

Handling context overflow

When we cannot continue with the existing conversation as-is, we have two choices.

  1. We can start a new, empty conversation without the accumulated context. This discards the history, including prior decisions and unresolved work. It might still be a good idea to do, because the performance of LLM outputs decrease as the context size grows.
  2. We can create a smaller representation of the conversation context, since we want to keep this conversation going. That is what compaction does.

Compaction

In theory, there are many ways to implement compaction. For example, we can write a deterministic function which keeps some of what is in the conversation and discards the rest. In practice, though, implementations of compaction use an LLM request to summarize the conversation history.

Compaction replaces part of the history with a compressed representation, leaving room for additional messages and tool calls.

[system][tools][compaction result][user]
                                    ^
                               new message

Pi's implementation

Let's look more closely at how Pi specifically implements compaction.

When conversations grow too long, Pi uses compaction to summarize older content while preserving recent work. Compaction is triggered when the context limit is nearing the total size of the context window. It can also be manually triggered using the /compact command.

Pi checks for auto-compaction after a turn ends. Until then, each request extends the existing prompt and can reuse its cached prefix. Pi may also compact mid-turn, if it encounters a context overflow error.

When compacting, Pi retains some number of recent messages unchanged.

before compaction:
[system + tools][older turns][recent retained messages]

The number of retained messages varies because Pi uses a configurable token budget. Pi's current default of 20 thousand tokens comes out to roughly 5 to 20 turns. All the messages before this cut point are extracted and serialized, and will be summarized.

Pi's compaction prompt

The ideal outcome of a good summarization for a coding agent is like a handoff briefing from one shift to the next. Pi's compaction prompt focuses on the fact that there is a lot in the existing context that is no longer relevant. We should only keep around what is still important context for the next LLM request.

Pi therefore sends a different request for compaction than for regular conversation.

  1. The system prompt used in the standalone compaction request is different. Instead of telling the LLM "you are an expert coding assistant", we tell the LLM "you are a context summarization assistant."
  2. The user message in the compaction request is also different. It requests "a structured summary of this conversation branch for context when returning later." The prompt specifies sections for goal, progress and key decisions.
  3. It's a standalone request that doesn't use any of the existing conversation history, which means it can use a different LLM model without incurring any unnecessary cost.

The result of the compaction is appended to the Pi session as a compaction entry, and the session can now continue. After the compaction request, the context has been compressed.

after compaction:
[system][tools][summary][recent turns][new user message]

There is now room in the conversation context for many more messages.

Pi stores the compaction summary as plain text in the session. This keeps the compacted context readable and portable, since we can switch models in Pi and continue using the summary.

Compaction and prompt caching

Prompt caching is used by LLM providers to make repeated requests in the same conversation less expensive. In an active coding session, we pay less for the context that has already been generated by the model. This caching requires an exact prefix match, so compacting a session will break the prompt cache.

cached before compaction:
[system][tools][older history][recent retained turns]
<-------------------- cached prefix -------------------->

first request after compaction:
[system][tools][summary][recent retained turns][new user message]
<-- reusable -->^
                |
        first changed token
                |
                +-- everything after this point must be recomputed

The retained turns contain the same tokens, but they now follow a different prefix. Their previous cached state therefore cannot be reused.

New requests after compaction will benefit from prompt caching again.

Experiment

Since Pi is extensible and malleable, you can replace its compaction with your own. To test a different compaction mechanism, ask Pi to create an extension with a custom compaction prompt.

联系我们 contact @ memedata.com