Konfiguration, Anpassung, Plattform-Einrichtung und API-Referenzen für Pi.

Komprimierung und Zweigzusammenfassung

LLMs haben begrenzte Kontextfenster. Wenn Gespräche zu lang werden, verwendet Pi die Komprimierung, um ältere Inhalte zusammenzufassen und gleichzeitig aktuelle Arbeiten beizubehalten. Diese Seite behandelt sowohl die automatische Komprimierung als auch branch summarization.

Quelldateien (pi-mono):

Überprüfen Sie für TypeScript-Definitionen in Ihrem Projekt node_modules/@earendil-works/pi-coding-agent/dist/.

Überblick

Pi verfügt über zwei Zusammenfassungsmechanismen:

Mechanismus Auslösen Zweck
Verdichtung Der Kontext überschreitet den Schwellenwert oder /compact Fassen Sie alte Nachrichten zusammen, um den Kontext freizugeben
Zweigzusammenfassung /tree Navigation Behalten Sie den Kontext beim Wechseln von Zweigen bei

Beide verwenden dasselbe strukturierte Zusammenfassungsformat und verfolgen Dateivorgänge kumulativ. Komprimierungs- und Zweigzusammenfassungsanforderungen verwenden neue Routing-Sitzungs-IDs und deaktivieren, sofern vom Anbieter unterstützt, Eingabeaufforderungs-Cache-Schreibvorgänge, da diese einmaligen Eingabeaufforderungen wahrscheinlich nicht wiederverwendet werden.

Verdichtung

Wenn es ausgelöst wird

Die automatische Komprimierung wird ausgelöst, wenn:

contextTokens > contextWindow - reserveTokens

Standardmäßig beträgt reserveTokens 16384 Token (konfigurierbar in ~/.pi/agent/settings.json oder <project-dir>/.pi/settings.json). Dies lässt Raum für die Reaktion des LLM.

Sie können auch manuell mit /compact [instructions] auslösen, wobei optionale Anweisungen die Zusammenfassung fokussieren.

Wie es funktioniert

  1. Schnittpunkt finden: Gehen Sie von der neuesten Nachricht aus rückwärts und sammeln Sie Token-Schätzungen, bis keepRecentTokens (Standard 20.000, konfigurierbar in ~/.pi/agent/settings.json oder <project-dir>/.pi/settings.json) erreicht ist
  2. Nachrichten extrahieren: Sammeln Sie Nachrichten von der zuvor beibehaltenen Grenze (oder dem Sitzungsstart) bis zum Schnittpunkt
  3. Zusammenfassung generieren: Rufen Sie LLM auf, um eine Zusammenfassung im strukturierten Format zu erstellen und die vorherige Zusammenfassung als iterativen Kontext zu übergeben, sofern vorhanden
  4. Eintrag anhängen: Speichern Sie CompactionEntry mit Zusammenfassung und firstKeptEntryId
  5. Neu laden: Sitzung wird neu geladen, wobei Zusammenfassung + Nachrichten ab firstKeptEntryId verwendet werden
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 firstKeptEntryId

Bei wiederholten Komprimierungen beginnt die zusammengefasste Spanne an der beibehaltenen Grenze der vorherigen Komprimierung (firstKeptEntryId), nicht am Komprimierungseintrag selbst, und fällt auf den Eintrag nach der vorherigen Komprimierung zurück, wenn dieser beibehaltene Eintrag nicht im Pfad gefunden werden kann. Dadurch bleiben Nachrichten erhalten, die die frühere Komprimierung überstanden haben, indem sie auch in den nächsten Zusammenfassungsdurchlauf einbezogen werden. Pi berechnet außerdem tokensBefore aus dem neu erstellten Sitzungskontext neu, bevor das neue CompactionEntry geschrieben wird, sodass die Tokenanzahl den tatsächlichen Kontext vor der Komprimierung widerspiegelt, der ersetzt wird.

Geteilte Kurven

Ein „Turn“ beginnt mit einer Benutzernachricht und umfasst alle Assistentenantworten und Werkzeugaufrufe bis zur nächsten Benutzernachricht. Normalerweise erfolgt der Verdichtungsschnitt an den Kurvengrenzen.

Wenn eine einzelne Umdrehung keepRecentTokens überschreitet, landet der Schnittpunkt mitten in der Umdrehung bei einer Hilfsmeldung. Dies ist ein „Split Turn“:

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]

Für geteilte Runden generiert Pi zwei Zusammenfassungen und führt sie zusammen:

  1. Zusammenfassung des Verlaufs: Vorheriger Kontext (falls vorhanden)
  2. Zusammenfassung der Rundenpräfixe: Der frühe Teil der geteilten Runde

Schnittpunktregeln

Gültige Schnittpunkte sind:

  • Benutzernachrichten
  • Assistentennachrichten
  • BashExecution-Nachrichten
  • Benutzerdefinierte Nachrichten (custom_message, branch_summary)

Schneiden Sie niemals nach Werkzeugergebnissen (sie müssen bei ihrem Werkzeugaufruf bleiben).

CompactionEntry-Struktur

