Module 3: Memory: The Agent That Remembers
6. When memory degrades: context drift in long conversations
Description
By the end of this lesson you'll be able to recognize, with concrete evidence from the message array reaching the model, when an agent's erratic behavior isn't a memory failure — memory brought in the correct data, in the correct place in the array — but context drift: the gradual degradation a model suffers when the history it loads on every call grows too large. You'll be able to tell that symptom apart from a scope or storage problem (lessons 2 and 4), and you'll be able to anticipate what type of agent is most likely to run into it.
This matters because a production agent's quality criterion isn't just "does it remember?" — it's "does it keep reasoning well after remembering a lot?" A support agent that serves the same customer over months, with persistent memory (the kind you built in lesson 4), accumulates a history a single-session chatbot never gets to see. That history doesn't disappear: it gets reinjected in full, or nearly in full, on every new call. There comes a point where having too much of the right thing starts behaving, for the model, similarly to having nothing at all — and a real customer is going to find that point, not a two- or three-turn test case, sooner or later.
Connection to the module: in lesson 2 you saw that a correctly reinjected history doesn't guarantee the model reasons well about it — that lesson's Exercise 2 showed an agent that failed with all the information available, and the conclusion was "suspect the model or the prompt, not the memory." This lesson picks up exactly that crack and explains it in depth: that kind of failure isn't random, it has a name, a mechanism, and a predictable pattern. It also builds on lesson 4 — persistent memory that survives across sessions is precisely the scenario where a history can grow with no natural limit, week after week, until it turns into this lesson's problem. What this lesson doesn't cover yet is what to do about it systematically: summarizing the history or resetting the context is lesson 7's full job, right after this one.
When having the whole history stops being enough
Imagine a lawyer who, before every call with a client, reviews the case's complete file. The first week the file has five pages — they read it in a minute and nothing slips by. By week fifteen, the same file has eighty pages: new clauses, emails, updates, some note that contradicts something said at the start because circumstances changed. The lawyer still reads all of it before every call — nobody took any pages away — but with eighty fresh pages in their head, in the seconds before picking up the phone, two similar clauses from different pages start blending together, and they mix up which one applies to today's situation. It's not that they "forgot" the file — they read it in full, start to finish. It's that processing eighty pages with precision isn't the same as processing five, even though it's the same lawyer with the same ability.
A language model, within a single call, suffers a measurable version of exactly that. Anthropic calls it context rot, and describes it plainly: as the number of tokens in the context window grows, the model's ability to accurately retrieve information from that context decreases. The cause isn't that the model "runs out of memory" — it's architectural. Today's models are built on the transformer architecture, which lets every token attend to every other token in the context, producing, in Anthropic's words, "n² pairwise relationships for n tokens." Models operate with an "attention budget" — a budget that gets used up as more context comes in. The more tokens there are to relate to each other, the thinner that budget gets stretched, and the finer the attention available for any particular fragment.
This isn't a switch that flips all at once. Anthropic clarifies there's no "cliff": what there is, is a "performance gradient." A model stays capable in long contexts, but shows less precision at retrieving specific information and at reasoning about relationships that cross large stretches of the context. That nuance matters for diagnosing: you're not going to see the agent work perfectly through turn 30 and then completely collapse on turn 31. You're going to see it become, gradually, less reliable with data the customer said a while ago, while it keeps working fine with the most recent material.
Worked example
Since lesson 4, NubeFit has persistent per-customer memory: Postgres Chat Memory, with sessionKey equal to the customer's phone number and contextWindowLength at 50 — deliberately generous, so nothing gets lost. Let's compare the same question — "sign me up for the next spinning class you have available" — for two different customers: one just starting out, and one who's been chatting with the bot for five weeks.
Ana, new customer, turn 4. On turn 1 Ana wrote: "I prefer morning spinning classes, I work nights." The agent confirmed. Two brief questions later, about lockers and Sunday hours, Ana asks to be signed up for the next spinning class.
# Message array the model receives — Ana, turn 4
# (same node and same configuration as Sofía's case below)
messages = [
{ role: "system", content: "You are the NubeFit assistant..." },
{ role: "user", content: "I prefer morning spinning classes, I
work nights." }, # turn 1
{ role: "assistant", content: "Noted: spinning in the morning
timeslot." },
{ role: "user", content: "Do the lockers have locks?" }, # turn 2
{ role: "assistant", content: "Yes, the ones on the second floor,
included with your membership." },
{ role: "user", content: "And what time do you close on
Sundays?" }, # turn 3
{ role: "assistant", content: "We close at 6:00 p.m. on Sundays." },
{ role: "user", content: "Sign me up for the next spinning class
you have available." } # turn 4
]
What to expect — Ana. The agent responds: "I'll sign you up for morning spinning, which is the timeslot you told me you prefer — the next open spot is tomorrow at 7:00 a.m." The preference is three exchanges away, in an eight-message array. The model uses it with no apparent effort.
Sofía, customer for five weeks, turn 42. Sofía stated the same preference on turn 18, several sessions ago (all captured by persistent memory under her phone number). Since then she's accumulated another 23 turns: switched plans, froze her membership for two weeks for a trip, complained about parking, asked about protein shakes — and on turn 30, in passing, mentioned that on Saturday nights she prefers to go to yoga, "it's the only slot I have free that day." On turn 42 she writes exactly the same sentence as Ana:
# Message array the model receives — Sofía, turn 42
# contextWindowLength = 50, so turn 18 DOES make it into the array
messages = [
{ role: "system", content: "You are the NubeFit assistant..." },
{ role: "user", content: "I want to sign up for the Plus plan." }, # turn 1
{ role: "assistant", content: "Great, your Plus plan is now active..." },
// ... turns 3 to 17: payments, general hours, a question about classes
{ role: "user", content: "I prefer morning spinning classes, I
work nights." }, # turn 18
{ role: "assistant", content: "Noted: spinning in the morning
timeslot." },
// ... turns 20 to 29: plan change, freeze for a trip, parking
// complaint, question about protein shakes
{ role: "user", content: "On Saturday nights I prefer yoga, it's
the only slot I have free that day." }, # turn 30
{ role: "assistant", content: "Noted: yoga on Saturday nights." },
// ... turns 32 to 41: billing and a question about a cancelled
// group class
{ role: "user", content: "Sign me up for the next spinning class
you have available." } # turn 42
]
What to expect — Sofía. The agent responds: "Do you prefer the morning or evening spinning class? Let me know what time works best for you." The morning preference is, literally, inside the array — on turn 18, 24 turns away from turn 42. Memory did its job: Postgres Chat Memory gathered and reinjected the complete history, including that turn. But the model, with over forty turns of context and a semantically similar piece of data — the Saturday-night yoga preference — competing in the same array, doesn't retrieve it with the same precision it did for Ana.
Interpretation: the difference between Ana and Sofía isn't memory — both have the same node, the same configuration, and in both cases the relevant data did reach the model. The difference is how much context surrounds that data at the moment it's needed. With eight messages, the model retrieves it effortlessly. With over forty, and a semantically close distractor in the mix, it starts failing — the same gradual degradation pattern Anthropic describes, not a clean cutoff.
Why it happens: three factors that make it worse
Where the data sits within the history. The "Lost in the Middle" paper, by Liu et al., measured something specific: model performance at retrieving information depending on where in a long context it's located. The finding, in their own words, is that "performance is often highest when relevant information occurs at the beginning or end of the input context, and significantly degrades when models must access relevant information in the middle." It's a U-shaped pattern. That's why, in Sofía's case, turn 18 is more vulnerable than turn 1: by turn 42, turn 1 sits at an edge of the history (favored), while turn 18 ended up buried somewhere in the middle of an array that already has more than forty entries.
Semantically similar distractors amplify the error. Chroma's research report on context rot, which evaluated 18 models, found that "even a single distractor reduces performance relative to the baseline" — and that effect grows with context length. In Sofía's case, the mention of Saturday-night yoga isn't information about spinning at all, but it shares the same type of data (a scheduling preference for a class), and that's enough for it to compete with the correct data at the moment the model has to decide what to use.
How n8n measures it isn't how the model measures it. The memory node's Context Window Length parameter — the same one you adjusted in lessons 3 and 4 — counts, according to the official documentation, the number of previous interactions to consider. Interactions, not tokens. Two agents with the same contextWindowLength value can reach the model with very different amounts of tokens: one agent that only exchanges short sentences, and another that on every turn reinjects a tool's complete response with the entire week's class catalog, accumulate completely different context volumes under the same configured number of "turns." And with persistent memory (lesson 4), scoped to a real customer who might keep chatting for months, there's no natural ceiling stopping that growth — the history keeps expanding unless something actively controls it. It's worth telling this apart from the hard error the model provider can return when the total token count exceeds the physical limit of its context window — a binary failure, the node's execution just cuts off with an error. Context rot is different and shows up much earlier: the degradation is progressive, you don't need to approach the model's physical limit to start losing precision.
Common mistakes
Believing context rot is a memory failure, fixable by raising contextWindowLength or switching to a model with a bigger window (conceptual). What happens: seeing the agent ignore a piece of data the customer already gave, contextWindowLength gets raised to a bigger number, or a model with more capacity gets connected — and the problem doesn't improve, or even gets worse. Why it happens: the natural intuition is "if the agent doesn't use a piece of data it has well, give it more room so it fits better." But the context rot mechanism isn't about available space — it's about attention split across more tokens. Raising contextWindowLength adds more tokens to the array the model has to process on every call, which expands the problem instead of solving it. How to spot it: if raising contextWindowLength or switching models doesn't improve precision on a piece of data already present in the array — or makes it worse — it's not a capacity problem. How to fix it: diagnose first, with the execution panel, whether the data is in the message array (this rules out scope and storage, lessons 2 and 4); if it's there and still fails, the axis to work on is how much context surrounds that data, not how much context total you allow it to have — that's exactly what lesson 7 addresses.
Confusing "the model doesn't have the data" with "the model has the data but misuses it due to context overload" (conceptual). What happens: any "the agent doesn't remember X" gets treated as the same problem, and the sessionKey or the storage node gets checked over and over, with no result — because in this case both are perfectly well configured. Why it happens: for the customer writing in, "the bot ignored something I already said" feels identical no matter the internal cause; the visible symptom doesn't distinguish between the two causes. How to spot it: open n8n's execution panel and inspect the real message array the model received on that turn. If the data isn't there, it's a scope or storage problem (lessons 2 and 4). If it is there, literally, in some previous turn of the array, and the model still fails, it's context drift. How to fix it: branch the diagnosis right there — if the data already shows up in the array, stop checking sessionKey and the storage node; the problem has moved to how much context surrounds that data, this lesson's topic.
Testing the agent only with short, freshly started conversations during development, never with a simulation of a long relationship (practical). What happens: every test during development starts with a "new" customer or with few accumulated turns, so never enough context builds up for the degradation to show. The agent passes every test and fails, weeks later, with a real long-term customer — exactly the type of customer lesson 4's persistent memory is meant to serve well. Why it happens: simulating weeks of real history takes time, and it's not the natural flow for quickly iterating on a prompt or a tool during development. How to spot it: if you never ran a test with an array of thirty, forty, or more accumulated turns — even a synthetic one, built by hand — you didn't verify context drift, you only verified short-term behavior. How to fix it: before calling a version of the agent that's going to use persistent memory in production ready, build at least one long test conversation — you can ask a model to simulate thirty or forty plausible turns — and deliberately include an early piece of data the agent should retrieve on the last turn, like Sofía's case.
Exercises
Exercise 1 — Diagnosis with the execution panel. A support agent with persistent memory fails on turn 55 of a conversation: the customer had said, on turn 6, that their shipping address is in Bogotá, and on turn 55 the agent quotes shipping as if it were going to Mexico City. You open n8n's execution panel and confirm that the complete turn 6, with the Bogotá address, is indeed present in the message array the model received on turn 55. Is this a memory problem? What is it, and what's your basis for saying so?
See solution
It's not a memory problem. The data is present and in the correct place in the array — the memory node did its job: correct scope and storage, exactly as you defined them in lessons 2 and 4. It's context drift: with 55 turns of accumulated context, the model lost precision on an early piece of data, likely because it ended up buried far from the current turn — the effect "Lost in the Middle" describes — and possibly competing with other cities or addresses mentioned in later turns.
Why it works: this lesson's criterion is exactly that — if the data shows up in the array and the model still fails, the cause shifts from memory to context. Confirming it with the execution panel, not with an assumption, is what keeps you from checking the wrong node.
Exercise 2 — Apply the contextWindowLength criterion. Two NubeFit agents have contextWindowLength = 30 on their Postgres Chat Memory node. Agent 1 only uses the System Message and short text responses. Agent 2, in addition, has a tool connected that returns the week's complete class catalog — several hundred words — every time it's called, and that response gets saved as part of the history. With the same contextWindowLength value, which of the two agents is more exposed to context drift, and why?
See solution
Agent 2, even though the configured parameter is identical. Context Window Length counts interactions, not tokens — thirty turns of short messages can be a small array, while thirty turns where several include a tool's complete response with hundreds of words each accumulate far more tokens. Since the context rot mechanism depends on the volume of tokens the model has to relate to each other in a single call — not the number of turns as such — Agent 2 reaches the regime where precision starts degrading sooner, even though both agents have "the same" configured on the surface.
Why it works: separating "how many turns the node allows" from "how many tokens it actually accumulates" is necessary because the parameter you see on the memory node doesn't directly measure what causes context rot — the same separation you already made in lesson 2 between "the model's capacity" and "what decides what goes into that capacity," now applied to a different axis: how much goes in, not whether it goes in.
Exercise 3 — Correct a colleague. A colleague, after seeing Sofía's case, tells you: "Easy, let's raise contextWindowLength to 500 so the agent never loses anything a customer said." What would you tell them, using what you learned in this lesson?
See solution
That raising contextWindowLength doesn't solve context drift — it probably makes it worse. The mechanism isn't "the model runs out of room to store data": it's that every additional token further splits the model's attention budget among more possible relationships between tokens. With a contextWindowLength of 500, the agent does "have" nearly everything the customer ever said, in the sense that it's present in the array — but precisely because of that, with so much context competing, it's more likely to lose precision on a specific piece of data, not less.
Why it works: telling apart "the data is available" from "the model uses it accurately" is the same distinction that carries this entire lesson. What helps isn't accumulating more history with no limit, but managing how big and how loaded with irrelevant context that history is when it reaches every call — the problem the next lesson starts solving.
Summary and next step
You've now seen that a correctly reinjected history doesn't guarantee precision: as a conversation accumulates turns — especially with persistent memory that survives weeks or months, like lesson 4's — the model loses accuracy on data that is actually present in the message array. It's a documented, named phenomenon, context rot or context drift, caused by how the transformer architecture splits its attention among more and more tokens, not by any "space" limit you fix by enlarging the window or contextWindowLength.
Before moving on you should be able to: diagnose, with n8n's execution panel in view, whether a failure of "the agent ignored something the customer already said" is a memory problem (lessons 2 and 4) or context drift; explain why raising contextWindowLength or the model's size isn't, by itself, a solution; and recognize that the risk grows with long-lived conversations — exactly the kind of agent lesson 4's persistent memory makes possible.
You now know how to recognize the symptom and its cause. What you still don't have is what to do with a history that threatens to grow too large before context drift sets in — that's exactly the next lesson's job: when it's worth summarizing the conversation so it fits into a more manageable space, and when it's better to directly reset the context instead of continuing to drag everything along.
Resources
- Effective context engineering for AI agents — Anthropic — the source for the definition of context rot used in this lesson: the attention-budget mechanism, the n² relationships between tokens, and why the degradation is a gradient and not a clean cutoff.
- Context Rot — Chroma Research — the empirical study with 18 models backing this lesson's example: how semantic distractors amplify degradation as context grows.
- Lost in the Middle: How Language Models Use Long Contexts — Liu et al. (arXiv) — the paper documenting the U-shaped pattern: information at the start or end of a long context is retrieved better than what sits in the middle, the basis for why Sofía's turn 18 is more vulnerable than turn 1.
- Simple Memory node — n8n Docs — confirms that
Context Window Lengthcounts interactions, not tokens, the basis for this lesson's Exercise 2. - Postgres Chat Memory node — n8n Docs — the node behind Sofía's scenario: persistent memory that, having no natural time ceiling, is exactly the kind of configuration where history can grow large enough for context drift to show up.