Hoe compactie werkt in Pi

Een LLM-conversatie

Large Language Models (LLMs) hebben beperkte contextvensters (context windows). Het contextvenster bepaalt wat het model kan 'zien' terwijl het een antwoord genereert. De transformer-architectuur die door LLM's wordt gebruikt, beperkt de hoeveelheid input die ze kunnen verwerken.

De input voor een sessie met een coding agent bevat alle voorgaande berichten en tool-aanroepen. Deze hoeveelheid groeit naarmate je verder werkt. Zodra de limiet van het contextvenster wordt overschreden, weigert de LLM het verzoek.

Wanneer je interactief werkt met een coding agent zoals Pi, stuurt de agent verzoeken naar een LLM en ontvangt daar antwoorden op. Elk verzoek bevat:

  • Een systeemprompt;
  • Geladen bestanden (zoals AGENTS.md);
  • Tool-definities;
  • De conversatiegeschiedenis.

Een eerste verzoek van de coding agent ziet er als volgt uit: [systeem][tools][gebruiker]

Dit start een beurt. De LLM kan vervolgens een bericht terugsturen dat tool-aanroepen bevat. Het programma van de agent voert deze uit en stuurt een nieuw verzoek naar de LLM met de volledige conversatie, inclusief de resultaten van de tools.

De beurt is voltooid wanneer de assistent klaar is met het genereren van de output. De status na het eerste verzoek is dan: [systeem][tools][gebruiker][assistent: tool-aanroep][tool-resultaat][assistent]

Elke nieuwe interactie breidt de conversatie uit. Bij een tweede verzoek wordt dit: [systeem][tools][gebruiker][assistent: tool-aanroep][tool-resultaat][assistent][gebruiker] (nieuw gebruikersbericht)

Uiteindelijk overschrijdt de geschiedenis de contextlimiet, waarna het volgende verzoek een foutmelding geeft, zoals: Request exceeds the maximum size. [systeem][tools][gebruiker][assistent][....][tool-resultaat][gebruiker] → overschrijding contextvenster.

Het afhandelen van context-overflow

Wanneer de bestaande conversatie niet langer in zijn huidige vorm kan worden voortgezet, zijn er twee opties:

  1. Een nieuwe, lege conversatie starten: Hierbij wordt alle opgebouwde context weggegooid. Dit verwijdert de geschiedenis, inclusief eerdere beslissingen en onafgemaakt werk. Dit kan soms toch een goede keuze zijn, omdat de prestaties van LLM-outputs afnemen naarmate de context groter wordt.
  2. Een kleinere representatie van de context maken: Omdat we de conversatie vaak willen voortzetten, is dit waar compactie voor dient.

Compactie

In theorie zijn er veel manieren om compactie te implementeren. Men zou bijvoorbeeld een deterministische functie kunnen schrijven die bepaalde delen van de conversatie behoudt en de rest verwijdert. In de praktijk maken implementaties van compactie echter gebruik van een LLM-verzoek om de conversatiegeschiedenis samen te vatten.

Compactie vervangt een deel van de geschiedenis door een gecomprimeerde weergave, waardoor er weer ruimte ontstaat voor extra berichten en tool-aanroepen: [systeem][tools][resultaat compactie][gebruiker] (nieuw bericht)

De implementatie van Pi

Wanneer conversaties te lang worden, gebruikt Pi compactie om oudere inhoud samen te vatten terwijl recent werk behouden blijft.

Wanneer wordt compactie getriggerd?

  • Automatisch: Compactie wordt geactiveerd wanneer de contextlimiet bijna de totale grootte van het contextvenster bereikt. Pi controleert op auto-compactie nadat een beurt is beëindigd.
  • Handmatig: Het kan handmatig worden getriggerd met het /compact commando.
  • Midden in een beurt: Pi kan ook compacteren tijdens een beurt als er een context-overflow fout optreedt.

Wat wordt behouden?

Tijdens het compacteren behoudt Pi een aantal recente berichten ongewijzigd: [systeem + tools][oudere beurten][recent behouden berichten]

Het aantal behouden berichten varieert, omdat Pi gebruikmaakt van een configureerbaar token-budget. De huidige standaardinstelling is 20.000 tokens, wat ongeveer neerkomt op 5 tot 20 beurten. Alle berichten vóór dit snijpunt worden geëxtraheerd, geserialiseerd en samengevat.

De compactie-prompt van Pi

Het ideale resultaat van een goede samenvatting voor een coding agent is vergelijkbaar met een overdrachtsrapportage tussen twee diensten. De compactie-prompt van Pi is erop gericht om alleen informatie te behouden die relevant is voor het volgende LLM-verzoek.

Pi gebruikt hiervoor een ander type verzoek dan voor reguliere conversaties:

  • De systeemprompt: In plaats van "je bent een expert coding assistant", krijgt de LLM de instructie: "je bent een context-samenvattingsassistent".
  • Het gebruikersbericht: Er wordt gevraagd om "een gestructureerde samenvatting van deze conversietak voor context bij terugkeer op een later moment".

De prompt specificeert vaste secties voor:

  • Doel (goal)
  • Voortgang (progress)
  • Belangrijke beslissingen (key decisions)

Dit is een op zichzelf staand verzoek dat geen gebruikmaakt van de bestaande conversatiegeschiedenis. Dit betekent dat er een ander LLM-model gebruikt kan worden zonder onnodige kosten te maken. Het resultaat wordt als een compactie-entry aan de Pi-sessie toegevoegd, waarna de sessie kan worden voortgezet: [systeem][tools][samenvatting][recente beurten][nieuw gebruikersbericht]

Pi slaat de samenvatting op als platte tekst. Dit houdt de gecomprimeerde context leesbaar en overdraagbaar; zo kunnen we in Pi van model wisselen en toch de samenvatting blijven gebruiken.

Compactie en prompt-caching

LLM-providers maken gebruik van prompt caching om herhaalde verzoeken in dezelfde conversatie goedkoper te maken. In een actieve programmeersessie betaal je minder voor de context die al door het model is gegenereerd.

Caching vereist echter een exacte match van het prefix (het begin van de tekst). Compactie doorbreekt dit prefix, waardoor de cache ongeldig wordt.

Situatie vóór compactie (gecached): [systeem][tools][oudere geschiedenis][recent behouden beurten]gecached prefix

Eerste verzoek na compactie: [systeem][tools][samenvatting][recent behouden beurten][nieuw gebruikersbericht]

Hoewel de 'recent behouden beurten' dezelfde tokens bevatten, volgen ze nu een ander prefix (de samenvatting). De vorige gecachte status kan daarom niet worden hergebruikt. Alles na het eerste gewijzigde token moet opnieuw worden berekend. Nieuwe verzoeken na de compactie zullen echter weer profiteren van prompt-caching.

Experimenteren

Omdat Pi uitbreidbaar en flexibel is, kun je de standaardcompactie vervangen door je eigen methode. Om een ander compactiemechanisme te testen, kun je Pi vragen om een extensie te maken met een aangepaste compactie-prompt.