Module 3: Memory: The Agent That Remembers
1. Introduction: the agent that remembers
Description
By the end of this lesson you'll be able to explain why "connecting memory" isn't a single switch, but two independent decisions — what identity you use to group history, and where you store that history —, and you'll be able to locate, given a symptom of an agent that "doesn't remember," which of those two decisions failed. You'll also have the complete map of the seven lessons coming up in this module.
This matters because the symptom "the agent doesn't remember" doesn't have a single cause. A customer who writes today and tomorrow expecting the agent to follow the thread of their case — a complaint, an HR inquiry, a price negotiation — isn't asking for something extravagant: they're assuming the same thing they'd assume of a real person. When the agent fails there, it's almost never because "memory is broken" in the abstract, but because one of two concrete pieces is misconfigured, and without knowing how to tell them apart you end up touching the wrong one.
Connection to the module: in lesson 5 of Module 1 you already connected a memory piece (Simple Memory) to your first agent, with a sessionKey and a contextWindowLength, and you saw that it resolved continuity between turn 1 and turn 2 of the same conversation. This lesson picks that piece back up to show something that wasn't the point that first time: that turn-to-turn continuity is just one of the things "memory" can solve, and there are at least two others this module covers in depth — persisting across different sessions for the same customer, and not degrading when the conversation gets long. This lesson doesn't repeat the #4521 order example turn by turn the way you saw it there; it extends it. You also won't see the live, practical comparison yet between an agent that forgets and one that doesn't — that's exactly the next lesson's job, right after this one.
An agent with no memory is a one-message chatbot
Imagine a customer service counter. The same person is behind it all day, but with a quirk: every time someone talks to them — even if it's the third sentence of the same conversation, spoken ten seconds after the last one — they act as if they're seeing that person for the first time. They ask again for their name, their account number, the reason for the visit. And if that same customer comes back tomorrow to ask "hey, what happened with that thing from yesterday?", the honest answer is "I don't know what you're talking about" — not out of rudeness, but because no trace of the previous conversation remains in their head.
That's how a language model behaves by default. Every call to the model is an isolated invocation: there's no invisible thread connecting one call to the next, except for whatever you forward back into the prompt yourself. When you saw in lesson 5 of Module 1 that the AI Agent node exposes an optional ai_memory connection, that piece is exactly the mechanism that captures what's already been said and injects it back into the context before the model reasons about the new message. Without it, it's not that the agent "has a bad memory" — it's that there's no memory to manage at all, and every message gets processed as if it were the only one that agent has ever seen.
But "connecting memory" isn't a single question with a single answer either. They're two separate decisions, and you can get one right and fail the other:
- Scope: under what identity do you group the history? It can be a chat window's id that lasts as long as that tab stays open, or it can be something stable about the real customer — their phone number, their account id — which is the same today and tomorrow.
- Storage: where does that history live once it's grouped? It can live in n8n's own process RAM, which disappears if the process restarts or if the worker handling the request changes, or it can live in a real database that survives both.
Worked example: the same order, three memory configurations
Go back to lesson 5 of Module 1's TuTienda support agent, with its get_order_status tool. Let's run the same question against three memory configurations, to see where each one fails on its own.
# CONFIGURATION A — no memory connected
memory = none # ai_memory with no connection
prompt.systemMessage = "You are the support assistant for TuTienda..."
tool.name = "get_order_status"
Turn 1 (10:03 a.m., same chat): "Where's my order #4521?" The agent calls get_order_status, gets "in transit, estimated delivery July 24," and responds correctly.
Turn 2 (10:03:15 a.m., fifteen seconds later, same chat): "And when does it arrive?"
What to expect — Configuration A. With no memory, every invocation of the AI Agent node only sees the message that just arrived — nothing of what was said fifteen seconds earlier. The model has no way of knowing that "arrive" refers to order 4521; it responds with something like "can you give me the order number?", even though the customer already gave it in the same conversation.
# CONFIGURATION B — Simple Memory, scoped by chat session
memory.node = "Simple Memory"
memory.sessionKey = "{{ $json.chatSessionId }}" # generated by the widget when the chat opens
memory.contextWindowLength = 10
Turns 1 and 2, same day, same chat tab: work as you already saw in Module 1 — the agent resolves that "arrive" refers to order 4521 and responds with the correct date.
Turn 3 — the next day, the customer opens a new conversation (new tab, the widget generates a chatSessionId different from yesterday's) and writes: "Hi, has my order arrived yet?"
What to expect — Configuration B. Here two things fail at once, though only one is obvious. The obvious one: the widget generated a new chatSessionId for this conversation, and Simple Memory indexes history by that key — under today's key there's nothing saved, so it's a scope problem: the customer is the same person, but for memory it's a different session. The less obvious one: even if you had deliberately reused yesterday's exact chatSessionId, Simple Memory stores everything in n8n's process RAM — n8n's documentation explicitly warns against using this node in queue mode because there's no guarantee two calls land on the same worker. A container restart, or a worker change, and that history simply isn't there. That's a storage problem. The agent asks for the order number again — the same symptom as in Configuration A, even though this time there was a memory piece connected.
# CONFIGURATION C — Postgres Chat Memory, scoped by real customer
memory.node = "Postgres Chat Memory"
memory.sessionKey = "{{ $json.customerPhone }}" # stable: it's the same today and tomorrow
memory.tableName = "n8n_chat_histories"
memory.contextWindowLength = 10
Turn 3, same scenario as in B — next day, new chat, same customer.
What to expect — Configuration C. This time both decisions are resolved. The sessionKey is no longer the ephemeral id the widget generates on every visit, but the customer's phone number — a value that doesn't change between yesterday and today, no matter how many new tabs they open. And the history no longer lives in n8n's process RAM, but in a real Postgres table that's still there after a restart or a worker change. When today's message arrives with that same sessionKey, n8n loads yesterday's history from the database, and the agent responds: "Yes, your order #4521 arrived on July 24, as we discussed yesterday."
Interpretation: the three configurations show that "memory" isn't a single dial. Configuration A had no memory at all. Configuration B had memory, but got both decisions wrong at once for this use case — ephemeral scope and non-persistent storage — so the symptom looks identical to A's even though the cause is different. Configuration C works because it resolves scope (stable customer identity) and storage (real database) at the same time. That, in one scene, is the map for lessons 3 and 4 of this module.
This module's seven decisions
The rest of this module is seven concrete pieces about those two decisions — scope and storage — plus a third problem that only shows up once the conversation gets long:
| Lesson | What it resolves |
|---|---|
| 2 | The practical, live difference between an agent that forgets every message and one that holds the thread of a conversation |
| 3 | How the window of recent turns within a single conversation works — the session-scope axis you saw in Configuration B |
| 4 | How to give an agent a stable sessionKey per real customer and store the history in a database that survives across sessions — the per-user scope and persistent storage axis you saw in Configuration C |
| 5 | How to design multi-turn conversations that keep the thread without the agent losing track |
| 6 | What context drift is: why an agent can degrade in a very long conversation even when its memory works perfectly |
| 7 | When it's worth summarizing the history instead of continuing to accumulate it, and how to reset the context without losing what matters |
| 8 | Mini-project: assemble an agent with persistent per-user memory that survives across sessions, bringing together scope, storage, and handling long conversations |
Notice the order: first you see the difference having memory or not makes (lesson 2), then the two concrete ways of having it — per session (lesson 3) and persistent per user (lesson 4) —, then how that behaves in a real multi-turn conversation (lesson 5), and finally the problem none of the previous pieces solves on its own: what happens when there's too much history (lessons 6 and 7), before assembling it all in the mini-project (lesson 8).
Common mistakes
Treating "memory" as a single yes-or-no switch (conceptual). What happens: you connect any memory node, consider "the agent already remembers the customer" solved, and you're surprised when the next day — or on another device — the agent has no idea about yesterday's conversation. Why it happens: the AI Agent node only asks for one connection at ai_memory, and that visual simplicity suggests it's a single piece with a single behavior. But as you saw in the worked example, there are two independent decisions behind it — scope and storage — and you can get one right and fail the other. How to spot it: if the agent remembers within the same tab but not across different sessions of the same customer, don't ask "is memory broken?" — ask "what identity am I using as sessionKey, and where does that history live?" How to fix it: name the two decisions separately before touching anything — that's exactly what lessons 3 and 4 split apart.
Confusing session memory with persistent memory (conceptual). What happens: you use Simple Memory — session memory, in RAM — for a use case where the customer needs the agent to remember them days later, and the project fails in production right when a restart or a redeploy wipes out all the accumulated history. Why it happens: Simple Memory is, rightly, the simplest option to get started with — that's how you used it in Module 1 — and it's easy not to notice that "simple" also means "lives only as long as the process keeps running." How to spot it: ask yourself, before choosing the memory node, whether your use case needs the agent to remember something that happened in a different session, not just in different turns of the same session. If the answer is yes, Simple Memory isn't enough, no matter how well you configure contextWindowLength. How to fix it: for real persistence across sessions, you need memory backed by a database (Postgres Chat Memory, Redis Chat Memory) and a stable sessionKey for the customer, not for the chat — the full subject of lesson 4.
Believing memory stores the system's real state, not just what was said (conceptual, and becomes practical in Module 4). What happens: the agent responds with a piece of data that was correct three turns ago — "your order is in transit" — but it's already changed in the real system — the order has already been delivered — and the agent doesn't notice because it trusts what memory stored instead of calling the tool again. Why it happens: memory literally stores the text of the conversation — what the agent said, not what's true right now in your database or your API. It's not a cache of your system; it's a log of what was discussed. How to spot it: check whether the agent calls get_order_status again once enough time has passed for the real status to have changed, or whether it just repeats what it already said before. How to fix it: memory solves conversational continuity — what was discussed —; it doesn't replace querying a tool again when the data may have changed. That boundary between "what's remembered" and "what's verified" is what you're going to sharpen when you connect memory and tools together in Module 4.
Exercises
Exercise 1 — Diagnosing the three configurations. Without reading the worked example again, write in one sentence why Configuration A failed and in another sentence why Configuration B failed, using the words "scope" and "storage" where they apply.
See solution
Configuration A: neither scope nor storage failed — there was no memory piece connected at all, so there was no history to group or to store. Configuration B: it failed on scope, because the sessionKey was the chat window's ephemeral id (different every day), and it also failed on storage, because Simple Memory stores history in n8n's process RAM, which doesn't survive a restart and doesn't guarantee the same worker in queue mode.
Why it works: splitting the symptom ("doesn't remember") into its two possible causes — which identity you use and where the data lives — is the diagnosis you're going to repeat every time a production agent "loses memory" between one session and another.
Exercise 2 — Apply the criterion to a case of your own. Think of an agent you'd like to build (or one you already use as a customer). Does it need memory that survives only within a conversation, or memory that recognizes the same user days later? Justify your answer with this lesson's criterion, not with "because it would be better."
See solution
There's no single answer — it depends on the case. The right criterion is: is there a single real customer, stably identifiable (phone, email, account id), who interacts with the agent again at a different moment and expects the agent to connect both interactions? If the answer is yes, you need persistent memory with a stable per-customer sessionKey (Configuration C). If the agent only needs to hold the thread within a single continuous conversation — and never talks to that same user again in another session that matters to remember — session memory (Configuration B) is enough and is simpler to maintain.
Why it works: the criterion isn't "how much memory looks impressive," but whether there's a stable real-world identity the agent needs to recognize across different sessions.
Exercise 3 — The map without looking at it. Without looking at the previous section's table again, write from memory what problem each of the seven remaining lessons in this module (2 through 8) solves, one sentence each.
See solution
(2) The practical, live difference between an agent that forgets and one that holds the thread. (3) How the window of recent turns within a single session works. (4) How to give a stable per-customer sessionKey and store history in a persistent database. (5) How to design multi-turn conversations without the agent losing track. (6) What context drift is and why an agent degrades in very long conversations. (7) When to summarize history and reset the context. (8) The mini-project that assembles persistent per-user memory.
Why it works: if you could reconstruct the order without looking, you already have this module's progression internalized — from "is there memory or not?" to "whose memory is it and where does it live?" to "what do I do when there's too much of it?"
Summary and next step
In this lesson you saw that an agent with no memory connected processes every message as if it were the only one it has ever received, and that "connecting memory" doesn't solve the problem in one move: they're two independent decisions — what identity you use to scope the history, and where you store that history once it's grouped — as shown by the three configurations of the #4521 order. You also saw the map of the seven pieces waiting for you in this module, including a third problem — degradation in long conversations — that neither of the previous two decisions solves on its own.
Before moving on to lesson 2 you should be able to: explain in one sentence the difference between memory scope and memory storage, citing the #4521 order example; name, without looking at the table, at least four of the seven lessons ahead and what problem each one solves; and, given an agent that "doesn't remember" between two sessions, say which of the two decisions you'd suspect first.
That distinction — session memory versus no memory at all — is exactly what the next lesson turns into a live comparison: you're going to see, side by side, the same agent with and without memory connected, and why that difference is what separates a chatbot from a real assistant.
Resources
- How memory works — n8n Docs — the complete catalog of memory nodes in n8n: Simple Memory versus the persistent options (Postgres Chat Memory, Redis Chat Memory, Motorhead, Xata, Zep) you're going to use in lessons 3 and 4.
- Simple Memory node — n8n Docs — reference for the node you already used in Module 1: the
Session KeyandContext Window Lengthparameters, and the official warning against using it in queue mode because it doesn't guarantee the same worker across calls — the technical reason behind Configuration B's storage failure. - Postgres Chat Memory node — n8n Docs — reference for the node behind Configuration C: how it stores history in a real Postgres table, indexed by
Session Key. - Effective context engineering for AI agents — Anthropic — the article where Anthropic describes "context rot": the more accumulated history tokens, the more the model's ability to recall it accurately degrades — the research behind what this module's lesson 6 calls context drift.
- AI Agent node — n8n Docs — the same node reference you already used in Modules 1 and 2; confirms that
ai_memoryis the only one of the three special connections that's optional.