Working Memory and Context Windows
Duration: 15 min
Working Memory and Context Windows
Duration: 15 min
Overview
Working memory is the agent's active workspace - the information it can directly reason about right now. In LLM-based agents, this maps to the context window: the tokens currently fed to the model. This module explores how to manage context windows effectively, techniques for fitting more relevant information into limited space, and strategies for deciding what stays and what gets evicted.
Understanding context window management is essential because it directly determines the quality of agent responses. An agent with a poorly managed context window will hallucinate, forget instructions, or lose track of multi-step tasks.
Key Concepts & Foundations
- What: Working memory is the subset of all agent knowledge actively available for reasoning in a single inference call
- Why: Context windows are finite (4K to 200K tokens); what you include determines response quality
- How: Use sliding windows, priority queues, and dynamic allocation to maximize context utility
Context Window Anatomy
A typical agent context window contains:
1. System prompt - Agent identity, capabilities, and rules (fixed) 2. Retrieved context - RAG results, tool outputs, memory recalls (dynamic) 3. Conversation history - Recent messages (sliding window) 4. Current task - The immediate user request (fixed per turn)
Token Budget Allocation
With a 128K context window, a practical allocation might be:
- System prompt: 2K tokens (fixed overhead)
- Long-term memory recalls: 4K tokens (retrieved per turn)
- Conversation history: 8K tokens (sliding window)
- Tool results: 4K tokens (current turn)
- User message + response space: remaining tokens
The Recency-Relevance Trade-off
- Recency bias - Keep the most recent messages (simple, but loses important earlier context)
- Relevance bias - Keep semantically similar messages (complex, but preserves important context)
- Hybrid - Keep recent messages + retrieve relevant older ones
Hands-On Implementation
from dataclasses import dataclass
from typing import Optional
@dataclass
class Message:
role: str # system, user, assistant, tool
content: str
token_count: int = 0
priority: float = 1.0 # higher = more important to keep
def __post_init__(self):
# Rough token estimate: ~4 chars per token
if self.token_count == 0:
self.token_count = len(self.content) // 4
class ContextWindowManager:
"""Manages what fits in the agent's context window."""
def __init__(self, max_tokens: int = 128000):
self.max_tokens = max_tokens
self.system_prompt: Optional[Message] = None
self.messages: list[Message] = []
self.reserved_tokens = 4000 # Reserve for model response
@property
def available_tokens(self) -> int:
used = self.reserved_tokens
if self.system_prompt:
used += self.system_prompt.token_count
used += sum(m.token_count for m in self.messages)
return self.max_tokens - used
def set_system_prompt(self, content: str):
self.system_prompt = Message(role="system", content=content, priority=10.0)
def add_message(self, role: str, content: str, priority: float = 1.0):
msg = Message(role=role, content=content, priority=priority)
self.messages.append(msg)
self._enforce_limit()
def _enforce_limit(self):
"""Evict lowest-priority messages when over budget."""
while self.available_tokens < 0 and self.messages:
# Find lowest priority message (but never remove the last user message)
candidates = self.messages[:-1] if len(self.messages) > 1 else []
if not candidates:
break
lowest = min(candidates, key=lambda m: m.priority)
self.messages.remove(lowest)
def build_prompt(self) -> list[dict]:
"""Build the final prompt for the LLM."""
prompt = []
if self.system_prompt:
prompt.append({"role": "system", "content": self.system_prompt.content})
for msg in self.messages:
prompt.append({"role": msg.role, "content": msg.content})
return prompt
def get_stats(self) -> dict:
total_used = (
(self.system_prompt.token_count if self.system_prompt else 0)
+ sum(m.token_count for m in self.messages)
+ self.reserved_tokens
)
return {
"total_capacity": self.max_tokens,
"used_tokens": total_used,
"available_tokens": self.available_tokens,
"message_count": len(self.messages),
"utilization": f"{total_used / self.max_tokens * 100:.1f}%"
}
Demo: Managing a conversation with limited context
ctx = ContextWindowManager(max_tokens=2000) # Small window for demo
ctx.set_system_prompt("You are a helpful coding assistant.")Simulate a multi-turn conversation
ctx.add_message("user", "What is a Python decorator?", priority=0.5)
ctx.add_message("assistant", "A decorator is a function that wraps another function to extend its behavior without modifying it.", priority=0.5)
ctx.add_message("user", "Show me an example", priority=0.5)
ctx.add_message("assistant", "Here is a simple timing decorator that measures execution time of any function.", priority=0.5)
This high-priority message should survive eviction
ctx.add_message("user", "Remember: I always want type hints in code examples", priority=5.0)
ctx.add_message("user", "Now explain context managers", priority=1.0)print("Context stats:", ctx.get_stats())
print(f"Messages retained: {len(ctx.messages)}")
for msg in ctx.messages:
print(f" [{msg.role}] (priority={msg.priority}) {msg.content[:60]}...")
Advanced Techniques
When managing working memory in production agents:
1. Dynamic Budget Allocation - Adjust token budgets per section based on task complexity 2. Attention Anchoring - Place critical instructions at the start and end of context (LLMs attend more to these positions) 3. Progressive Summarization - As conversation grows, summarize older turns rather than dropping them entirely 4. Context Window Monitoring - Track utilization and alert when approaching limits 5. Multi-Model Routing - Use smaller models for simple tasks (saves context for complex ones)
Quiz
Q1: What happens when an agent's context window is full?
- A) The model automatically summarizes old messages
- B) Older or lower-priority information must be evicted or summarized ✓
- C) The model crashes
- D) Tokens become cheaper
Q2: Why should you reserve tokens in the context window?
- A) To save money
- B) To leave space for the model's response generation ✓
- C) To make the prompt look shorter
- D) To comply with API limits
Q3: What is the recency-relevance trade-off?
- A) Choosing between fast and slow models
- B) Deciding whether to keep recent messages or semantically relevant older ones ✓
- C) Picking between short and long prompts
- D) Trading accuracy for speed