Skip to content
NLEN
Illustration: Prompt compression for long contexts

Prompt compression for long contexts: summarize or prune

By Ivo Donker — compiled with AI assistance (Claude & Gemini)

The context windows of modern language models keep growing, to hundreds of thousands or even millions of tokens. Yet stuffing a context window indiscriminately carries considerable operational drawbacks: latency rises noticeably, the cost per API call scales linearly, and the model loses focus through attention dilution. Prompt compression is the umbrella term for techniques that minimize the token size of the input while keeping the relevant information intact.

In practice, developers face a fundamental architectural choice: do we apply semantic compression through summaries produced by a language model, or do we opt for deterministic syntactic pruning based on fixed rules, regular expressions and heuristics? For a grasp of the basic principles of window limits and context management, the guide to context window management in practice offers a complete overview of how conversation histories can be built up technically.

Why context compression remains necessary with large context windows

The availability of context windows of 128k to 1M tokens suggests that developers no longer need to worry about the size of their prompts. That is a persistent misconception. A large context window does not guarantee that a model processes all information with equal accuracy. The well-known lost in the middle phenomenon shows that transformer models retrieve information at the start and the end of a long prompt considerably better than facts buried somewhere in the middle.

The compute required for the prefillphase of inference also scales linearly with context length (and quadratically in the pure self-attention layers without optimizations). A prompt of 80,000 tokens causes a noticeable delay in time to first token (TTFT), which makes interactive applications and chatbots feel slow and sluggish. To analyze the real financial impact of large contexts, the article on what a long context window really costs provides a detailed economic calculation.

Finally, needless context pollution leads to hallucination and instruction drift. When a prompt contains thousands of lines of irrelevant documentation, superfluous log lines or outdated conversation turns, the attention mechanism becomes overloaded. Targeted compression acts as a signal-to-noise filter: it forces the model to concentrate on the core instructions and the variables it actually needs.

Semantic compression: recursive and hierarchical summarization

Semantic compression uses a language model to rewrite a long text into a more compact representation while preserving its meaning. This usually happens through a secondary, cheaper LLM call (a compact flash or haiku model, for instance). Two common patterns can be distinguished here: rolling buffers and hierarchical MapReduce summaries.

In a rolling summary buffer , the oldest interaction is merged into an existing summary after every conversation turn. The prompt format therefore keeps a fixed structure: a permanent system prompt, an updated status paragraph and the two to four most recent dialogue exchanges. This keeps the total token count within a predictable range.

In hierarchical summarization , a long document is cut into thematic chunks. Each chunk is summarized in parallel, after which the intermediate results are condensed in a second step into an overarching abstract. This pattern is well suited to long reports or transcripts, but it introduces specific risks:

Syntactic pruning: deterministic pruning and token truncation

Unlike semantic compression, syntactic pruning relies not on a language model but on deterministic code and heuristics. The aim is to eliminate tokens without a single extra LLM call. That yields an immediate reduction in latency and processing cost.

Deterministic pruning can be set up at various levels:

The great advantage of syntactic pruning is absolute precision around variables. Numbers, source URLs, identifiers and variable names in source code stay exactly the same, whereas a summarizing model can easily round such details off or transcribe them incorrectly.

Compression in autonomous workflows and agentic loops

In autonomous AI systems that execute several steps in succession, context growth is one of the main causes of failure. For a deeper look at how these systems work, the overview article on AI agents and autonomous assistants shows how memory and task execution interact.

During a run, an agent continuously collects observations, raw tool outputs, stack traces and interim conclusions in its scratchpad. After six or seven iterations, the context holds tens of thousands of tokens of stale JSON responses. If that context is not actively cleaned up, context pollution sets in and the agent gets stuck in redundant actions. For developers dealing with stalled systems, the guide to debugging agentic loops offers concrete diagnostic strategies for tracking down memory leaks and circular reasoning.

