# Context window management for agents: select, compress, isolate

> How to keep agents from degrading when the context fills up — retrieval-based selection, compression strategies, multi-agent isolation, and the numbers behind each choice.

Canonical: https://ironclad.academy/ai-engineering/articles/context-window-management-agents | Difficulty: medium | Topics: Context Engineering, Agents, Prompting, Cost Optimization
Covers: Anthropic, OpenAI, LangChain

## Key takeaways
- Context quality matters more than context size — a 128k window with precise selection outperforms a 1M window stuffed with loosely relevant history.
- RAG-based tool selection reduces context consumption by an order of magnitude on large tool libraries and improves accuracy roughly 3x over listing all tools.
- Apply compression before 70-80% window utilization; waiting for the hard limit means you are already in degraded-output territory.
- Multi-agent isolation is the most effective long-horizon strategy: bounded sub-agent contexts never experience the compounding context explosion of a single long-running agent.
- Verbose tool outputs are the fastest-growing context segment in production agents — post-process them to summaries or structured key-value results immediately after receipt.
- Context window overflow is a silent failure: the model does not tell you it has forgotten early information, so you need observability tooling to catch it before users do.

> **In one line:** Agents accumulate context until quality silently degrades — the discipline of selecting, compressing, and isolating context is what keeps multi-step systems accurate past the first few turns.

**The problem.** A single-turn chatbot can ignore context management — the window is almost empty. An agent running 20 tool calls over a complex task cannot. Every tool output, every reasoning step, every retrieved document lands in the context and stays there. By step 12, the model is operating on a dense, heterogeneous accumulation of partially relevant information, and the quality of its next decision reflects that. The window does not overflow cleanly; it degrades gradually, with the model starting to miss constraints it acknowledged in step 2, lose track of sub-goals set in step 5, and over-weight the most recent tool output simply because it is closest to the generation position.

**The framework.** LangChain formalized the approach in 2025 as four operations: **Write** (produce context: scratchpads, memory), **Select** (choose what enters: RAG over tools, semantic memory retrieval), **Compress** (shrink what is already in: summarization, pruning), and **Isolate** (scope the context: sub-agents with bounded windows). This article focuses on the three that directly address overflow: Select, Compress, and Isolate.

**Key points.**
- Tool schema listing is a hidden token sink: 50 tools at ~200 tokens each = 10,000 tokens before any user input. RAG-based selection retrieves only the 3-5 relevant tools.
- Compression is not just summarization: trimming verbatim tool outputs, deduplicating retrieved chunks, and converting raw API responses to structured summaries all count.
- The lost-in-the-middle effect is real and well-documented: models perform best on information near the beginning or end of long contexts; relevant content buried at position 60% of a full window performs measurably worse.
- Sub-agent isolation is the most scalable long-horizon strategy — each agent sees only its slice.

```
Back-of-envelope: context budget for a 128k-token agent

System prompt (role, rules, output format):    ~2,000 tokens
Tool schemas (10 relevant tools × ~200t):      ~2,000 tokens
Conversation history (5 prior turns):          ~1,500 tokens
RAG results (3 retrieved chunks × ~500t):      ~1,500 tokens
Scratchpad / working memory:                   ~1,000 tokens
Current task input:                              ~500 tokens
-----------------------------------------------------------------
Total in-context:                              ~8,500 tokens  (6.6% of 128k)
Output reserve (max generation):               ~4,000 tokens
-----------------------------------------------------------------
Headroom before compression needed:          ~115,500 tokens

But at step 15 of a coding task with verbose tool outputs:
Tool outputs accumulated (15 × avg 3,000t):  ~45,000 tokens
Conversation turns (30 messages × ~200t):    ~6,000 tokens
-----------------------------------------------------------------
Total at step 15:                            ~60,000 tokens  (47% of 128k)
→ Compression should start at step 8-10, not when you hit the wall.
```

