Sessiecompactering met de Prompt API

Gepubliceerd: 23 juni 2026

Elke LanguageModel sessie heeft een beperkt contextvenster. Naarmate een gesprek groeit, verzamelt het model de volledige berichtgeschiedenis in de context: elke gebruikersvraag en elk antwoord van de assistent. Wanneer het venster vol raakt, treedt de automatische overloopafhandeling van de browser in werking. Deze verwijdert de oudste berichtparen, één vraag- en antwoordpaar tegelijk, om ruimte vrij te maken voor de nieuwe vraag. Als de binnenkomende vraag zo groot is dat het verwijderen van de volledige gespreksgeschiedenis er niet in past, mislukt de aanroep direct met een QuotaExceededError .

Sessiecompactering is een proactief alternatief: vat de gespreksgeschiedenis samen met de Summarizer API en start vervolgens een nieuwe sessie met die samenvattingen als initialPrompts . De browser verwijdert initialPrompts nooit tijdens de runtime-overflow-afhandeling, waardoor de gecomprimeerde samenvatting permanent in de context van het model verankerd blijft, zolang de samenvattingen zelf binnen het contextvenster passen wanneer create() wordt aangeroepen. De nieuwe sessie bevat dezelfde gespreksdraad tegen een fractie van de oorspronkelijke tokenkosten.

Sessiecompactering biedt een manier voor langlopende LanguageModel gesprekken om binnen het contextvenster te blijven zonder de continuïteit te verliezen. De belangrijkste stappen zijn:

  1. Monitor contextUsage ten opzichte van contextWindow en toon dit aan de gebruiker.
  2. Luister naar de contextoverflow -gebeurtenis als een vroege waarschuwing.
  3. Detecteer de taal van elk bericht met de Language Detector API en vat het vervolgens samen met een taalbewuste Summarizer API-instantie.
  4. Vernietig de oude sessie en start een nieuwe met initialPrompts .
  5. Bewaar een fullHistory kopie van de geschiedenis voor foutcorrectie.

Volg het contextgebruik

De Prompt API biedt twee attributen om te controleren hoe volledig de context van een sessie is:

  • session.contextUsage : het aantal tokens dat momenteel wordt gebruikt.
  • session.contextWindow : de totale tokencapaciteit van de sessie.

Geef dit weer in een <progress> -element, zodat gebruikers in één oogopslag kunnen zien hoe dicht de sessie bij de limiet is. Stel value en max direct in op het aantal tokens; de browser schaalt de balk automatisch:

<progress id="token-bar" value="0" max="1"></progress>
<label for="token-bar" id="token-label">Context: — / — tokens</label>
function updateTokenDisplay(session) {
  const usage = session.contextUsage;
  const total = session.contextWindow;

  tokenBar.value = usage;
  tokenBar.max = total;
  tokenLabel.textContent =
    `${Math.round(usage)} / ${Math.round(total)} tokens ` +
    `(${Math.round((usage / total) * 100)}%)`;
}

Roep updateTokenDisplay() aan na elk antwoord op een prompt, zodat de balk actueel blijft.

Luister naar contextoverloop

Wanneer een nieuwe prompt de resterende context overschrijdt, start het automatische herstelproces van de browser: de browser verwijdert één voor één de oudste prompt- en antwoordparen totdat er voldoende ruimte vrij is. De contextoverflow -gebeurtenis wordt geactiveerd op het moment dat deze verwijdering begint. Registreer direct na het aanmaken van de sessie een handler:

session.addEventListener('contextoverflow', () => {
  showWarning('⚠ Context window nearly full. Consider compacting the session.');
});

Dit uitzettingsgedrag heeft twee belangrijke eigenschappen:

  • initialPrompts worden niet verwijderd tijdens de uitvoering. De browser verwijdert ze niet om ruimte te maken voor een volgende prompt. Als de gecombineerde grootte van de initialPrompts die aan LanguageModel.create() worden doorgegeven echter te groot is om in het contextvenster te passen, zal create() een QuotaExceededError retourneren. Zorg er daarom voor dat de compactie klein genoeg is om het gesprek voort te zetten.
  • Het verwijderen van een prompt heeft een limiet. Als de binnenkomende prompt zo groot is dat het verwijderen van het volledige voorgaande gesprek er nog steeds niet in past, mislukt de aanroep van prompt() of promptStreaming() met een QuotaExceededError en wordt er niets verwijderd.

