LLM Context Windows: What They Mean for System Design

Context window size shapes every architectural decision in LLM applications. This post covers how to reason about context allocation, the limits that still matter even with large windows, and the patterns that scale.

Context Windows Have Grown Enormously - and Still Matter

In 2020, GPT-3 had a 4,096-token context window. By 2024, Claude 3.5 Sonnet supported 200,000 tokens, and current frontier models offer as much or more. This growth might suggest context management is a solved problem. It is not.

Larger context windows shift but do not eliminate the constraints. Cost scales with context size. Latency increases. Performance on information buried deep in the context can be worse than information near the edges. And most importantly: dumping everything into the context is not the same as having the model reliably use it.

The Token Budget Framework

Every LLM call consumes tokens from a fixed budget. Allocate that budget explicitly:

python
def build_rag_context( system_prompt: str, query: str, retrieved_chunks: list[str], max_context_tokens: int = 100_000, model: str = "claude-sonnet-4-6" ) -> str: import anthropic client = anthropic.Anthropic() # Measure what we have system_tokens = client.count_tokens(system_prompt) query_tokens = client.count_tokens(query) overhead = 500 # Buffer for formatting, separators, response available_for_context = max_context_tokens - system_tokens - query_tokens - overhead # Fill available context greedily (best chunks first) context_parts = [] used_tokens = 0 for chunk in retrieved_chunks: chunk_tokens = client.count_tokens(chunk) if used_tokens + chunk_tokens > available_for_context: break context_parts.append(chunk) used_tokens += chunk_tokens return "\n\n---\n\n".join(context_parts)

The "Lost in the Middle" Problem

Research shows that LLMs perform worse on information buried in the middle of a long context than on information near the start or end. The model "attends" more strongly to the beginning and end of the prompt.

Practical implications:

  • Put the most important instructions at the start of the system prompt
  • Put the most relevant retrieved chunks first (not last) in the context
  • For very long contexts, consider chunking the context and running multiple calls
python
def sort_chunks_for_context(chunks: list[dict]) -> list[dict]: """Put highest-relevance chunks at the beginning of the context.""" return sorted(chunks, key=lambda c: c.get("rerank_score", 0), reverse=True)

Prompt Caching: A Must for Long Contexts

When your system prompt or context is static (or changes infrequently), prompt caching reduces cost dramatically by caching the prefix:

python
response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, system=[{ "type": "text", "text": long_system_prompt_with_company_docs, "cache_control": {"type": "ephemeral"} # Cache this prefix }], messages=[{"role": "user", "content": user_query}] ) # First call: full input cost # Subsequent calls with same prefix: ~90% lower input cost

For a RAG system with a 50,000-token context that is 80% static knowledge and 20% per-query retrieval: prompt caching reduces the input token cost by ~80% after the first call.

Multi-Turn Conversation Context Management

In a multi-turn conversation, history grows indefinitely. Eventually it exceeds the context window. Strategies:

Sliding window: Keep only the last N turns.

python
def trim_conversation(messages: list[dict], max_turns: int = 10) -> list[dict]: if len(messages) > max_turns * 2: return messages[-max_turns * 2:] return messages

Summarization: Summarize older history, keep recent turns verbatim.

python
def compress_history(messages: list[dict], keep_recent: int = 5) -> list[dict]: if len(messages) <= keep_recent * 2: return messages older = messages[:-keep_recent * 2] recent = messages[-keep_recent * 2:] summary_prompt = "Summarize this conversation briefly:\n" + \ "\n".join(f"{m['role']}: {m['content'][:200]}" for m in older) summary = call_llm(summary_prompt, max_tokens=200) return [{"role": "user", "content": f"[Earlier conversation summary: {summary}]"}, {"role": "assistant", "content": "Understood."}] + recent

When Not to Use a Large Context Window

Large context windows enable new patterns - but they are not always the right tool:

  • For many independent queries: Each query gets only the context it needs, not a 200K-token window of everything
  • For real-time applications: 200K tokens × latency per token = slow responses; use RAG to retrieve only what matters
  • When precision matters: A targeted 2,000-token context often produces better answers than a diluted 200,000-token context where the relevant information is 1% of the total

Large contexts are most valuable for: document analysis, long code review, comprehensive research synthesis. Not for standard chatbots or classification tasks.

Common Mistakes

Ignoring the "lost in the middle" effect. Research consistently shows that language models recall information placed at the very beginning or end of a long context much more reliably than information placed in the middle. If you concatenate retrieved chunks without considering position, the most relevant chunk may land in the middle and get underweighted. Experiment with placing high-priority content at the start or end of your context window.

Treating the full context window as free. Every token in the context window costs money at inference time and increases latency. A 128K-token window does not mean you should fill it - it means you can when necessary. Always measure whether expanding the context actually improves task performance before committing to large contexts in production.

Forgetting that KV cache memory scales quadratically with context length. The key-value cache used for efficient autoregressive generation grows as O(n^2) with sequence length, not linearly. At long contexts this becomes the dominant memory cost and can exceed the model weights themselves. Profile KV cache memory usage at your target context length before committing to hardware.

What to Practice Next

  • Design a benchmark that places the same relevant fact at three positions in a long context (beginning, middle, end) and measures model accuracy at each position.
  • Implement a sliding window chunker that respects token budgets and places the highest-similarity chunk first in the context; measure whether it improves answer accuracy on your eval set.
  • Profile peak GPU memory at context lengths of 4K, 16K, 64K, and 128K for a model you deploy; plot the curve and identify the point where memory becomes the bottleneck.

Related Posts

More posts

Fine-Tuning and Post-Training: LoRA, SFT, DPO, and Reasoning RL

What actually happens after pretraining, and when you should do any of it yourself. Parameter-efficient fine-tuning with LoRA, supervised fine-tuning data, preference optimization, and the reinforcement learning recipe behind reasoning models, with a decision framework and a project you can run on one GPU.

#fine-tuning#post-training#rl#reasoning-models#huggingface#llm

Common ML Architectures Reference: CNN, RNN, Transformer, MoE

A concise technical reference for the neural network architectures that power modern ML - what each one does, how it works, when to use it, and what to watch out for.

#cnn#reference#moe#deep-learning#rnn#transformer

Building a Streaming API With LLMs

Streaming transforms LLM user experience - users see the first token in under a second instead of waiting for full generation. This post covers the implementation patterns for both server and client.

#llm#system-design#openai