Definiert in 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 kann alle JSON-serialisierbaren Daten in details speichern. Die Standardkomprimierung verfolgt Dateivorgänge, aber benutzerdefinierte Erweiterungsimplementierungen können ihre eigene Struktur verwenden. Generierte und von der Erweiterung bereitgestellte Zusammenfassungen speichern ihren LLM usage, sofern verfügbar, sodass die Sitzungssummen die Zusammenfassungsarbeit umfassen.

Siehe prepareCompaction() und compact() für die Implementierung. Für eine direkte programmatische Zusammenfassung gibt generateSummary() den Zusammenfassungstext und generateSummaryWithUsage() { text, usage } zurück.

Zweigzusammenfassung

Wenn es ausgelöst wird

Wenn Sie /tree verwenden, um zu einem anderen Zweig zu navigieren, bietet Pi an, die Arbeit, die Sie verlassen, zusammenzufassen. Dadurch wird Kontext vom linken Zweig in den neuen Zweig eingefügt.

Wie es funktioniert

  1. Gemeinsamen Vorfahren finden: Tiefster Knoten, den alte und neue Positionen gemeinsam haben
  2. Einträge sammeln: Gehen Sie vom alten Blatt zurück zum gemeinsamen Vorfahren
  3. Mit Budget vorbereiten: Nachrichten bis zum Token-Budget einbeziehen (neueste zuerst)
  4. Zusammenfassung erstellen: LLM mit strukturiertem Format aufrufen
  5. Eintrag anhängen: BranchSummaryEntry am Navigationspunkt speichern
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)

Kumulative Dateiverfolgung

Sowohl Komprimierung als auch branch summarization verfolgen Dateien kumulativ. Beim Generieren einer Zusammenfassung extrahiert pi Dateioperationen aus:

  • Toolaufrufe in den Nachrichten werden zusammengefasst
  • Vorherige Komprimierung oder Zweigzusammenfassung details (falls vorhanden)

Dies bedeutet, dass die Dateiverfolgung über mehrere Komprimierungen oder verschachtelte Zweigzusammenfassungen hinweg akkumuliert wird und der vollständige Verlauf der gelesenen und geänderten Dateien erhalten bleibt.

BranchSummaryEntry-Struktur

Definiert in 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[];
}

Genau wie bei der Komprimierung können Erweiterungen benutzerdefinierte Daten in details speichern.

Siehe collectEntriesForBranchSummary(), prepareBranchEntries() und generateBranchSummary() für die Implementierung.

Zusammenfassungsformat

Sowohl die Komprimierung als auch branch summarization verwenden dasselbe strukturierte Format:

## 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>

Nachrichtenserialisierung

Vor der Zusammenfassung werden Nachrichten über serializeConversation() in Text serialisiert:

[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 tool

Dadurch wird verhindert, dass das Modell das Gespräch als Fortsetzung betrachtet.

Tool-Ergebnisse werden während der Serialisierung auf 2000 Zeichen gekürzt. Inhalte, die über diese Grenze hinausgehen, werden durch eine Markierung ersetzt, die angibt, wie viele Zeichen abgeschnitten wurden. Dadurch bleiben Zusammenfassungsanfragen innerhalb angemessener Token-Budgets, da Tool-Ergebnisse (insbesondere von read und bash) normalerweise den größten Beitrag zur Kontextgröße leisten.

Benutzerdefinierte Zusammenfassung über Extensions

Extensions kann sowohl die Komprimierung als auch branch summarization abfangen und anpassen. Siehe extensions/types.ts für Ereignistypdefinitionen.

session_before_compact

Gefeuert vor der automatischen Komprimierung oder /compact. Kann abbrechen oder eine benutzerdefinierte Zusammenfassung bereitstellen. Siehe SessionBeforeCompactEvent und CompactionPreparation in der Typendatei.

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 */ },
    }
  };
});

Konvertieren von Nachrichten in Text

Um eine Zusammenfassung mit Ihrem eigenen Modell zu erstellen, konvertieren Sie Nachrichten mit serializeConversation in Text:

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,
    }
  };
});

Unter custom-compaction.ts finden Sie ein vollständiges Beispiel mit einem anderen Modell.

session_before_tree

Vor /tree Navigation abgefeuert. Wird immer ausgelöst, unabhängig davon, ob der Benutzer die Zusammenfassung ausgewählt hat. Kann die Navigation abbrechen oder eine benutzerdefinierte Zusammenfassung bereitstellen.

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 */ },
      }
    };
  }
});

Siehe SessionBeforeTreeEvent und TreePreparation in der Typendatei.

Einstellungen

Komprimierung in ~/.pi/agent/settings.json oder <project-dir>/.pi/settings.json konfigurieren:

{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}
Einstellung Standard Beschreibung
enabled true Aktivieren Sie die automatische Komprimierung
reserveTokens 16384 Für die LLM-Antwort zu reservierende Token
keepRecentTokens 20000 Kürzlich zu behaltende Token (nicht zusammengefasst)

Deaktivieren Sie die automatische Komprimierung mit "enabled": false. Sie können weiterhin manuell mit /compact komprimieren.