```mermaid
flowchart LR
    TASK[Task input] --> SELECT[Select\nRAG over tools & memory]
    SELECT --> CTX[Context window]
    CTX --> AGENT[Agent step]
    AGENT --> TOOL[Tool call]
    TOOL --> OUTPUT[Tool output]
    OUTPUT --> COMPRESS[Compress\nsummarize & prune]
    COMPRESS --> CTX
    AGENT -->|subtask| SUB[Sub-agent\nisolated context]
    SUB -->|summary| CTX
    AGENT --> DONE[Final answer]

    style SELECT fill:#0e7490,color:#fff
    style COMPRESS fill:#0e7490,color:#fff
    style SUB fill:#15803d,color:#fff
    style CTX fill:#00e5ff,color:#0a0a0f
```

**Key design decisions.**

| Decision | Default approach | Better approach |
| --- | --- | --- |
| Tool listing | Inject all tool schemas on every step | RAG-select 3-5 relevant tools per step |
| Tool output handling | Store verbatim in context | Post-process to structured summary immediately |
| Conversation history | Keep all turns forever | Sliding window + summarize older turns |
| Long-horizon tasks | Single agent accumulates all context | Sub-agent per phase, pass summaries between |
| Compression trigger | At window limit | At 70% utilization (proactive) |
| Memory | Re-retrieve on each step | Semantic store with write-through on new facts |

**If you have 60 seconds, say this.** "Context management for agents is RAM management. I watch three token segments separately: tool schemas, tool outputs, and conversation history. Tool schemas I select via embedding-based retrieval — only the relevant few go in per step. Tool outputs I compress immediately after receipt — a 3,000-token API response becomes a 200-token structured summary before it hits the messages array. History I manage with a sliding window plus periodic summarization at checkpoints. For tasks that span more than 10-15 steps, I break them into sub-agents, each with a bounded context, passing only summaries between them. I set a compression trigger at 70% window utilization, not 100%, because by the time you overflow you've already been operating in degraded-quality territory for several steps."



A production agent I worked on looked fine in testing. It ran 8 tool calls, answered a complex multi-document question, passed all evals. When we put it in front of real users and their sessions ran to 15-20 turns, the failure mode was subtle: the agent would answer correctly but then contradict a constraint it had acknowledged four messages ago, or re-fetch data it had already retrieved because it stopped "remembering" it had done so. The context window wasn't overflowing — we were at ~60% utilization. The problem was that no one was managing what was in that 60%.

This is the defining characteristic of context failures in production agents: they degrade silently. The model does not throw an error. It just becomes progressively less coherent, less consistent, and less accurate as the relevant signal gets diluted by accumulated noise. The `agent-loop` visualizer below shows the stall from the outside: an agent burning steps re-doing work it no longer remembers doing.

[Interactive visualizer on the original page: agent-loop — An agent stuck re-doing work it forgot]

## The context accumulation problem

Think of the context window as a fixed-size whiteboard. At the start of a task it is almost empty. Each agent step writes on it: the reasoning trace, the tool call, the tool output, the observation. Nothing gets erased unless you explicitly erase it.

By step 10 of a coding agent, that whiteboard might contain:
- The original task specification
- Three rounds of code the model wrote and then revised
- Eight tool outputs from file reads, terminal runs, and test results
- The model's own reasoning steps narrated in prose

Most of this is historical. The model needs the current code state and the most recent error, not the full ancestry of every revision. But it is all there, occupying tokens, and the model attends to all of it on every generation step.

```mermaid
flowchart TD
    S1[Step 1: 2k tokens] --> S5[Step 5: 18k tokens]
    S5 --> S10[Step 10: 45k tokens]
    S10 --> S15[Step 15: 90k tokens]
    S15 --> WARN["70% of 128k window\n→ compression overdue"]
    S15 --> S20[Step 20: 112k tokens]
    S20 --> FAIL["87% of window\nquality degraded, approaching error"]

    style WARN fill:#ffaa00,color:#0a0a0f
    style FAIL fill:#ff2e88,color:#111
```

The growth is dominated by tool outputs. A single call to a code execution tool might return a full stack trace — 1,500 tokens. A web search tool returning full page content might return 8,000 tokens per call. Verbose tool outputs are the fastest-growing segment, and they also contain the most noise relative to signal. (The trajectory above assumes chunkier tool outputs than the back-of-envelope in the summary block, which lands step 15 at ~60k — a search-heavy agent pulling full page content hits the steeper curve easily.)

