
Imagine you're building a customer support chatbot for a software company. The product documentation is 400 pages long, and your users expect the bot to answer questions accurately about any of it. You've heard about Retrieval-Augmented Generation — the technique where you pull relevant documents from a database and feed them to a language model before asking it to answer — and you're excited to get started. Then you hit your first real wall: the model throws an error because you've sent it too much text. You've just collided with one of the most consequential constraints in all of applied AI: the context window.
Understanding context windows isn't just trivia. It's the foundational design constraint that determines how much you can retrieve, what format your retrieved text should be in, which model you should choose for a given task, and how you chunk your source documents. Get this wrong, and your RAG system either silently fails — truncating the context and losing crucial information — or wastes money by stuffing unnecessary tokens into every request. Get it right, and you can build systems that feel genuinely knowledgeable and precise.
By the end of this lesson, you'll understand context windows at a mechanical level and be able to make informed RAG design decisions because of that understanding. You won't just know what a context window is — you'll know why it shapes every other choice you make.
What you'll learn:
This lesson is designed for beginners. You should have a general idea of what a large language model (LLM) is — something like GPT-4 or Claude — and a rough sense that RAG systems retrieve documents to help a model answer questions. You don't need to have built anything yet.
Before we can talk about context windows, we need to talk about tokens, because a context window is measured in tokens, not words or characters.
When you send text to a language model, the model doesn't read it the way you do, letter by letter or word by word. Instead, the text gets broken down into chunks called tokens by a process called tokenization. A token is roughly a fragment of text — sometimes a whole word, sometimes part of a word, sometimes a punctuation mark. The exact breakdown depends on the tokenizer, which is a lookup table that maps text fragments to numerical IDs.
Here's a concrete example. Take the sentence:
"Retrieval-Augmented Generation is powerful."
A tokenizer might split this into something like:
["Retrieval", "-", "Aug", "mented", " Generation", " is", " powerful", "."]
That's 8 tokens for a sentence with 6 "words." Notice that "Augmented" got split into "Aug" and "mented" because the tokenizer treats hyphenated compound words differently, and common words like "is" are single tokens while longer, less common words get fragmented.
As a rough rule of thumb: 1 token ≈ 0.75 words in English, or about 4 characters. So 1,000 tokens is roughly 750 words, or about a page and a half of standard document text. In other languages, especially non-Latin scripts, a single character might consume multiple tokens — which is an important consideration if your RAG system handles multilingual content.
Why does this matter? Because every cost calculation, every rate limit, and every context window size is measured in tokens, not words. When an API charges you $0.01 per 1,000 tokens, or tells you the model supports "128,000 tokens," they mean tokens in this technical sense.
Try it yourself: OpenAI provides a free tokenizer playground at platform.openai.com/tokenizer. Paste in a paragraph from your own documents and see exactly how it gets broken into tokens. This is one of the most useful 5-minute exercises you can do before designing a RAG system.
A context window is the maximum amount of text — measured in tokens — that a language model can process in a single interaction. Think of it as the model's short-term working memory. When you send a request to a model, everything in that request plus everything in the response has to fit within this window. If you exceed it, the model either returns an error or silently truncates the input, dropping content from one end.
To build a strong intuition here, consider an analogy. Imagine you're giving a complicated briefing to a consultant, but the consultant can only hold a 30-page document in their hands at once. If your briefing materials are 300 pages, you have to carefully choose which 30 pages to hand them — because they genuinely cannot read the rest. They can't "glance at" the other pages. They don't exist in the conversation.
That's exactly how a language model works. It isn't skimming your 400-page product manual and selectively attending to the relevant parts. It can only reason about the tokens that are inside its context window right now. Everything else is invisible to it.
Early language models had very small context windows. GPT-2 could handle around 1,024 tokens. GPT-3 expanded that to 4,096. These were tight constraints — a few pages at most. Modern models have pushed the boundaries dramatically:
This expansion sounds like it solves everything. Why bother with careful RAG design if you can just dump your entire document corpus into the context? We'll address this directly in a later section, but the short answer is: cost, latency, and quality all degrade as context size grows. The constraint hasn't disappeared; it's shifted.
Here's where RAG design gets real. Your context window isn't just a size limit — it's a budget that you have to deliberately allocate across several competing needs. Let's break down what consumes tokens in a typical RAG interaction.
Almost every production RAG system starts with a system prompt — instructions you write that tell the model how to behave. Something like:
You are a technical support assistant for Acme Software.
Answer questions using only the provided documentation excerpts.
If the answer is not in the documentation, say "I don't know"
rather than guessing. Be concise and use numbered steps for
instructions.
That example is about 50 tokens. Real system prompts at production scale can run 200–800 tokens once you add persona details, formatting rules, safety guidelines, and examples of good vs. bad responses.
In a RAG system, after the system prompt comes the retrieved content — the passages pulled from your document database that are relevant to the user's question. This is typically the largest single consumer of your token budget.
For example, if you retrieve 5 document chunks and each chunk is 500 tokens, that's 2,500 tokens of retrieved context.
If you're building a multi-turn chatbot (one that remembers earlier messages in a conversation), you need to include prior turns in the context so the model understands the conversation's flow. A 10-turn conversation could easily be 1,500–3,000 tokens.
The actual question the user just typed — usually short, maybe 20–100 tokens.
Here's a part people often forget: the model's output counts against the context window too, and you need to reserve space for it. If you set a max_tokens of 500 for the response, you need to ensure the rest of your context leaves at least 500 tokens of headroom.
Let's model this concretely. Suppose you're using GPT-3.5-Turbo with a 16,385-token context window:
System prompt: 400 tokens
Conversation history: 1,500 tokens
User question: 50 tokens
Reserved for response: 500 tokens
---------------------------------
Available for retrieval: 13,935 tokens
At 500 tokens per chunk, that gives you room for about 27 chunks of retrieved content. That sounds like a lot — until your chunks are longer, your system prompt is more complex, or your users have multi-turn conversations. This is the arithmetic every RAG practitioner needs to do before writing a line of code.
Warning: Always calculate your maximum context usage at the worst case, not the average case. A user who's been chatting for 20 turns with a complex question will hit your limits much faster than your average user. Build in a safety margin of 10–15% to avoid silent truncation.
Now that we understand what the context window is and how it gets consumed, let's look at four concrete design decisions that context windows directly govern.
Chunking is the process of splitting your source documents into smaller pieces that can be stored and retrieved individually. The size of those chunks — measured in tokens — is one of the most impactful design choices you'll make.
If your chunks are too large (say, 2,000 tokens each), you can only fit a few of them into your context. Worse, each chunk may contain a lot of irrelevant content that dilutes the signal and confuses the model.
If your chunks are too small (say, 50 tokens each), each chunk might not contain enough context to stand alone. A sentence like "See the previous section for configuration options" is useless without its surrounding context.
A common starting point is 256–512 tokens per chunk, with some overlap between adjacent chunks (say, 50 tokens of overlap) so that concepts that span a boundary don't get severed. But this isn't universal — technical reference documentation with dense, structured content might warrant smaller chunks, while narrative documents like legal agreements might need larger ones to preserve coherent reasoning.
# Conceptual example of chunking logic
# (using LangChain's text splitter as illustration)
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=400, # target tokens per chunk
chunk_overlap=50, # overlap between chunks
length_function=len, # or a token-counting function
)
with open("product_manual.txt") as f:
raw_text = f.read()
chunks = splitter.split_text(raw_text)
print(f"Split {len(raw_text)} characters into {len(chunks)} chunks")
print(f"Average chunk length: {sum(len(c) for c in chunks) / len(chunks):.0f} chars")
Tip: Use a token-counting function rather than character counting for your chunk size parameter. The
tiktokenlibrary from OpenAI lets you count tokens precisely for any of their models, which gives you much more predictable behavior than relying on character counts.
Once you've decided on chunk size, you need to decide how many chunks to retrieve per query. This is a direct trade-off between coverage (retrieving more increases the chance that the right information is present) and context efficiency (retrieving more burns through your token budget faster).
With a small context window like 4,096 tokens, you might only be able to retrieve 3–5 chunks. With a 128,000-token window, you could theoretically retrieve 200+ chunks — but that doesn't mean you should. Research and practitioner experience consistently shows that stuffing too many chunks into context leads to "lost in the middle" problems: models tend to attend more strongly to content at the beginning and end of a long context, and information buried in the middle gets underweighted.
A practical approach is to start with retrieving 5–10 chunks and add a reranking step — where a smaller, faster model scores the retrieved chunks for relevance and you keep only the top 3–5 before final generation. This way, you search broadly but only put the best content into the expensive context window.
Context window size is a legitimate factor in choosing which model to use for a given application. Consider these scenarios:
Scenario A — Customer FAQ bot: Questions are short, answers are drawn from a well-structured knowledge base, conversation history is minimal. A smaller-context, cheaper model like GPT-3.5-Turbo handles this well. You don't need a 128K context window.
Scenario B — Legal document analyzer: A user wants to ask questions about a 150-page contract. You need to either chunk the document and run multiple retrievals, or use a model with a sufficiently large context window to ingest the whole document at once. Here, Claude 3.5 Sonnet or Gemini 1.5 Pro becomes relevant.
Scenario C — Long research sessions: A user is working through a complex technical problem over 30 turns of conversation. History accumulates fast. You may need a mid-size context model and a strategy for compressing or summarizing old conversation turns.
The right model depends on understanding your actual token budget requirements, not just picking the most powerful or cheapest option.
When conversations grow long or documents are large, you need explicit strategies for managing what's in the context window. Here are the main approaches:
Sliding window: Keep only the N most recent conversation turns. Simple but abrupt — the model loses access to earlier context entirely.
Summarization: Periodically summarize older conversation turns using a lightweight model, then replace the raw history with the summary. More graceful than a sliding window but adds latency and complexity.
Selective retrieval for history: Store conversation turns in a vector database, just like documents, and retrieve the most relevant past turns for the current question. Particularly effective for long-running assistant sessions.
Document map-reduce: For large document analysis, break the document into sections, summarize each section independently, then synthesize the summaries. This lets you process documents larger than any context window by working in stages.
By now you might be thinking: "Context windows keep getting bigger. Will this whole problem just solve itself?" It's a fair question, and the honest answer is "partially, but not entirely." Here's why:
Cost scales with tokens. LLM APIs charge per token — both input and output. Sending 800,000 tokens of context with every user query would cost hundreds of dollars per conversation at current pricing. Even as prices fall, burning tokens unnecessarily is waste.
Latency scales with context length. The time it takes to get a response increases as context length grows. For a retrieval step that's already adding latency, bloating the context further degrades user experience.
Quality doesn't scale linearly. There's an empirical phenomenon called the "lost in the middle" problem — models perform worse at recalling information from the middle of very long contexts compared to information near the beginning or end. Precision retrieval of the right 5 chunks often outperforms dumping 100 chunks and hoping the model finds the relevant parts.
Large context windows are genuinely useful for specific tasks — analyzing a single long document, maintaining long research sessions — but they don't eliminate the need for smart retrieval design.
This exercise requires Python and the tiktoken library (pip install tiktoken). You'll calculate the token budget for a hypothetical RAG system and make a design recommendation.
Setup:
import tiktoken
# Load the tokenizer for GPT-4o
enc = tiktoken.encoding_for_model("gpt-4o")
def count_tokens(text: str) -> int:
return len(enc.encode(text))
# --- Define your context components ---
system_prompt = """You are a technical support assistant for CloudDeploy Pro.
Answer questions only using the provided documentation excerpts.
If the answer isn't in the documentation, say so clearly.
Format multi-step instructions as numbered lists."""
conversation_history = """
User: How do I create a new deployment pipeline?
Assistant: To create a new deployment pipeline in CloudDeploy Pro, navigate to
the Pipelines tab and click New Pipeline. You'll be prompted to select a template
or start from scratch. Would you like me to walk through a specific template type?
User: Yes, I want to use the blue-green deployment template.
Assistant: The blue-green template provisions two identical production environments...
[additional turns abbreviated for this example]
"""
user_question = "What happens to the old environment after a successful blue-green deploy?"
max_response_tokens = 600 # tokens to reserve for the model's answer
# --- Calculate the budget ---
model_context_limit = 128_000 # GPT-4o
used_by_system = count_tokens(system_prompt)
used_by_history = count_tokens(conversation_history)
used_by_question = count_tokens(user_question)
available_for_retrieval = (
model_context_limit
- used_by_system
- used_by_history
- used_by_question
- max_response_tokens
)
print(f"System prompt: {used_by_system:>6} tokens")
print(f"Conversation history: {used_by_history:>6} tokens")
print(f"User question: {used_by_question:>6} tokens")
print(f"Reserved for response:{max_response_tokens:>6} tokens")
print(f"-------------------------------------------")
print(f"Available for retrieval: {available_for_retrieval:>6} tokens")
# --- Now calculate how many chunks fit ---
tokens_per_chunk = 400
max_chunks = available_for_retrieval // tokens_per_chunk
print(f"\nWith {tokens_per_chunk}-token chunks, you can retrieve up to {max_chunks} chunks")
print(f"Recommended retrieval count (top-k after reranking): {min(max_chunks, 8)}")
What to explore:
tokens_per_chunk to 200, 500, and 1000 and observe how the maximum chunk count shifts.conversation_history string to simulate a 20-turn conversation and see how quickly it erodes your retrieval budget.Mistake 1: Assuming tokens ≈ words and not verifying A common shortcut is to estimate "1 token per word" and then be surprised when the model runs out of context. Always use a proper token counter before finalizing your chunking and retrieval strategy.
Mistake 2: Forgetting to account for the response in your budget
Engineers often calculate context as: system + history + question + retrieved docs = total. They forget that the model's output also occupies the same context window. Reserve your max_tokens before allocating retrieval budget.
Mistake 3: Setting chunk sizes in characters instead of tokens
Many text splitters accept a chunk_size parameter that defaults to character count, not token count. A 1,000-character chunk might be 200 tokens for simple English text but 400+ tokens for code with lots of special characters. Always verify what unit your splitter is using.
Mistake 4: Retrieving the same amount regardless of context window If you're building an application that might run against multiple models (a common pattern as you optimize cost over time), hard-coding "retrieve 10 chunks always" will cause failures when you switch to a smaller context model. Make chunk count a dynamic calculation based on the active model's limit.
Mistake 5: Ignoring the "lost in the middle" problem If you are retrieving many chunks and stuffing them all in, consider ordering them strategically: put the most relevant chunks at the beginning and end of the context block, not in the middle. This isn't guaranteed to help with every model, but it's a low-cost mitigation for a real issue.
Debugging tip: If your RAG system gives correct answers for some queries but vague or wrong answers for others, and the "wrong" cases tend to involve topics that require synthesizing information from multiple sections of your documents, context window exhaustion or the "lost in the middle" problem is often the culprit. Add logging to track how many tokens each request consumes.
Let's pull together what you've learned. A token is the unit of text that language models process, roughly 0.75 words each. The context window is the maximum number of tokens a model can handle in one interaction — its working memory. In a RAG system, that context window must be shared across your system prompt, conversation history, retrieved documents, and the model's response.
This budget-constrained reality shapes four core design decisions:
Large context windows are improving and prices are falling, but the fundamental tension between coverage, cost, and quality isn't going away. Smart RAG practitioners will always be deliberate about what goes into the context window and why.
Next steps to deepen your skills:
tiktoken or the equivalent for your model provider.sentence-transformers or Cohere's Rerank API.The context window is where theory meets engineering in RAG system design. Master it, and you'll make better decisions at every other layer of the stack.
Learning Path: RAG & AI Agents