Best practice for agents: Never write tool outputs to central memory unfiltered. Have the tool return a pre-pruned string itself, or replace processed tool results after validation with an unambiguous status confirmation (for example Status: succesvol weggeschreven (ID #4921) instead of the complete 200-line JSON response).

A comparison: summarizing, pruning and RAG extraction

To determine which method best suits a specific architecture, we have to weigh the properties of semantic summarization, syntactic pruning and dynamic RAG filtering against one another. The table below sets out the main operational differences.

Property Semantic summarization Syntactic pruning RAG & vector filtering
Typical reduction 50% – 85% 20% – 45% 70% – 95%
Latency impact High (requires an extra LLM run) Negligible (< 2 ms) Medium (a database lookup)
Preservation of exact data Moderate (risk of rounding) 100% exact for retained text Exact within the selected chunks
Contextual coherence Very high (a flowing narrative) Medium (text can fragment) Fragmentary per chunk
Implementation complexity Medium to high Low (regular expressions / parsers) High (pipeline & vector store)
Biggest risk Hallucination in the summary Removing crucial context Mismatch through semantic drift

A practical implementation of a hybrid compression pipeline

In production applications, a hybrid approach gives the most stable result. Deterministic filters at the front are combined with a sliding window and a periodic summary buffer. The Python pattern below illustrates how such a pipeline can be set up without external libraries.

import re

class ContextCompressor:
  def __init__(self, max_tokens=2000, system_prompt=""):
    self.max_tokens = max_tokens
    self.system_prompt = system_prompt
    self.summary_buffer = ""
    self.recent_turns = []

  def _prune_text(self, text: str) -> str:
    # Verwijder meervoudige witruimtes en lege regels
    text = re.sub(r'\n\s*\n', '\n\n', text)
    text = re.sub(r'[ \t]+', ' ', text)
    # Verwijder standaard HTML- en markdown-ruis
    text = re.sub(r'<!--.*?-->', '', text, flags=re.DOTALL)
    return text.strip()

  def _estimate_tokens(self, text: str) -> int:
    # Snelle benadering: 1 token ~= 4 karakters
    return len(text) // 4

  def add_turn(self, role: str, content: str):
    clean_content = self._prune_text(content)
    self.recent_turns.append({"role": role, "content": clean_content})
    self._rebalance()

  def _rebalance(self):
    total = self._estimate_tokens(self.system_prompt) + \
            self._estimate_tokens(self.summary_buffer)
    
    # Bereken tokens in recente beurten
    for turn in self.recent_turns:
      total += self._estimate_tokens(turn["content"])

    # Als limiet wordt overschreden, verplaats oudste beurt naar samenvatting
    while total > self.max_tokens and len(self.recent_turns) > 2:
      oldest = self.recent_turns.pop(0)
      # In productie: vervang onderstaande append door een compacte LLM-samenvatting
      self.summary_buffer += f"\n[{oldest['role']}: {oldest['content'][:120]}...]"
      total = self._estimate_tokens(self.system_prompt) + \
              self._estimate_tokens(self.summary_buffer) + \
              sum(self._estimate_tokens(t["content"]) for t in self.recent_turns)

  def build_prompt(self) -> str:
    prompt_parts = [f"SYSTEM: {self.system_prompt}"]
    if self.summary_buffer:
      prompt_parts.append(f"HISTORIE SAMENVATTING: {self.summary_buffer}")
    for turn in self.recent_turns:
      prompt_parts.append(f"{turn['role'].upper()}: {turn['content']}")
    return "\n\n".join(prompt_parts)

This architecture ensures that the active interaction always stays sharp and unaltered for the most recent dialogue exchanges, while older interactions condense step by step into a compact historical overview.

Quantifying and evaluating information loss

Every compression step carries the risk that essential details disappear. To keep optimizations from degrading the quality of the final answers, a structured evaluation method is necessary. Never rely on subjective spot checks; measure performance systematically.

The most reliable measurement methods include:

In every evaluation, always measure three variables at once: token reduction (as a percentage), TTFT latency (in milliseconds) and accuracy on your validation set. Only when accuracy stays within acceptable margins is a compression method ready for production.

Context caching and prefix stability: the hidden pitfall

A common mistake when implementing dynamic summary buffers is disrupting the prefix cache. Many modern LLM providers offer prompt caching: static text at the start of a prompt is cached on the server, so that repeated input tokens are processed roughly 50 to 90% more cheaply and considerably faster.

When a rolling summary is placed directly after the system prompt and changes with every turn, the cache breaks for the entire prompt. The cost saving on a few hundred compressed tokens then often fails to outweigh the loss of the caching discount across tens of thousands of static tokens. For more background on the exact rate structures and cache mechanisms, the article on what a prompt costs in tokens and caching offers practical calculations.

For directly applicable practical tips on cutting token consumption without losing performance, the overview of proven token-saving tricks provides additional guidance on smart cache alignment.

The solution to this dilemma is to position static blocks before dynamic elements. Keep the fixed system prompt and the unchanged documentation exactly the same at the start of the payload, and place summary buffers and shifting conversation histories only at the tail end of the prompt.

A decision tree: which method do you choose, and when?

The choice between summarizing, pruning or a hybrid depends primarily on two factors: the required processing speed and the extent to which the task depends on exact numbers and identifiers.

By treating context not as a bottomless pit but as scarce working memory, applications stay fast, affordable and reliable, whatever the scale of the underlying models.