DEV Community

Solon Framework
Solon Framework

Posted on

Context Compression Strategies in Solon Agents: KeyInfo, Hierarchical, Vector Archive, Composite

Long agent runs do not fail only because tools are wrong. They also fail because the context becomes a landfill: every thought, every tool dump, every retry lands in the same window until the model gets slower, noisier, and more expensive.

Solon AI treats that as a runtime concern, not a prompt-writing problem. ContextCompressionInterceptor watches the ReAct trace, and when history crosses a threshold it compresses the oldest slice with a pluggable CompressionStrategy.

This post is about the strategy layer: what each built-in strategy keeps, when to use it, and how to stack them for production.

The product problem

A naive agent loop stores everything:

  1. user goal
  2. model thoughts
  3. tool calls + raw observations
  4. more thoughts
  5. more tool dumps

After enough turns you hit three failure modes:

  • token tax — you pay for history that no longer changes the decision
  • attention rot — the model re-reads noise and misses the hard facts
  • hard truncation — the provider silently clips the window and you lose the wrong half

Compression is the controlled alternative: keep the decision-critical residue, drop or archive the rest.

Core pieces

Piece Role
ContextCompressionInterceptor Watches message / token thresholds and triggers compression
CompressionStrategy Decides how expired messages become residual context
ReActAgent.defaultInterceptorAdd(...) Mounts the guard on the agent loop

Official docs:

Dependency:

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-ai-agent</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

How the interceptor works

From the docs, the flow is intentionally simple:

  1. set thresholds such as maxMessages (for example 12 or 40)
  2. when history exceeds the threshold, take the oldest slice (Expired Messages)
  3. run the configured strategy on that slice
  4. inject the resulting summary back into the context head
  5. remove the original detail messages

That is not “delete history.” It is summarize and re-anchor. The agent still has a continuous memory chain; it just no longer carries every intermediate observation at full fidelity.

Constructor shape used in the official sample:

ContextCompressionInterceptor memoryGuard =
        new ContextCompressionInterceptor(40, 40000, myStrategy);
Enter fullscreen mode Exit fullscreen mode

The first number is the message threshold. The second is a content / token-related budget. The third is the strategy.

Four built-in strategies

1) LLMCompressionStrategy — one-shot semantic summary

  • Keeps: a short narrative of what happened
  • Drops: verbose thoughts and raw dumps
  • Best for: general chatty agents where exact tool payloads do not matter later
  • Trade-off: cheap and simple; may blur exact parameters

Use this when “what did we decide?” is enough and “what exact JSON did the API return?” is not.

2) KeyInfoExtractionStrategy — fact board

  • Keeps: facts, parameters, conclusions, verified failed attempts
  • Drops: long reasoning prose
  • Best for: SQL generation, ops runbooks, form-filling, any vertical task where a missed ID breaks the next tool call
  • Trade-off: less readable as a story, more useful as a checklist

This is the strategy I reach for first in production tool loops. Models recover better from a dense fact board than from a polite but vague summary.

3) HierarchicalCompressionStrategy — rolling summary

  • Keeps: Summary_N-1 + new history -> Summary_N
  • Drops: nothing critical from the chain of progress; it folds old summary into new summary
  • Best for: multi-hour tasks, multi-step research, long support threads
  • Trade-off: needs a model call on each compression; worth it when the task must survive many windows

This is the “infinite endurance” option. Memory never fully resets; it densifies.

4) VectorStoreCompressionStrategy — cold archive + anchor

  • Keeps in context: a retrieval anchor
  • Keeps outside context: original detail in a vector store
  • Best for: audit, compliance, or agents that may need to re-open raw evidence later
  • Trade-off: needs a vector backend; pair it with a retrieval tool / talent so the agent can fetch archives on demand

The official sample even mounts the vector strategy as a talent so the agent can actively query archived memory:

.defaultTalentAdd(vectorStoreCompression)
Enter fullscreen mode Exit fullscreen mode

That turns compression from pure lossy reduction into hot summary + cold recall.

Production pattern: composite stack

Single strategies are easy. Production usually wants a stack. Solon provides CompositeCompressionStrategy for that.

Recommended order from the docs:

  1. VectorStore first — archive raw detail so nothing is physically lost
  2. KeyInfo next — keep a hard fact board in the working window
  3. Hierarchical last — maintain a rolling global progress summary