The `context-window` visualizer below turns this budget math interactive — drag the segments and watch which one tips the window over first.

[Interactive visualizer on the original page: context-window — Context budget by segment — drag to see overflow]

## Select: what enters the context

The cheapest token is the one that never enters the window. Selection strategies keep irrelevant content out before it becomes a compression problem.

### RAG-based tool selection

Most agent tutorials list every tool schema in the system prompt. At 5 tools this is fine. At 50 tools, you're injecting 10,000 tokens of schemas into every single agent step, most of which describe tools that are irrelevant to the current step in the current task.

**RAG-based tool selection** works exactly like document RAG: embed each tool's name and description, retrieve the top-k most similar to the current step's query, and inject only those schemas into the context. The model sees a small, focused set of tools and can reason about them clearly.

```python
from openai import OpenAI
import numpy as np

client = OpenAI()

# Pre-embed all tool descriptions at startup
def embed(text: str) -> list[float]:
    resp = client.embeddings.create(model="text-embedding-3-small", input=text)
    return resp.data[0].embedding

TOOLS = [
    {"name": "read_file", "description": "Read file contents from disk", "schema": {...}},
    {"name": "run_tests", "description": "Execute the test suite and return results", "schema": {...}},
    {"name": "search_web", "description": "Search the web for current information", "schema": {...}},
    # ... 47 more tools
]

tool_embeddings = [(t, embed(f"{t['name']}: {t['description']}")) for t in TOOLS]

def select_tools(task_step: str, top_k: int = 5) -> list[dict]:
    """Retrieve the k most relevant tools for the current agent step."""
    query_emb = np.array(embed(task_step))
    scores = [
        (tool, float(np.dot(query_emb, np.array(emb))))
        for tool, emb in tool_embeddings
    ]
    scores.sort(key=lambda x: x[1], reverse=True)
    return [tool["schema"] for tool, _ in scores[:top_k]]
```

LangChain's 2025 research on this approach showed roughly 3× accuracy improvement over listing all tools when the tool library contained 20+ entries. The intuition is straightforward: when the model sees 50 tool schemas, it is simultaneously attending to 50 options on every generation step. When it sees 5, it can reason clearly about which one fits.

### Semantic memory retrieval

An agent with access to persistent memory faces the same problem at the memory layer. Injecting the full memory store on every step is wasteful and noisy. Instead, retrieve only the memory entries relevant to the current step.

```python
# Pseudocode — works the same way with any vector store (Qdrant, Pinecone, pgvector)
def get_relevant_memories(current_step: str, memory_store, top_k: int = 3) -> list[str]:
    results = memory_store.search(query=current_step, limit=top_k)
    return [r.content for r in results]
```

The [agent memory architectures article](/ai-engineering/articles/agent-memory-architectures) covers the full taxonomy of episodic, semantic, and procedural memory — but from a context-management standpoint, the key principle is the same: retrieve what is relevant to this step, not everything you have ever stored.

### Lost-in-the-middle awareness

Selection is also about *placement*. The lost-in-the-middle effect is well-documented: models attend more reliably to content at the start and end of long contexts. If you must include a long retrieved document, put the most critical section near the top of your RAG results, not buried in position 3 of 5. The `lost-middle` visualizer below makes this effect visceral — drag the "relevant fact" chunk to different positions and watch how accuracy changes.

