Sitzungen mit der Prompt API komprimieren

Veröffentlicht: 23. Juni 2026

Jede LanguageModel-Sitzung hat ein endliches Kontextfenster. Im Laufe einer Unterhaltung sammelt das Modell den vollständigen Nachrichtenverlauf in seinem Kontext: jeden Nutzer-Prompt und jede Assistentenantwort. Wenn das Fenster voll ist, greift die automatische Überlaufbehandlung des Browsers. Dabei werden die ältesten Nachrichtenpaare (jeweils ein Prompt und eine Antwort) entfernt, um Platz für den neuen Prompt zu schaffen. Wenn der eingehende Prompt so groß ist, dass er auch ohne den gesamten Unterhaltungsverlauf nicht passt, schlägt der Aufruf mit einem QuotaExceededError fehl.

Sitzung komprimieren ist eine proaktive Alternative: Fassen Sie den Unterhaltungsverlauf mit der Summarizer API zusammen und starten Sie dann eine neue Sitzung, in der Sie diese Zusammenfassungen als initialPrompts verwenden. Der Browser entfernt initialPrompts während der Verarbeitung von Laufzeitüberläufen nie. Die komprimierte Zusammenfassung bleibt also dauerhaft im Kontext des Modells verankert, solange die Zusammenfassungen selbst in das Kontextfenster passen, wenn create() aufgerufen wird. Die neue Sitzung setzt den Unterhaltungsverlauf fort, aber zu einem Bruchteil der ursprünglichen Token-Kosten.

Durch die Sitzungskompaktierung können langlebige LanguageModel-Unterhaltungen im Kontextfenster bleiben, ohne die Kontinuität zu verlieren. Die wichtigsten Schritte sind:

  1. contextUsage relativ zu contextWindow überwachen und dem Nutzer präsentieren.
  2. Achten Sie auf das Ereignis contextoverflow als Frühwarnung.
  3. Erkennen Sie die Sprache jeder Nachricht mit der Language Detector API und fassen Sie sie dann mit einer sprachsensitiven Summarizer API-Instanz zusammen.
  4. Zerstören Sie die alte Sitzung und starten Sie eine neue mit initialPrompts.
  5. Bewahren Sie eine fullHistory-Kopie für die Fehlerbehebung auf.

Kontextnutzung verfolgen

Die Prompt API stellt zwei Attribute zur Verfügung, mit denen Sie überwachen können, wie vollständig der Kontext einer Sitzung ist:

  • session.contextUsage: Die Anzahl der aktuell verbrauchten Tokens.
  • session.contextWindow: Die Gesamtkapazität der Sitzung in Tokens.