VectorStoreCompressionStrategy vectorStoreCompression =
        new VectorStoreCompressionStrategy(vectorRepo);

CompressionStrategy myStrategy = new CompositeCompressionStrategy()
        .addStrategy(vectorStoreCompression)
        .addStrategy(new KeyInfoExtractionStrategy(chatModel))
        .addStrategy(new HierarchicalCompressionStrategy(chatModel));

ContextCompressionInterceptor memoryGuard =
        new ContextCompressionInterceptor(40, 40000, myStrategy);

Agent agent = ReActAgent.of(chatModel)
        .defaultInterceptorAdd(memoryGuard)
        .defaultTalentAdd(vectorStoreCompression) // optional active archive lookup
        .build();
Enter fullscreen mode Exit fullscreen mode

Why this order works:

  • archive first so later strategies can be aggressive
  • extract facts so tool parameters survive
  • roll a hierarchical summary so the agent still knows the mission story

Thresholds that do not fight the model

The docs give practical ranges by context size:

Context size maxMessages content budget Notes
~20k 10–15 8k–12k compress often; small windows punish laziness
~100k 30–40 24k–32k balanced; 32k is a good high-recall line for many models
~200k 50–60 48k–64k fewer compressions; preserve tool-call chains longer
~1m 100–150 128k+ budget for cost/latency more than hard capacity

Two reminders from the official guide:

  • a larger summary window helps the LLM understand history, but costs more
  • a window that is too small makes the model clumsy — for example a file just read can get compressed before it is used

So do not set thresholds only from provider max tokens. Set them from task shape:

  • short tool loops with critical IDs → smaller window + KeyInfo
  • long research / multi-agent sessions → larger window + Hierarchical
  • regulated workloads → VectorStore in the stack

How this pairs with other guards

Compression is one layer of runtime hygiene. It works best with the rest of the ReAct production kit:

Guard What it stops
maxTurns / autoRethink unbounded reasoning depth
StopLoopInterceptor repeated same-action loops
ToolRetryInterceptor flaky tool physics
ToolSanitizerInterceptor giant / sensitive observations before they pollute memory
sessionWindowSize silent history bloat across turns
ContextCompressionInterceptor long-run context landfill inside one run

A practical recipe for a durable agent:

ReActAgent agent = ReActAgent.of(chatModel)
        .maxTurns(12)
        .autoRethink(true)
        .sessionWindowSize(8)
        .defaultInterceptorAdd(new StopLoopInterceptor())
        .defaultInterceptorAdd(new ToolRetryInterceptor())
        .defaultInterceptorAdd(new ToolSanitizerInterceptor())
        .defaultInterceptorAdd(memoryGuard)
        .build();
Enter fullscreen mode Exit fullscreen mode

Sanitize before you compress. If you archive garbage, you only create a more durable garbage pile.

What not to do

  • Do not treat compression as a substitute for better tools. If every observation is a 50 KB blob, fix the tool output first.
  • Do not compress so aggressively that path parameters and entity IDs disappear. Prefer KeyInfo when tools are stateful.
  • Do not ignore cold storage for audit paths. Hierarchical summary is great for progress, weak as a compliance record.
  • Do not invent a second memory system in application code when the interceptor already owns the lifecycle hook (onReasonStart is exactly where this kind of work belongs).

Minimal decision guide

If your agent... Start with
chats and rarely reuses exact tool dumps LLMCompressionStrategy
depends on IDs, filters, SQL, configs KeyInfoExtractionStrategy
runs for many windows / hours HierarchicalCompressionStrategy
may need original evidence later VectorStoreCompressionStrategy
is shipping to production Composite: Vector + KeyInfo + Hierarchical

Closing

Context compression is not a clever prompt trick. It is an operating policy for long agent runs:

  • decide what is hot
  • decide what is cold
  • decide when the window is full enough to force a rewrite of history

Solon’s strategy model makes that policy explicit. Start with KeyInfo, add Hierarchical when tasks get long, and put VectorStore underneath when losing the original record is unacceptable.

Docs to keep open while you tune:

Top comments (1)

Collapse
 
alexshev profile image
Alex Shev

Context compression is really governance for attention. The agent should not read everything just because it can. A good compression strategy preserves decisions, constraints, and evidence while letting stale detail fall out of the active path.