{"slug": "llm-memory-vs-context-window-the-gap-nobody-explains", "title": "LLM Memory vs Context Window: The Gap Nobody Explains", "summary": "An engineer at a retail client discovered that a customer-support agent was burning money by sending the entire raw conversation transcript on every turn, costing $0.27 per turn instead of a few cents. The developer explains the difference between a context window and memory, advocating for a curated memory layer that separates working, episodic, semantic, and summary memory to cut costs and improve performance.", "body_md": "*The context window is a container. Memory is what you decide to put in it. Almost every \"memory\" conversation conflates the two — here is how they actually work, and how to build a memory layer that does not bankrupt you.*\n\nTwo months ago I was debugging a customer-support agent for a retail client, and the numbers on the dashboard made no sense. Conversations that should have cost a few cents were running to $0.27 per turn. When I opened the logs, I found the cause in one line: the last message in a 40-turn conversation carried a context of 61,000 tokens, and about 55,000 of those tokens were the *entire raw transcript* of the conversation, sent back verbatim on every single turn.\n\nThe developer who built it had done what every tutorial implies is fine: \"the model has a big context window, so just send the whole history.\" The window was 128k. Nothing crashed. Every turn was technically within budget. But at roughly $0.50 per million input tokens on the model they used, 55,000 redundant tokens per turn, across a support queue handling 1,200 conversations a day, meant they were burning money on tokens that the model had already read and that did not help answer the current question.\n\nThat is the gap this article is about. **A context window is a capacity. Memory is a policy.** The window says how much text the model can look at. Memory is the discipline of deciding what belongs in that window, when, and for how long. The moment you stop conflating the two is the moment your agent stops costing ten times what it should.\n\nLet me be precise about the mechanics, because \"context window\" gets thrown around like it is a magical vault.\n\nThe context window is the set of tokens the model attends to when generating the next token. It is bounded by the model's architecture — the maximum sequence length it was trained to handle — and it is currently anywhere from 4,000 tokens (small models) to 200,000 tokens (flagship models), with a handful of research models pushing toward a million.\n\nThree properties of the window matter in production:\n\nMemory is not one thing. Every serious LLM application uses at least three kinds, and most articles only talk about one. Here is the working taxonomy I use:\n\nThis is the raw material the model reasons over right now: the current user message, the last few turns, the retrieved documents, the tool results, the system prompt. Working memory *is* the context window content. The design job is curation: what subset of history earns a seat.\n\nThe brutal constraint is that working memory is where cost and forgetting collide. The more you include, the more you pay and the more the model loses the thread; the less you include, the more context you lose. Every memory system below exists to make this trade-off explicit and cheap.\n\nEpisodic memory is the record of what happened in past conversations: the full turns, timestamps, decisions, outcomes. This is the data you need for analytics and for reconstructing a conversation, but it is *not* what you dump into the window. The retail agent I debugged was shipping episodic memory straight into working memory, uncurated. That is the classic mistake.\n\nThe right move is to keep episodic memory in a database — PostgreSQL, Redis, wherever — and only *project* a curated slice into the window when needed.\n\nSemantic memory is the durable knowledge the agent draws on: product docs, policies, past resolved tickets. This is the vector-database layer. At query time you embed the user's question, retrieve the top-k relevant chunks, and inject them as context. This is what most people mean when they say \"memory\" in RAG systems, and it is the easiest layer to get wrong because retrieval quality — chunking, embedding model, top-k — determines everything.\n\nSummary memory is the strategy that directly attacks the cost problem: instead of replaying a 55,000-token transcript, you compress it into a running summary — \"user is a returning customer, issue is a refund for order #4821, they tried the portal twice, escalated once.\" Summaries collapse 20 turns into 200 tokens.\n\nThe failure mode of summary memory is *lossy compression*. A summary is a decision about what to forget, and bad summaries forget the detail that matters for the current turn. The professional approach is a tiered design: a short rolling summary for the front of the window, the last few raw turns appended after it (because recent context is usually the most relevant), and the ability to retrieve the full transcript or a specific detail on demand when the summary is not enough.\n\nHere is the concrete mental model I now build against. Imagine the context window as a fixed budget, and assign each category a priority:\n\n```\n┌────────────────────────────────────────────────┐\n│ SYSTEM PROMPT        (~fixed, always present) │\n├────────────────────────────────────────────────┤\n│ SUMMARY MEMORY       (compressed history)      │\n├────────────────────────────────────────────────┤\n│ LAST 3–5 RAW TURNS   (recent context, verbatim)│\n├────────────────────────────────────────────────┤\n│ RETRIEVED DOCUMENTS  (top-k semantic hits)     │\n├────────────────────────────────────────────────┤\n│ CURRENT TOOL OUTPUTS (fresh, task-critical)    │\n└────────────────────────────────────────────────┘\n```\n\nThe system prompt is fixed. The summary is re-computed as the conversation grows. The recent raw turns are the last few, not all. The retrieved documents are the top-k, not the whole knowledge base. The tool outputs are fresh. Every category competes for the same budget, and the ordering above is roughly my priority order when budget runs tight.\n\nThe decision rule that drives this: **when the window is getting full, compress the past before you drop the present.** Never silently truncate recent turns to make room for old ones — users rarely reference turn 30, and they always reference the last thing they said.\n\nLet me make this concrete. This is a minimal but production-shaped summary-memory loop — every N turns, compress the history into a summary, and always keep the last few raw turns for recent context.\n\n``` python\nimport json\nfrom openai import OpenAI\n\nclient = OpenAI()\n\nSYSTEM = \"You are a customer support agent. Answer using only provided context.\"\n\ndef summarize(history: list[dict]) -> str:\n    r = client.chat.completions.create(\n        model=\"your-summarizer-model\",\n        messages=[\n            {\"role\": \"system\",\n             \"content\": \"Compress this conversation into a compact running \"\n                        \"summary: customer identity, issue, actions taken, \"\n                        \"outstanding decisions. Max 180 words.\"},\n            *history,\n        ],\n    )\n    return r.choices[0].message.content\n\nclass ConversationMemory:\n    def __init__(self, summary: str = \"\", raw_limit: int = 4, summarize_every: int = 8):\n        self.summary = summary\n        self.raw = []                      # recent raw turns (episodic slice)\n        self.full = []                     # full transcript, kept out of the window\n        self.raw_limit = raw_limit\n        self.summarize_every = summarize_every\n\n    def add(self, role: str, content: str) -> None:\n        self.full.append({\"role\": role, \"content\": content})\n        self.raw.append({\"role\": role, \"content\": content})\n        if len(self.raw) > self.raw_limit:\n            self.raw.pop(0)\n\n    def maybe_compress(self) -> None:\n        if len(self.full) >= self.summarize_every:\n            self.summary = summarize(self.full)\n            self.full.clear()              # transcript archived elsewhere\n\n    def window(self, retrieved: list[str]) -> list[dict]:\n        messages = [{\"role\": \"system\", \"content\": SYSTEM}]\n        if self.summary:\n            messages.append({\"role\": \"system\",\n                             \"content\": f\"Conversation so far: {self.summary}\"})\n        for chunk in retrieved:\n            messages.append({\"role\": \"system\",\n                             \"content\": f\"Context: {chunk}\"})\n        return messages + self.raw\n```\n\nThe shape is what matters, not the library. `full`\n\nis episodic memory — kept for audit, never dumped into the window wholesale. `summary`\n\nis the compressed view. `raw`\n\nis the last four turns, verbatim, because recent context is disproportionately important. `retrieved`\n\nis the semantic layer, injected as context. When a user asks about something from 20 turns ago, the summary carries the outline and the retrieval layer fills in the detail — and the token bill for a 40-turn conversation drops from 55,000 to a few thousand.\n\nI want to give you the numbers I actually see, because \"memory\" articles love architecture diagrams and hate invoices.\n\n**The cost of not doing this.** In that retail agent, the fix cut input tokens per turn by roughly 90% — from 55,000 to around 5,000. At their model's input price, that turned a $0.27-per-turn conversation into a $0.03 one. Across 1,200 conversations a day, that is a saving of roughly $280 a day, or more than $8,000 a month, from one architecture change. Latency improved too, because the model was no longer chewing through 50k redundant tokens before answering.\n\n**Where summary memory breaks.** Summaries are lossy, and the losses are not random — they skew toward details the summarizer judged \"obvious\" at the time, which are exactly the details a later turn might need. The fix is a retrieval fallback: when the agent detects it cannot answer from summary-plus-recent, it should query the archived transcript for the specific detail, not guess. I have also seen summary *drift* — where early wrong facts get baked into the summary and propagated, because the summarizer repeats its own prior summary instead of re-reading the transcript. Re-summarizing from a window of raw turns, rather than from a previous summary, reduces this.\n\n**Where retrieval memory breaks.** Semantic memory fails quietly: a bad embedding model or aggressive top-k cuts can inject irrelevant context, and the model will happily answer from it. The rule from my RAG work applies double inside a memory system: treat every retrieved chunk as a hypothesis, not a fact, and measure retrieval quality with a labeled evaluation set — never trust the demo.\n\n**The context-window ceiling.** Even with perfect memory management, some tasks legitimately exceed the window: a 300-page legal contract being analyzed clause by clause, a code review across a large repository. When the working set is bigger than the window and cannot be compressed, the honest answer is chunked, map-reduce-style processing — summarize sections, then reason over the section summaries — or a model with a genuinely larger window, accepting the cost that comes with it.\n\nNot every app needs this machinery, and I have been guilty of over-engineering it. The honest guidance:\n\nThe tell I look for: if you are spending more than a few cents per turn on an assistant that answers from history, you have already hit the threshold where a memory policy pays for itself. Measure first. A $30 experiment (log token counts per turn, model the cost) will tell you in an afternoon whether the machinery is worth it.\n\nWhen you design memory for an LLM application, go through this list:\n\nThe developer who built that retail agent was not careless. He was following the loudest advice in the ecosystem — \"the window is huge, so send everything\" — and the window was indeed huge enough that nothing crashed. That is the trap. **Nothing crashing is not the same as something working well.** The context window forgives the architecture; the invoice does not.\n\nWhen I explained the fix to the client, I put it in one sentence: the window is the room, and memory is the furniture. You get to decide what is in the room, and you should never leave 55,000 tokens of clutter in it. That single decision is the difference between an agent that costs $0.27 a turn and one that costs $0.03 — and between an agent that forgets the middle of the conversation and one that remembers exactly what matters, on every single turn.\n\n*Gulshan Yad", "url": "https://wpnews.pro/news/llm-memory-vs-context-window-the-gap-nobody-explains", "canonical_source": "https://dev.to/mryadavgulshan/llm-memory-vs-context-window-the-gap-nobody-explains-1mk6", "published_at": "2026-08-25 02:30:00+00:00", "updated_at": "2026-08-25 02:43:11.684622+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "ai-infrastructure", "developer-tools"], "entities": ["PostgreSQL", "Redis"], "alternates": {"html": "https://wpnews.pro/news/llm-memory-vs-context-window-the-gap-nobody-explains", "markdown": "https://wpnews.pro/news/llm-memory-vs-context-window-the-gap-nobody-explains.md", "text": "https://wpnews.pro/news/llm-memory-vs-context-window-the-gap-nobody-explains.txt", "jsonld": "https://wpnews.pro/news/llm-memory-vs-context-window-the-gap-nobody-explains.jsonld"}}