Stellen Sie dies in einem <progress>-Element dar, damit Nutzer auf einen Blick sehen, wie nahe die Sitzung am Limit ist. Legen Sie value und max direkt auf die Anzahl der Tokens fest. Der Browser skaliert den Balken 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)}%)`;
}

Rufen Sie updateTokenDisplay() nach jeder Prompt-Antwort auf, damit die Leiste aktuell bleibt.

Auf Kontextüberlauf achten

Wenn ein neuer Prompt den verbleibenden Kontext überschreitet, beginnt die automatische Wiederherstellung des Browsers: Die ältesten Prompt- und Antwortpaare werden einzeln entfernt, bis genügend Speicherplatz frei ist. Das contextoverflow-Ereignis wird ausgelöst, wenn der Ausschluss beginnt. Registrieren Sie einen Handler unmittelbar nach dem Erstellen der Sitzung:

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

Dieses Verhalten beim Entfernen von Seiten hat zwei wichtige Eigenschaften:

  • initialPrompts werden zur Laufzeit nicht entfernt. Der Browser entfernt sie nicht, um Platz für einen eingehenden Prompt zu schaffen. Wenn die kombinierte Größe der an LanguageModel.create() übergebenen initialPrompts jedoch zu groß ist, um in das Kontextfenster zu passen, wird create() mit einem QuotaExceededError abgelehnt. Achten Sie also darauf, dass die Komprimierung klein genug ist, um die Unterhaltung fortzusetzen.
  • Die Bereinigung ist begrenzt. Wenn der eingehende Prompt so groß ist, dass er auch nach dem Entfernen der gesamten vorherigen Unterhaltung nicht passt, schlägt der prompt()- oder promptStreaming()-Aufruf mit einem QuotaExceededError fehl und es wird nichts entfernt.

Weitere Informationen zur Verarbeitung von Kontextüberlauf finden Sie in der Dokumentation zur Prompt API.

Verwenden Sie das contextoverflow-Ereignis, um den Nutzer zu warnen, die Schaltfläche „Senden“ zu deaktivieren oder die Komprimierung automatisch auszulösen, bevor der Browser den Unterhaltungsverlauf im Hintergrund verwirft.

Sitzung komprimieren

Die Verdichtung umfasst drei Schritte:

  1. Fassen Sie jede Nachricht im Unterhaltungsverlauf mit der Summarizer API zusammen.
  2. Zerstören Sie die alte Sitzung.
  3. Erstelle eine neue Sitzung, in der die Zusammenfassungen als initialPrompts verwendet werden.

Fasse den Verlauf zusammen

Die Summarizer API eignet sich hervorragend zum Komprimieren einzelner Chatnachrichten. Ermitteln Sie für jede Nachricht zuerst die Sprache mit der Language Detector API, damit die Zusammenfassung richtig konfiguriert werden kann:

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
}

Der 0.7-Konfidenzschwellenwert verhindert, dass bei unsicheren Erkennungen Maßnahmen ergriffen werden. Wenn die Konfidenz unter dem Schwellenwert liegt, wird auf navigator.language zurückgegriffen.

Erstellen Sie als Nächstes eine Zusammenfassung, die für die erkannte Sprache konfiguriert ist. Bevorzugen Sie preference: 'speed', um die kleinere Modellvariante mit geringerer Latenz auszuwählen, und greifen Sie auf preference: 'auto' zurück, wenn das schnellere Modell die erkannte Sprache nicht unterstützt:

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];
}

Durch das Zwischenspeichern von Zusammenfassungen pro format+lang-Paar werden redundante create()-Aufrufe vermieden, wenn aufeinanderfolgende Nachrichten dieselbe Sprache haben.

Das format-Argument wird aus dem Nachrichteninhalt abgeleitet. Wenn Sie 'markdown' für normalen Text angeben, kann es zu unerwünschter Formatierung kommen. Wenn Sie 'plain-text' für Markdown angeben, werden Codeblöcke und Hervorhebungen entfernt. Ein kleiner regulärer Ausdruck unterscheidet die beiden:

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

Nachdem Sprache und Format festgelegt wurden, fassen Sie jede Nachricht zusammen und übergeben Sie einen context-String, damit das Modell versteht, dass es sich um einen Chatbeitrag und nicht um ein eigenständiges Dokument handelt:

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

Alte Sitzung beenden

Geben Sie die Ressourcen der alten Sitzung frei, bevor Sie die neue erstellen:

session.destroy();
session = null;

Neue Sitzung mit komprimiertem Verlauf erstellen

Übergeben Sie die komprimierten Nachrichten als initialPrompts, um die neue Sitzung mit dem Unterhaltungskontext zu starten:

// 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', () => {
  /* ... */
});

Die neue Sitzung beginnt mit einem niedrigeren contextUsage. Die Unterhaltung wird dort fortgesetzt, wo sie aufgehört hat: Das Modell verwendet die Zusammenfassungen als vorherigen Kontext, sodass es Folgefragen zu früheren Themen beantworten kann.

Fehler verarbeiten

Wenn die Zusammenfassung oder die Sitzungserstellung fehlschlägt, nachdem die alte Sitzung bereits beendet wurde, kann der Nutzer nicht mehr chatten. Ein separates fullHistory-Array beibehalten, das nie durch die Komprimierung überschrieben wird, und es als Fallback für die Wiederherstellung verwenden:

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', () => {
    /* ... */
  });
}

Nach der Wiederherstellung aus fullHistory ist der Kontext möglicherweise wieder fast voll, aber der Nutzer kann zumindest wieder arbeiten und sofort eine weitere Komprimierung versuchen.

Optional verhindern, dass bestimmte Inhalte komprimiert werden

Wenn es kritische Teile einer Nachricht gibt, die immer im Kontext bleiben müssen, z. B. Codebeispiele, verarbeiten Sie diese separat. Im folgenden Beispiel wird eine Nachricht in abwechselnde Prosa- und Code-Fence-Segmente aufgeteilt. Anschließend werden nur die Prosa-Teile zusammengefasst, während die Code-Segmente unverändert bleiben:

// 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;
}

Demo ansehen

In der Demo zur Sitzungskompaktierung können Sie mit der Prompt API chatten und die Sitzung jederzeit komprimieren. In der Tokenleiste wird die Kontextnutzung in Echtzeit angezeigt. Die Farbe ändert sich, wenn der Kontext sich füllt. Nach jeder Verdichtung wird in einem Logeintrag die Anzahl der Tokens vor und nach der Verdichtung aufgezeichnet, sodass Sie die Reduzierung direkt sehen können.

Sie können sich die vollständige und die komprimierte JSON-Datei der Unterhaltung unten auf der Seite im minimierbaren Abschnitt Debug: conversation JSON (Debug: JSON-Datei der Unterhaltung) ansehen.

Der Quellcode ist auf GitHub verfügbar.