InterviewPrepKit

Home / Coding / Agent Coding / Memory & Context / Summarize Old Turns to Save Context

Summarize Old Turns to Save Context

medium 00:00
Solving tips
  • Compaction has three fixed parts: the system message you always keep, a recent window you never touch, and the old middle you collapse into one summary. Get those boundaries right and the rest is bookkeeping.
  • Only summarize when you are actually over budget and there is a non-empty middle to collapse. Summarizing a conversation that already fits just burns a model call and loses detail.
  • Take both the tokenizer and the summarizer as injected callables so the whole function is deterministic and unit-testable without a real model or a real token count.

A long-running agent cannot keep every turn forever: the context window is finite and every token costs money and latency. The standard fix is compaction. Once the transcript grows past a budget, you collapse the oldest turns into a short summary and keep only the system prompt plus the last few messages verbatim. This exercise is that control logic.

How compaction works

The history is a list of {"role", "content"} messages. You split it into three regions:

  • The system message — if messages[0] has role == "system", it is the durable instruction set and is always kept as the first message.
  • The recent window — the last keep_recent messages. These are the live context the model needs and are kept verbatim.
  • The old middle — everything between the system message and the recent window. When you are over budget, this whole region is replaced by one summary message.

You only compact when the transcript is actually over budget: sum count_tokens(m) across every message and compare against token_budget. If the history already fits, return it untouched. If it is over budget but the middle region is empty (there is nothing to collapse), also return it untouched — you cannot save space that is not there.

Both count_tokens and summarize are injected callables. That keeps the function deterministic: the same inputs always produce the same compacted list, with no real tokenizer or model in the loop.

Task

Complete compact_history(messages, summarize, count_tokens, token_budget, keep_recent=2):

  1. Compute the total token count. If it is not greater than token_budget, return messages unchanged.
  2. Peel off the system message: head = [messages[0]] if its role is "system", else head = []. The rest is the body.
  3. Split the body into recent (its last keep_recent messages) and old (everything before them). Handle keep_recent == 0, where recent is empty and old is the whole body.
  4. If old is empty, there is nothing to summarize — return messages unchanged.
  5. Otherwise build summary = {"role": "system", "content": summarize(old)} and return head + [summary] + recent.

Example

messages = [
    {"role": "system",    "content": "You are a helpful assistant."},  # 5 tokens
    {"role": "user",      "content": "one two three four"},            # 4 tokens
    {"role": "assistant", "content": "reply a"},                       # 2 tokens
    {"role": "user",      "content": "five six seven"},                # 3 tokens
    {"role": "assistant", "content": "reply b"},                       # 2 tokens
]

count_tokens = lambda m: len(m["content"].split())        # total = 16
summarize = lambda old: f"earlier conversation ({len(old)} messages)"

compact_history(messages, summarize, count_tokens, token_budget=12, keep_recent=2)
# -> [
#   {"role": "system", "content": "You are a helpful assistant."},
#   {"role": "system", "content": "earlier conversation (2 messages)"},
#   {"role": "user",   "content": "five six seven"},
#   {"role": "assistant", "content": "reply b"},
# ]

# already within budget -> returned unchanged
compact_history(messages, summarize, count_tokens, token_budget=100, keep_recent=2) is messages
# -> True

Constraints

  • Compact only when the total token count is strictly greater than token_budget.
  • Never drop or rewrite the system message, and never rewrite the recent window — only the old middle is summarized.
  • The summary is a single message {"role": "system", "content": <summary string>}.
  • keep_recent may be 0 (summarize everything after the system message) and may be larger than the body (then nothing is old, so return unchanged).
  • Do not call any real model, tokenizer, or network — summarize and count_tokens are provided.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug