Compactación y resumen de ramas
Los LLM tienen ventanas de contexto limitadas. Cuando las conversaciones se alargan demasiado, Pi utiliza la compactación para resumir el contenido anterior y al mismo tiempo preservar el trabajo reciente. Esta página cubre tanto la autocompactación como branch summarization.
Archivos fuente (pi-mono):
packages/coding-agent/src/core/compaction/compaction.ts- Lógica de autocompactaciónpackages/coding-agent/src/core/compaction/branch-summarization.ts- Resumen de sucursalespackages/coding-agent/src/core/compaction/utils.ts- Utilidades compartidas (seguimiento de archivos, serialización)packages/coding-agent/src/core/session-manager.ts- Tipos de entrada (CompactionEntry,BranchSummaryEntry)packages/coding-agent/src/core/extensions/types.ts- Tipos de eventos de extensión
Para las definiciones de TypeScript en su proyecto, inspeccione node_modules/@earendil-works/pi-coding-agent/dist/.
Descripción general
Pi tiene dos mecanismos de resumen:
| Mecanismo | Desencadenar | Objetivo |
|---|---|---|
| Compactación | El contexto supera el umbral, o /compact |
Resumir mensajes antiguos para liberar contexto |
| Resumen de sucursales | /tree navegación |
Preservar el contexto al cambiar de sucursal |
Ambos utilizan el mismo formato de resumen estructurado y realizan un seguimiento de las operaciones de archivos de forma acumulativa. Las solicitudes de compactación y resumen de bifurcación utilizan ID de sesión de enrutamiento nuevos y, cuando el proveedor las admite, deshabilitan las escrituras de caché de avisos porque es poco probable que estos avisos únicos se reutilicen.
Compactación
Cuando se activa
La compactación automática se activa cuando:
contextTokens > contextWindow - reserveTokensDe forma predeterminada, reserveTokens son 16384 tokens (configurables en ~/.pi/agent/settings.json o <project-dir>/.pi/settings.json). Esto deja espacio para la respuesta del LLM.
También puedes activarlo manualmente con /compact [instructions], donde las instrucciones opcionales centran el resumen.
Cómo funciona
- Buscar punto de corte: retroceda desde el mensaje más reciente, acumulando estimaciones de tokens hasta alcanzar
keepRecentTokens(20k predeterminado, configurable en~/.pi/agent/settings.jsono<project-dir>/.pi/settings.json). - Extraer mensajes: recopile mensajes desde el límite mantenido anterior (o inicio de sesión) hasta el punto de corte.
- Generar resumen: Llame a LLM para resumir con formato estructurado, pasando el resumen anterior como contexto iterativo cuando esté presente.
- Agregar entrada: Guarde
CompactionEntrycon resumen yfirstKeptEntryId - Recarga: Se recarga la sesión, usando resumen + mensajes desde
firstKeptEntryIden adelante
Before compaction:
entry: 0 1 2 3 4 5 6 7 8 9
┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┘
└────────┬───────┘ └──────────────┬──────────────┘
messagesToSummarize kept messages
↑
firstKeptEntryId (entry 4)
After compaction (new entry appended):
entry: 0 1 2 3 4 5 6 7 8 9 10
┌─────┬─────┬─────┬─────┬──────┬─────┬──── ─┬──────┬─────┬─────┬─────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool│ cmp │
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴─────┴─────┘
└──────────┬──────┘ └──────────────────────┬───────────────────┘
not sent to LLM sent to LLM
↑
starts from firstKeptEntryId
What the LLM sees:
┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐
│ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │
└────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘
↑ ↑ └─────────────────┬────────────────┘
prompt from cmp messages from firstKeptEntryIdEn compactaciones repetidas, el tramo resumido comienza en el límite mantenido de la compactación anterior (firstKeptEntryId), no en la entrada de compactación en sí, y vuelve a la entrada después de la compactación anterior si esa entrada mantenida no se puede encontrar en el camino. Esto preserva los mensajes que sobrevivieron a la compactación anterior al incluirlos también en la siguiente pasada de resumen. Pi también vuelve a calcular tokensBefore a partir del contexto de sesión reconstruido antes de escribir el nuevo CompactionEntry, por lo que el recuento de tokens refleja el contexto real previo a la compactación que se reemplaza.
Giros divididos
Un "turno" comienza con un mensaje de usuario e incluye todas las respuestas del asistente y llamadas de herramientas hasta el siguiente mensaje de usuario. Normalmente, la compactación corta en los límites de las curvas.
Cuando un solo giro excede keepRecentTokens, el punto de corte llega a mitad del giro en un mensaje del asistente. Este es un "turno dividido":
Split turn (one huge turn exceeds budget):
entry: 0 1 2 3 4 5 6 7 8
┌─────┬─────┬─────┬──────┬─────┬──────┬──────┬─────┬──────┐
│ hdr │ usr │ ass │ tool │ ass │ tool │ tool │ ass │ tool │
└─────┴─────┴─────┴──────┴─────┴──────┴──────┴─────┴──────┘
↑ ↑
turnStartIndex = 1 firstKeptEntryId = 7
│ │
└──── turnPrefixMessages (1-6) ───────┘
└── kept (7-8)
isSplitTurn = true
messagesToSummarize = [] (no complete turns before)
turnPrefixMessages = [usr, ass, tool, ass, tool, tool]Para turnos divididos, Pi genera dos resúmenes y los fusiona:
- Resumen del historial: contexto anterior (si corresponde)
- Resumen del prefijo de turno: la primera parte del turno dividido
Reglas de punto de corte
Los puntos de corte válidos son:
- Mensajes de usuario
- Mensajes del asistente
- Mensajes de ejecución de Bash
- Mensajes personalizados (mensaje_personalizado, resumen_rama)
Nunca corte los resultados de la herramienta (deben permanecer con su llamada de herramienta).
Estructura de entrada de compactación
Definido en session-manager.ts:
interface CompactionEntry<T = unknown> {
type: "compaction";
id: string;
parentId: string;
timestamp: number;
summary: string;
firstKeptEntryId: string;
tokensBefore: number;
usage?: Usage; // LLM usage that generated the summary
fromHook?: boolean; // true if provided by extension (legacy field name)
details?: T; // implementation-specific data
}
// Default compaction uses this for details (from compaction.ts):
interface CompactionDetails {
readFiles: string[];
modifiedFiles: string[];
}Extensions puede almacenar cualquier dato serializable JSON en details. La compactación predeterminada rastrea las operaciones de archivos, pero las implementaciones de extensiones personalizadas pueden usar su propia estructura. Los resúmenes generados y proporcionados por la extensión almacenan su LLM usage cuando esté disponible, de modo que los totales de las sesiones incluyan el trabajo de resumen.
Consulte prepareCompaction() y compact() para conocer la implementación. Para un resumen programático directo, generateSummary() devuelve el texto de resumen y generateSummaryWithUsage() devuelve { text, usage }.
Resumen de sucursales
Cuando se activa
Cuando usas /tree para navegar a una rama diferente, Pi te ofrece resumir el trabajo que estás dejando. Esto inyecta contexto de la rama izquierda a la nueva rama.
Cómo funciona
- Buscar ancestro común: nodo más profundo compartido por posiciones antiguas y nuevas
- Recopila entradas: camina desde la hoja vieja hasta el ancestro común
- Prepárese con el presupuesto: incluya mensajes hasta el presupuesto simbólico (los más recientes primero)
- Generar resumen: Llame a LLM con formato estructurado
- Agregar entrada: Guardar
BranchSummaryEntryen el punto de navegación
Tree before navigation:
┌─ B ─ C ─ D (old leaf, being abandoned)
A ───┤
└─ E ─ F (target)
Common ancestor: A
Entries to summarize: B, C, D
After navigation with summary:
┌─ B ─ C ─ D
A ───┤
└─ E ─ F ─ [summary of B,C,D] (new leaf)Seguimiento de archivos acumulativos
Tanto la compactación como el branch summarization rastrean archivos de forma acumulativa. Al generar un resumen, pi extrae las operaciones de archivos de:
- Llamadas a herramientas en los mensajes que se resumen
- Compactación anterior o resumen de ramas
details(si corresponde)
Esto significa que el seguimiento de archivos se acumula en múltiples compactaciones o resúmenes de ramas anidadas, preservando el historial completo de archivos leídos y modificados.
RamaResumenEstructura de entrada
Definido en session-manager.ts:
interface BranchSummaryEntry<T = unknown> {
type: "branch_summary";
id: string;
parentId: string;
timestamp: number;
summary: string;
fromId: string; // Entry we navigated from
usage?: Usage; // LLM usage that generated the summary
fromHook?: boolean; // true if provided by extension (legacy field name)
details?: T; // implementation-specific data
}
// Default branch summarization uses this for details (from branch-summarization.ts):
interface BranchSummaryDetails {
readFiles: string[];
modifiedFiles: string[];
}Al igual que la compactación, las extensiones pueden almacenar datos personalizados en details.
Consulte collectEntriesForBranchSummary(), prepareBranchEntries() y generateBranchSummary() para conocer la implementación.
Formato de resumen
Tanto la compactación como branch summarization usan el mismo formato estructurado:
## Goal
[What the user is trying to accomplish]
## Constraints & Preferences
- [Requirements mentioned by user]
## Progress
### Done
- [x] [Completed tasks]
### In Progress
- [ ] [Current work]
### Blocked
- [Issues, if any]
## Key Decisions
- **[Decision]**: [Rationale]
## Next Steps
1. [What should happen next]
## Critical Context
- [Data needed to continue]
<read-files>
path/to/file1.ts
path/to/file2.ts
</read-files>
<modified-files>
path/to/changed.ts
</modified-files>Serialización de mensajes
Antes del resumen, los mensajes se serializan en texto mediante serializeConversation():
[User]: What they said
[Assistant thinking]: Internal reasoning
[Assistant]: Response text
[Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
[Tool result]: Output from toolEsto evita que el modelo lo trate como una conversación para continuar.
Los resultados de la herramienta se truncan a 2000 caracteres durante la serialización. El contenido que supera ese límite se reemplaza con un marcador que indica cuántos caracteres se truncaron. Esto mantiene las solicitudes de resumen dentro de presupuestos simbólicos razonables, ya que los resultados de las herramientas (especialmente de read y bash) suelen ser los que más contribuyen al tamaño del contexto.
Resumen personalizado a través de Extensions
Extensions puede interceptar y personalizar tanto la compactación como branch summarization. Consulte extensions/types.ts para conocer las definiciones de tipos de eventos.
sesión_antes_compact
Disparado antes de la autocompactación o /compact. Puede cancelar o proporcionar un resumen personalizado. Consulte SessionBeforeCompactEvent y CompactionPreparation en el archivo de tipos.
pi.on("session_before_compact", async (event, ctx) => {
const { preparation, branchEntries, customInstructions, reason, willRetry, signal } = event;
// preparation.messagesToSummarize - messages to summarize
// preparation.turnPrefixMessages - split turn prefix (if isSplitTurn)
// preparation.previousSummary - previous compaction summary
// preparation.fileOps - extracted file operations
// preparation.tokensBefore - context tokens before compaction
// preparation.firstKeptEntryId - where kept messages start
// preparation.settings - compaction settings
// branchEntries - all entries on current branch (for custom state)
// reason - "manual" (/compact), "threshold", or "overflow"
// willRetry - whether the aborted turn is retried after compaction (overflow recovery)
// signal - AbortSignal (pass to LLM calls)
// Cancel:
return { cancel: true };
// Custom summary:
return {
compaction: {
summary: "Your summary...",
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore,
// usage: summaryResponse.usage, // Optional; included in session totals
details: { /* custom data */ },
}
};
});Convertir mensajes a texto
Para generar un resumen con tu propio modelo, convierte mensajes a texto usando serializeConversation:
import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";
pi.on("session_before_compact", async (event, ctx) => {
const { preparation } = event;
// Convert AgentMessage[] to Message[], then serialize to text
const conversationText = serializeConversation(
convertToLlm(preparation.messagesToSummarize)
);
// Returns:
// [User]: message text
// [Assistant thinking]: thinking content
// [Assistant]: response text
// [Assistant tool calls]: read(path="..."); bash(command="...")
// [Tool result]: output text
// Now send to your model for summarization
const { summary, usage } = await myModel.summarize(conversationText);
return {
compaction: {
summary,
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore,
usage,
}
};
});Consulte custom-compaction.ts para ver un ejemplo completo utilizando un modelo diferente.
sesión_antes_árbol
Disparado antes de la navegación /tree. Siempre se activa independientemente de si el usuario elige resumir. Puede cancelar la navegación o proporcionar un resumen personalizado.
pi.on("session_before_tree", async (event, ctx) => {
const { preparation, signal } = event;
// preparation.targetId - where we're navigating to
// preparation.oldLeafId - current position (being abandoned)
// preparation.commonAncestorId - shared ancestor
// preparation.entriesToSummarize - entries that would be summarized
// preparation.userWantsSummary - whether user chose to summarize
// Cancel navigation entirely:
return { cancel: true };
// Provide custom summary (only used if userWantsSummary is true):
if (preparation.userWantsSummary) {
return {
summary: {
summary: "Your summary...",
// usage: summaryResponse.usage, // Optional; included in session totals
details: { /* custom data */ },
}
};
}
});Consulte SessionBeforeTreeEvent y TreePreparation en el archivo de tipos.
Ajustes
Configure la compactación en ~/.pi/agent/settings.json o <project-dir>/.pi/settings.json:
{
"compaction": {
"enabled": true,
"reserveTokens": 16384,
"keepRecentTokens": 20000
}
}| Configuración | Por defecto | Descripción |
|---|---|---|
enabled |
true |
Habilitar la autocompactación |
reserveTokens |
16384 |
Tokens para reservar para la respuesta LLM |
keepRecentTokens |
20000 |
Tokens recientes para conservar (no resumidos) |
Desactive la compactación automática con "enabled": false. Aún puedes compactar manualmente con /compact.