Lees meer over het afhandelen van contextoverloop in de Prompt API-documentatie.

Gebruik de contextoverflow -gebeurtenis om de gebruiker te waarschuwen, de verzendknop uit te schakelen of automatisch compactie te activeren voordat de browser stilletjes de gespreksgeschiedenis verwijdert.

Verkort de sessie

Verdichting bestaat uit drie stappen:

  1. Vat elk bericht in de gespreksgeschiedenis samen met de Summarizer API.
  2. Verwijder de oude sessie.
  3. Maak een nieuwe sessie aan met de samenvattingen als initialPrompts .

Vat de geschiedenis samen.

De Summarizer API is uitermate geschikt voor het comprimeren van individuele chatberichten. Detecteer voor elk bericht eerst de taal met de Language Detector API, zodat de samenvatter correct kan worden geconfigureerd:

async function detectLanguage(text, threshold = 0.7) {
  const detector = await LanguageDetector.create();
  const results = await detector.detect(text);
  if (results.length > 0 && results[0].confidence >= threshold) {
    return results[0].detectedLanguage;
  }
  return null; // confidence too low — caller falls back to navigator.language
}

De betrouwbaarheidsdrempel 0.7 voorkomt dat er actie wordt ondernomen op basis van onzekere detecties. Als de betrouwbaarheid lager is dan de drempel, wordt teruggevallen op navigator.language .

Maak vervolgens een samenvatter aan die is geconfigureerd voor de gedetecteerde taal. Geef preference: 'speed' op om de kleinere variant met lagere latentie te selecteren, en val terug op preference: 'auto' als het snellere model de gedetecteerde taal niet ondersteunt.

const summarizers = {}; // cache, keyed by `${format}:${lang}`

async function getSummarizer(format, lang) {
  const key = `${format}:${lang}`;
  if (summarizers[key]) return summarizers[key];

  const baseOptions = {
    type: 'tldr',
    format, // 'markdown' or 'plain-text'
    length: 'short',
    expectedInputLanguages: [lang],
    expectedContextLanguages: [lang],
    outputLanguage: lang,
  };

  let options = { ...baseOptions, preference: 'speed' };
  let avail = await Summarizer.availability(options);

  if (avail === 'unavailable') {
    options = { ...baseOptions, preference: 'auto' };
    avail = await Summarizer.availability(options);
  }

  if (avail === 'unavailable') {
    throw new Error('Summarizer API unavailable on this device.');
  }

  summarizers[key] = await Summarizer.create(options);
  return summarizers[key];
}

Door samenvatters per format + lang in de cache op te slaan, worden overbodige create() aanroepen voorkomen wanneer opeenvolgende berichten dezelfde taal delen.

Het format is afgeleid van de inhoud van het bericht zelf. Het specificeren van 'markdown' voor gewone tekst kan ongewenste opmaak introduceren, terwijl het specificeren van 'plain-text' voor Markdown codeblokken en accentuering verwijdert. Een kleine reguliere expressie maakt het verschil tussen de twee:

