Skip to content
intermediate Phase 11 · Memory and Conversation State

Summarizing Long Conversations

Roll old turns into running summaries so long sessions stay within token limits.

50m
0 problems
Topic Progress 0%

The Threshold That Fires Compression

Compression is the moment a chat system agrees to forget something. Before the threshold, every turn is kept verbatim and the model sees exactly what was said. After it, the head of the conversation is gone and one paragraph stands in for it. The question is not whether to summarize — the window forces that — it is what you have promised to lose, and when.

Two parameters hold the policy:

THRESHOLD = 6000   # fire only when history exceeds this many tokens
KEEP      = 1600   # raw dialogue kept verbatim, newest first

def compress(history, llm):
    total = sum(t.tokens for t in history)
    if total <= THRESHOLD:
        return history                      # under budget: no call, no cost, no loss

    system, turns = history[0], history[1:]
    head, tail = split_by_tokens(turns, total - system.tokens - KEEP)
    note = Turn("system", "Conversation so far:\n" + llm.summarize(head))
    return [system, note] + tail            # head is replaced, not appended beside

Input: 18 turns totalling 7,420 tokens (96 of them the system prompt); the newest 1,600 body tokens are the tail, 6 turns.

Output: 8 entries — system (96), summary note (254), 6 recent turns (1,600) — 1,950 tokens instead of 7,420.

Three lines carry three decisions. The total <= THRESHOLD guard makes compression on-demand rather than per-turn: a summarization call costs money, adds a round trip of latency, and destroys detail, so it should fire roughly once per session instead of once per message. split_by_tokens(..., total - system.tokens - KEEP) defines the sacrifice as a token count, not a turn count — a user who pastes a 900-token document pulls the compression point forward, and a "drop the first 5 turns" rule would not notice. [system, note] + tail is a replacement: appending the note while keeping the head grows history faster than before it.

Compress after each write, before the next request assembles. What gets sent from there is the trimming decision in Managing Conversation History, and the arithmetic that fixes THRESHOLD and KEEP lives in Context Window Budgeting.

What the Summary Must Keep Verbatim

A summary is lossy compression, and lossy in the wrong places is worse than none at all. "They discussed deployment plans" is a valid summary that also deletes the one fact the next turn needs. The contract has to separate what may be paraphrased from what must be copied character for character:

SUMMARY_PROMPT = """Summarize this conversation for use in later turns.

Copy VERBATIM — do not paraphrase, round, or reword:
  - decisions the user made ("ship without the export button")
  - open questions still unresolved
  - preferences and constraints (timezones, formats, limits, names)

Compress: greetings, restatements, attempts that failed.

At most 120 words. No preamble, no "The user..." prefix.

Conversation:
{transcript}
"""

Good output:

Decided: ship without the export button (turn 6). Open: whether billing
migrates before Friday. Prefers UTC and 24h time; no emoji in customer
email. Name: Priya Raman.

Bad output:

The user discussed a launch, raised concerns about billing, and shared
some preferences about how they like things handled.

The bad one passes a human glance and fails at turn 20, when the model needs 10:15 UTC and receives a mood. Three instructions make the difference: the verb copy rather than include, concrete categories with an example each, and the 120-word ceiling. Drop the ceiling and the verbatim list expands until the summary costs nearly as much as the turns it replaced.

Placement matters as much as wording. The note goes in as a system turn ahead of the surviving tail — [note] + recent_turns — not as a user message. A system entry is standing context: the model treats it as background it may rely on. Spoken as a user message it reads as a fresh claim to verify, and models will occasionally argue with it or attribute it to the wrong speaker. That is the same lever as persona and format rules in System Prompt Design.

Versioned Summaries and Rollback

Summarization is the one step in the pipeline that destroys data, which is exactly why the raw log must not participate in the destruction. Keep turns append-only and treat every summary as a derived view over them:

CREATE TABLE summaries (
  user_id         text   NOT NULL,
  conversation_id text   NOT NULL,
  version         int    NOT NULL,   -- 1, 2, 3 ... one active per conversation
  covers_from     int    NOT NULL,   -- seq of the first turn compressed
  covers_to       int    NOT NULL,
  text            text   NOT NULL,
  prompt_rev      text   NOT NULL,   -- which SUMMARY_PROMPT produced it
  created_at      timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (user_id, conversation_id, version)
);

Rollback is then a pointer move rather than a restore: mark version N inactive and rebuild the send path from version N-1, or from raw turns if none exists. Compression decides what is sent, never what is stored — the distinction is what makes an irreversible step reversible.