[Interactive visualizer on the original page: lost-middle — Where you place the fact matters as much as whether it's there]

## Compress: shrink what is already in

Even with good selection, context grows. Compression strategies reduce what is already in the window.

### Post-processing tool outputs

The highest-return compression target is tool outputs. A `run_tests` tool that returns 3,000 tokens of pytest output is almost always reducible to a 150-token structured summary:

```python
def compress_tool_output(tool_name: str, raw_output: str, model: str = "gpt-4o-mini") -> str:
    """Post-process a tool output to a compact summary before adding to context."""
    compress_prompt = f"""You are compressing a tool output for an agent context.
Extract only: status (pass/fail/error), key results, any error messages or failure details.
Discard: verbose logs, passing test details, boilerplate output.
Output as structured bullet points, max 200 tokens.

Tool: {tool_name}
Output:
{raw_output[:4000]}"""  # cap input to prevent runaway costs

    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": compress_prompt}],
        max_tokens=250,
    )
    return resp.choices[0].message.content
```

This adds a small LLM call, but using a cheap model (`gpt-4o-mini` at ~$0.15/1M input tokens, illustrative as of mid-2026) to compress a 3,000-token tool output that would otherwise be re-read by the main frontier model on every subsequent step is almost always worth it. The math: if the frontier model charges $15/1M input tokens and runs 10 more steps, that compressed output saves `2,800 tokens × 10 steps × $15/1M = $0.42` in downstream costs while adding `3,000 × $0.15/1M = $0.00045` in compression cost. The ratio is ~900:1.

### Conversation history management

Conversation history is the other major growth segment. A sliding window approach keeps the most recent N turns verbatim and summarizes everything older:

```python
def manage_history(
    messages: list[dict],
    max_verbatim_turns: int = 6,
    summary_model: str = "gpt-4o-mini"
) -> list[dict]:
    """Keep recent turns verbatim; summarize and compress older turns."""
    if len(messages) <= max_verbatim_turns * 2:  # *2 for user+assistant pairs
        return messages

    # Peel off the real system prompt — it must survive compression verbatim,
    # or the agent loses its role and rules after the first summarization pass.
    system_prompt = []
    if messages and messages[0]["role"] == "system":
        system_prompt = [messages[0]]
        messages = messages[1:]

    older = messages[:-max_verbatim_turns * 2]
    recent = messages[-max_verbatim_turns * 2:]

    # Summarize the older portion
    summary_text = client.chat.completions.create(
        model=summary_model,
        messages=[
            {"role": "user", "content": (
                "Summarize this conversation history for an agent. "
                "Preserve: decisions made, facts established, constraints set, current state. "
                "Discard: small talk, redundant attempts, verbose reasoning traces.\n\n"
                + "\n".join(f"{m['role']}: {m['content']}" for m in older)
            )}
        ],
        max_tokens=500,
    ).choices[0].message.content

    return [
        *system_prompt,
        {"role": "system", "content": f"[Conversation history summary]\n{summary_text}"},
        *recent
    ]
```

The compression trigger matters. Do not wait until the window is full. Set a threshold at 70-80% utilization and compress proactively. By 90% you are already in the degraded zone, and a context that just hit the limit will error out mid-turn.

```mermaid
sequenceDiagram
    participant ORCH as Orchestrator
    participant AGENT as Agent
    participant COMPRESS as Compressor

    loop Each agent step
        ORCH->>AGENT: current messages (check utilization)
        alt < 70% window
            AGENT->>AGENT: proceed normally
        else 70-80% window
            ORCH->>COMPRESS: summarize older history
            COMPRESS-->>ORCH: compressed summary
            ORCH->>AGENT: compressed messages
            Note over AGENT: ~40-50% window after compression
        else > 90% window
            Note over AGENT: already degraded — emergency truncation
        end
    end
```

### Recursive summarization at checkpoints

For very long tasks, a checkpoint strategy works better than continuous rolling compression: at predefined milestones (every 10 steps, or after each major phase completes), summarize the entire accumulated context into a "state snapshot" and start a fresh context with that snapshot as the new system prompt prefix. This resets the context clock while preserving the essential state.

The state snapshot should answer four questions: What was the task? What has been done? What is the current state? What remains? Everything else can be discarded.

### Build it or use the platform feature?

Before hand-rolling any of this, check what your stack already ships — as of mid-2026, most of it exists natively. Anthropic's context-editing and memory-tool betas auto-clear stale tool results from the context, which is the post-processing strategy above, productized. OpenAI's Responses API supports automatic truncation of older history. And agent frameworks bundle compaction: Claude Code auto-compacts as the window fills, and LangGraph ships summarization nodes you drop into the graph. For a standard agent loop, the platform feature is the right default — it is one flag instead of a compression pipeline you now own and debug.

Hand-rolled compression earns its keep in two situations: custom preservation rules (a generic auto-compactor does not know that your error messages must survive verbatim while passing-test logs can vanish) and per-segment accounting (platform compaction gives you a smaller context, not a breakdown of which segment was eating it). If neither applies, take the free version.

## Isolate: sub-agents with bounded contexts

Selection and compression extend a single agent's effective horizon. Isolation solves a different problem: some tasks are inherently too long for any single context, and the compounding of selection and compression errors across 50+ steps introduces its own noise.

**Multi-agent isolation** means decomposing the task into subtasks, each handled by a sub-agent with a fresh, bounded context. The orchestrator passes a compact task brief to each sub-agent; the sub-agent returns a compact result summary. No individual agent ever accumulates the full context explosion.

```mermaid
flowchart TD
    ORCH[Orchestrator\n~5k token context]

    ORCH -->|"task: research X\n(500 tokens)"| R1[Research Agent\n~20k context]
    ORCH -->|"task: analyze Y\n(500 tokens)"| R2[Analysis Agent\n~20k context]
    ORCH -->|"task: write Z\n(500 tokens)"| R3[Writing Agent\n~20k context]

    R1 -->|"summary: findings\n(800 tokens)"| ORCH
    R2 -->|"summary: conclusions\n(600 tokens)"| ORCH
    R3 -->|"draft section\n(1200 tokens)"| ORCH

    ORCH --> FINAL[Final synthesis\n~8k total]

    style ORCH fill:#00e5ff,color:#0a0a0f
    style R1 fill:#0e7490,color:#fff
    style R2 fill:#0e7490,color:#fff
    style R3 fill:#0e7490,color:#fff
```

The orchestrator's context stays small because it only ever sees task briefs and summaries, never the full intermediate work. Each sub-agent's context is bounded to its specific subtask. The information passing between them is compact and deliberate — you choose what the next agent needs to know, rather than letting it inherit everything.

The [multi-agent orchestration patterns article](/ai-engineering/articles/multi-agent-orchestration-patterns) covers the broader architectural choices. From a context management standpoint, the key constraint on isolation is designing good inter-agent communication: what you pass between agents should be a semantic summary of the result, not a raw dump of the work product.

```python
from anthropic import Anthropic

client_anthropic = Anthropic()

def run_sub_agent(task_brief: str, available_tools: list, max_steps: int = 10) -> str:
    """Run a sub-agent with a fresh, bounded context and return a compact result."""
    messages = [{"role": "user", "content": task_brief}]

    for step in range(max_steps):
        response = client_anthropic.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            system="You are a focused sub-agent. Complete the task in the brief. "
                   "Return a compact, structured summary of your result.",
            tools=available_tools,
            messages=messages,
        )

        if response.stop_reason == "end_turn":
            # Extract the text result and return it
            return next(
                b.text for b in response.content if b.type == "text"
            )

        # Handle tool use (abbreviated)
        messages.append({"role": "assistant", "content": response.content})
        # ... process tool calls, append results ...

    return "[sub-agent: reached step limit without completion]"
```

### Context scoping and schema hiding

A subtler form of isolation applies within a single agent: **schema hiding** — structuring your application state so the model only sees the fields relevant to its current task, not the entire state object.

If your agent has access to a large runtime state (user profile, order history, session data, preferences), injecting the full state object on every step wastes tokens and introduces noise. Instead, pass a view of the state scoped to the current step:

```python
from pydantic import BaseModel

class FullApplicationState(BaseModel):
    user_id: str
    order_history: list[dict]  # potentially 200+ orders
    preferences: dict
    current_cart: dict
    payment_methods: list[dict]
    address_book: list[dict]
    # ...

def get_agent_view(state: FullApplicationState, task: str) -> dict:
    """Return only the state fields relevant to this task."""
    if "order" in task.lower():
        return {
            "recent_orders": state.order_history[-5:],  # last 5 only
            "current_cart": state.current_cart,
        }
    elif "payment" in task.lower():
        return {
            "payment_methods": state.payment_methods,
            "current_cart": state.current_cart,
        }
    # ... other task types
    return {"current_cart": state.current_cart}  # minimal default
```

This is a lightweight form of isolation that does not require spinning up a separate agent — it just controls what enters the context.

## The numbers: when to use what

Back-of-the-envelope cost modeling for a 20-step coding agent, frontier model at $15/1M input tokens (illustrative, mid-2026):

```
Scenario A: No context management
  Average context at step 10: 60k tokens
  Average context at step 20: 110k tokens
  Total input tokens across 20 steps: ~1.1M tokens
  Cost (input only): ~$16.50 per task

Scenario B: Tool output compression + history sliding window
  Average context (compressed): 25k tokens throughout
  Total input tokens across 20 steps: ~500k tokens
  Cost of 20 compression calls (gpt-4o-mini): ~$0.09
  Net cost: ~$7.59 per task  →  54% reduction

Scenario C: RAG tool selection + compression + 4-sub-agent isolation
  Each sub-agent context: ~20k tokens, 5 steps
  Orchestrator context: ~8k tokens
  Total input tokens (all agents): ~420k tokens
  Cost of compression calls: ~$0.07
  Net cost: ~$6.37 per task  →  61% reduction, better quality
```

The quality gain from Scenario C is harder to quantify but real: each sub-agent operates on a clean, focused context rather than an increasingly cluttered one. Task completion rate in production tends to improve meaningfully with isolation, even when raw token counts are similar to a well-compressed single-agent approach.

One caveat on those numbers: all three scenarios price input at the flat uncached rate. The [prompt caching article](/ai-engineering/articles/prompt-caching-cost-optimization) covers the other big cost lever, and it interacts with these strategies — not always in your favor. Prompt caches key on an exact stable prefix, and two of the techniques above work against that: per-step RAG tool selection changes the tools array every step (a guaranteed cache miss on the schemas and everything after them), and every history-summarization event rewrites the prefix, forcing one full-price re-read of what was previously a ~0.1× cached read. Worked line: compressing a fully-cached 60k context to 25k saves ~35k tokens per step at the cached rate (~$0.05/step at $1.50/1M) but costs one uncached read of the new 25k prefix (~$0.38) — payback in ~7 steps if you compress at checkpoints, never if you compress continuously. The practical rule: keep the system prompt and a small fixed fallback tool set in a stable cached prefix, place the per-step selected tools below it, and summarize at coarse checkpoints rather than every turn. Compression and caching stack — but only if you architect for both.

## What breaks

### Silent quality degradation before overflow

The worst failure mode is the one that looks fine. Context at 75% utilization on step 15 of a complex task produces outputs that are plausible but slightly less accurate, slightly less consistent with earlier constraints. Without careful eval comparison or step-level tracing, you will not notice until users complain — or until they stop using the product without complaining.

The mitigation is observability with per-segment token accounting. Log not just total context length but how many tokens came from each source at each step. When the "tool outputs" segment grows beyond ~30% of the window, you are accumulating history you should be compressing.

Tools like LangSmith, [Langfuse](/ai-engineering/articles/llm-observability-tooling-compared), and Braintrust all capture the full messages array at each step — the key is structuring your logging to break it down by segment, which typically requires adding metadata to your messages before sending them.

### Compression information loss

Summarization discards information. The model running the compression call makes judgment calls about what is "important" — and those calls can be wrong. A compression step that summarizes "the test suite passed" when the actual output was "7 of 8 tests passed, 1 flaked" introduces a subtle inaccuracy that can compound in later steps.

Mitigation: be specific in your compression prompts. Include explicit rules for what to preserve (error messages verbatim, status codes, counts, file paths) and what to discard (passing test logs, progress bars, boilerplate headers). Use structured output formats for compressed summaries so downstream parsing is reliable.

### Sub-agent communication errors

With isolation comes handoff risk. If the summary a sub-agent returns is incomplete or subtly wrong, the orchestrator makes decisions based on that summary — and unlike a single-agent context where errors are visible, the orchestrator cannot verify what the sub-agent actually did.

Mitigation: structured result schemas instead of free-form summaries. Define what a sub-agent result looks like (status, key findings, artifacts produced, blockers) and use structured output to enforce it. The [structured outputs article](/ai-engineering/articles/structured-outputs-constrained-decoding) covers the mechanics of constraining sub-agent output reliably.

### Tool selection miss

RAG-based tool selection can retrieve the wrong tools if the embedding model misunderstands the current step context. If the model needs a tool and it is not in the selected set, it will either fail the step or hallucinate a call to a tool that does not exist.

Mitigation: always include a small "fallback" set of general tools (e.g., "ask for clarification", "report inability to proceed") in the selected set regardless of relevance score. Also log tool selection decisions — if a task type consistently requires a tool that the retrieval step misses, you may need to improve the tool description or add explicit routing rules for that task type.

## The decision in practice

Apply these three strategies roughly in order of implementation cost:

**Start with compression** — it requires no architecture changes, just post-processing tool outputs before adding them to messages and adding a history summarization step. The token and cost reduction is immediate.

**Add RAG tool selection** when your agent has more than ~10 tools. Below 10, the overhead is not worth it; above 20, it becomes material to both context size and answer quality.

**Move to multi-agent isolation** when tasks routinely exceed 15-20 steps or when you are hitting quality degradation despite good compression. This is the most effective strategy for long-horizon tasks but requires the most architectural work — clear task decomposition, inter-agent communication schemas, orchestrator logic.

The [agent loop article](/ai-engineering/articles/agent-loop-perceive-reason-act) covers how agents cycle through perceive-reason-act; this article is about managing the memory those cycles accumulate. The [agentic RAG article](/ai-engineering/articles/agentic-rag-reasoning-loops) covers the retrieval-side of selection in more depth if your bottleneck is the quality of what gets retrieved rather than the volume.

One thing production has taught repeatedly: the agents that hold up at scale are not the ones with the largest context windows. They are the ones whose designers thought carefully about what should be in the context at each step — and had the tooling to see when that thinking was wrong.

## Frequently asked questions
Q: What happens when an agent runs out of context window space?
A: Behavior degrades before the hard limit hits. Most models show accuracy drops when the relevant information is buried in the middle of a long context (the lost-in-the-middle effect). At the hard limit, the API returns an error or silently truncates the oldest tokens depending on the implementation. The right strategy is to monitor context utilization proactively and apply compression or pruning before you reach 70-80% of the window.

Q: What is the difference between context selection and context compression for agents?
A: Selection means only loading relevant content into the context in the first place — using RAG over tool schemas, semantic retrieval over memory, or filtered knowledge graphs. Compression means reducing content that is already in the context: summarizing conversation history, trimming verbose tool outputs, or replacing raw data with structured summaries. Selection is cheaper because it avoids the tokens entirely; compression is a fallback when content has already been loaded.

Q: How does multi-agent isolation help with context management?
A: Each sub-agent runs with its own bounded context containing only the information needed for its subtask. The orchestrator passes a compact task brief rather than the full accumulated history. This means no individual agent ever sees the full context explosion that a single long-running agent would accumulate — a research sub-agent gets the query and retrieval results, the writing sub-agent gets the outline and research summary, not all intermediate steps of both.

Q: Is it better to use a larger context window model or to compress context?
A: Larger context windows solve the hard limit problem but do not solve quality degradation from irrelevant content. A 1M-token model stuffed with 900k tokens of loosely relevant history will still produce worse answers than a 128k-token model given 30k tokens of precisely selected context. Compression and selection strategies improve answer quality and reduce cost simultaneously — larger windows are a safety valve, not a replacement for good context architecture.

Q: What is RAG-based tool selection and why does it matter for agents?
A: When an agent has access to dozens or hundreds of tools, listing all tool schemas in the context consumes thousands of tokens and reduces the model ability to focus on the relevant ones. RAG-based tool selection embeds tool descriptions, retrieves only the relevant subset based on the current task, and injects just those schemas into the context. LangChain research in 2025 showed roughly 3x accuracy improvement over listing all tools, while reducing context consumption by an order of magnitude on large tool libraries.

Q: How do I observe what is actually in the context window at each agent step?
A: Structured logging of the full messages array at each agent step is the baseline — tools like LangSmith, Braintrust, and Langfuse all capture this natively in their tracing. What most teams miss is token accounting per segment: you want to know not just the total tokens but how many came from system prompt, tool schemas, conversation history, RAG results, and tool outputs separately. That breakdown tells you which segment is growing out of control and where to apply compression.