function looksLikeMarkdown(text) {
  return /(?:^#{1,6} |^[-*+] |\d+\. |\*\*|__|\[.+?\]\(|^> |^```)/m.test(text);
}

Nadat de taal en opmaak zijn vastgesteld, vat je elk bericht samen en geef je een context door, zodat het model begrijpt dat het een chatgesprek comprimeert en niet een losstaand document:

const compacted = [];

for (const msg of history) {
  const lang = (await detectLanguage(msg.content)) ?? navigator.language;
  const format = looksLikeMarkdown(msg.content) ? 'markdown' : 'plain-text';
  const summarizer = await getSummarizer(format, lang);

  const summary = await summarizer.summarize(msg.content.trim(), {
    context:
      `This is a ${msg.role} turn from a chat conversation. ` +
      `Preserve its key meaning as concisely as possible.`,
  });

  // Only use the summary if it's actually shorter.
  compacted.push({
    role: msg.role,
    content:
      summary.trim().length < msg.content.length ? summary.trim() : msg.content,
  });
}

Vernietig de oude sessie

Geef de resources van de oude sessie vrij voordat u de vervangende sessie aanmaakt:

session.destroy();
session = null;

Maak een nieuwe sessie aan met een gecomprimeerde geschiedenis.

Geef de gecomprimeerde berichten door als initialPrompts om de nieuwe sessie te vullen met de gesprekscontext:

// Collect every language the detector was confident about.
const sessionLangs =
  confidentLangs.size > 0 ? [...confidentLangs] : [navigator.language];

session = await LanguageModel.create({
  expectedInputs: [{ type: 'text', languages: sessionLangs }],
  expectedOutputs: [{ type: 'text', languages: sessionLangs }],
  initialPrompts: compacted,
});

// Re-register the overflow handler on the new session.
session.addEventListener('contextoverflow', () => {
  /* ... */
});

De nieuwe sessie begint met een lagere contextUsage . Het gesprek gaat verder waar het was gebleven: het model heeft de samenvattingen als voorafgaande context, waardoor het vervolgvragen over eerdere onderwerpen kan beantwoorden.

Fouten afhandelen

Als het samenvatten of aanmaken van een sessie mislukt nadat de oude sessie al is beëindigd, verliest de gebruiker de mogelijkheid om te chatten. Houd een aparte fullHistory array bij die nooit wordt overschreven door compactie en gebruik deze als terugvaloptie voor herstel:

const history = []; // current session's view, replaced on each compaction
const fullHistory = []; // every original message, never overwritten

// In the catch block:
if (!session) {
  session = await LanguageModel.create({
    initialPrompts: fullHistory.map(({ role, content }) => ({ role, content })),
  });
  session.addEventListener('contextoverflow', () => {
    /* ... */
  });
}

Het herstellen van fullHistory kan de context weer bijna vol maken, maar de gebruiker bevindt zich in ieder geval weer in een werkende staat en kan direct een nieuwe compactiepoging ondernemen.

Voorkom optioneel dat bepaalde inhoud wordt gecomprimeerd.

Als er cruciale onderdelen van een bericht zijn die altijd in de context moeten blijven, bijvoorbeeld codefragmenten, verwerk deze dan apart. Het volgende voorbeeld splitst een bericht op in afwisselende tekst- en codefragmenten, en vat vervolgens alleen de tekstgedeelten samen, terwijl de codefragmenten intact blijven:

// Splits text into alternating prose and code-fence segments.
// Returns [{ type: 'prose'|'code', content: string }, …]
function splitByCodeFences(text) {
  const parts = [];
  const re = /^```[^\n]*\n[\s\S]*?^```[ \t]*$/gm;
  let lastIndex = 0;
  let match;
  while ((match = re.exec(text)) !== null) {
    if (match.index > lastIndex) {
      parts.push({
        type: 'prose',
        content: text.slice(lastIndex, match.index),
      });
    }
    parts.push({ type: 'code', content: match[0] });
    lastIndex = match.index + match[0].length;
  }
  if (lastIndex < text.length) {
    parts.push({ type: 'prose', content: text.slice(lastIndex) });
  }
  return parts;
}

Probeer de demo

Met de demo voor sessiecomprimering kunt u chatten met de Prompt API en de sessie op elk gewenst moment comprimeren. De tokenbalk toont het realtime contextgebruik en verandert van kleur naarmate de context zich vult. Na elke comprimatie wordt een logboekvermelding gemaakt van het aantal tokens voor en na de compressie, zodat u de afname direct kunt zien.

Je kunt de volledige en gecomprimeerde conversatie-JSON bekijken in het uitklapbare gedeelte 'Debug: conversation JSON' onderaan de pagina.

De broncode is te vinden op GitHub .