Failure modes worth naming:

  • Chained compression — summarizing the previous summary compounds loss until only abstractions survive. Always compress from the raw head, never from the last note.
  • Double compression — two workers cross the threshold concurrently and both write notes, so the model reads the same history twice. Guard the write with a version compare or a Postgres advisory lock.
  • Mid-task compression — firing the threshold inside a tool loop can drop the tool result the next step depends on. Suppress compression between a tool call and its follow-up turn.
  • Prompt revision without re-run — editing SUMMARY_PROMPT changes nothing for sessions already compressed; record prompt_rev and re-compress on demand.
  • Silent rounding — the summarizer turns 9:15 into "morning" and a specific constraint into a vague one. That drift is measurable, and Evaluating Memory Quality is where it gets scored rather than noticed by users.

Summaries are the compact half of the design; the facts worth keeping forever, independent of any single session, are the subject of Semantic Memory with Vectors.

Quiz

1. Why is the compression point measured in tokens rather than turns?

Question 1 options

2. Where should the summary note be injected so the model treats it as background context?

Question 2 options

3. Your service summarized a conversation twice in a row and the second note lost the meeting time entirely. What caused it?

Question 3 options

4. Which summary instruction actually changes what the model preserves?

Question 4 options

Flashcards

Question

When should a rolling summary be triggered?

Answer

When total history tokens exceed a threshold (e.g. 6,000), and only then. Per-turn summarization pays cost and latency for sessions that would never overflow, so the trigger fires roughly once per session.

Question

What must a conversation summary copy verbatim versus compress?

Answer

Copy: decisions made, open questions, preferences and constraints (names, times, formats, limits). Compress: greetings, restatements, failed attempts. The verbatim list is what survives to turn 20.

Question

How is a summary injected into the next request, and why there?

Answer

As a system turn placed before the surviving recent turns. System entries read as established background the model relies on; a user-message summary looks like a fresh claim the model may verify or argue with.

Question

Why version a summary instead of overwriting it?

Answer

Compression is the only irreversible step, so each note is stored with a version, the seq range it covers, and the prompt revision that produced it. Rollback deactivates version N and resumes from N-1 — no restore needed because the turn log stays append-only.

Question

What is the difference between compression at send time and compression at storage time?

Answer

Send-time compression decides what the model sees and keeps the log intact, so rollback and re-summarization are possible. Storage-time deletion destroys the source, making a bad summary permanent. Only send-time compression is reversible.

Revision Notes

Key Takeaways

  • 1. Fire summarization on a token threshold, not per turn, and measure the sacrifice in tokens rather than turns
  • 2. Replace the head with the note — appending beside it grows the history you were shrinking
  • 3. The contract must name what is copied verbatim (decisions, open questions, preferences) and cap the length
  • 4. Inject as a system turn ahead of the tail so the model reads it as background, not as a new claim
  • 5. Keep the turn log immutable and version every summary, because compression is the only irreversible step

Interview Tips

  • • State the two numbers — threshold and keep — and justify both against the model window before writing any code
  • • Show a good and a bad summary side by side and explain which tokens the bad one destroyed
  • • Defend system-role placement: explain what happens when the same note is sent as a user message
  • • Be ready for "what happens when the summary is wrong?" — answer with versioning and rollback from the raw log, not with a better prompt

Cheat Sheet

Rolling Summary Cheat Sheet

Trigger

THRESHOLD, KEEP = 6000, 1600
total = sum(t.tokens for t in history)
if total > THRESHOLD:
    system, turns = history[0], history[1:]
    head, tail = split_by_tokens(turns, total - system.tokens - KEEP)
    note = Turn("system", "Conversation so far:\n" + llm.summarize(head))
    history = [system, note] + tail        # replace head, never append beside it

Prompt contract

Copy verbatim Compress
decisions ("ship without export") greetings, restatements
open questions failed attempts
preferences/constraints: names, times, formats narrative detail around them

Ceiling: ~120 words. Verb: copy, not include. Output with no preamble.

Injection: [system_note, *recent_turns] — system role, ahead of the tail, never a user message.

Versioning

summaries(user_id, conversation_id, version, covers_from, covers_to,
          text, prompt_rev, created_at)   -- PK (user_id, conversation_id, version)

Raw turns stay append-only; the summary is a derived view. Rollback = resume at version N-1.

Failure modes: chained summaries (always compress from raw turns), concurrent double compression (version check / advisory lock), compression mid tool-loop, prompt_rev changed without re-summarizing, silent rounding of numbers.

Interview point: compression decides what is sent, not what is stored — that is what makes it reversible.

Continue Learning

Before You Start

  • No prerequisites listed.

Related Reading