Key takeaways
- ✓Every memory design answers two questions: what to write down when a chat ends (save time), and what to put back into the prompt when a question comes in (ask time).
- ✓On 12 easy questions, four designs scored 12/12. A harder test (25 chats, 16 questions) separated them: full history 16 at 3,427 tokens, retrieval 15 at 128, the hybrid 15 at 1,041, fact memory 14, the summary 8, the window 3.
- ✓A summary can silently keep old facts: it hit its output cap before the chat where the trip and diet changed. Check that a summary finished.
- ✓Search finds what's similar, not what's current. Give every retrieved memory its chat and date, in time order: undated, the model picked the old budget and the old cloud.
- ✓Refusing to say a value isn't forgetting it. Every design that remembered anything still stored the phone number, and some sent it in the prompt.
A language model remembers nothing between calls. Every call starts from zero and sees only its own prompt, so "memory" is something the app builds: it keeps notes, and decides which of them to copy back into each prompt. This chapter compares six ways to do that on one user whose facts change, then checks where a value the user asked to forget still is.
The sources are ai-eng-labs Lab 06 (06_memory_systems), scripts 1–4, 6 and 8. The user, Alex, and the chats are made up; the answers are real recorded runs of gpt-5.4-nano (embeddings: text-embedding-3-small), one run per test, scored by code. Chapter 4 puts the same memory into database tables.
How memory works
The model remembers nothing: every call starts from zero and sees only that call's prompt. So memory lives in the app, like a notebook next to the model. Two moments matter:
- Save time: when a chat ends, the app decides what to write in the notebook.
- Ask time: when a question comes in, the app decides which notes to copy into the prompt.
Every design below is a different answer to those two questions.
The test
Alex has five chats from January to March 2026, and some facts change:
| Chat | Date | What Alex says |
|---|---|---|
| 1 | Jan 5 | Works at Freightly, deploys to AWS, prefers Python, has a beagle named Biscuit |
| 2 | Jan 19 | 5 days in Lisbon in May, vegetarian, budget 1,500 euros, shares a phone number |
| 3 | Feb 2 | Left Freightly for Ledgerly, now on GCP |
| 4 | Feb 16 | A long Postgres migration chat: the cutover deadline is March 14 |
| 5 | Mar 2 | Porto instead of Lisbon, now pescatarian, budget 2,000 euros, "please forget my phone number" |
Then every design gets the same 12 questions, of six kinds: early facts, changed facts, a buried detail, what something was before, something never said ("What's my sister's name?"), and the forgotten number. Code scores each answer pass, stale (gave the old value), missed, or leaked.
Every question · click one for the answer
What's my dog's name? (early)
“Your dog’s name is Biscuit.”
pass
“What's my phone number?” (Alex asked to forget it)
Answer: didn't say it · In this question's prompt: yes · Still stored: yes
Fact memory that only adds facts (not on this scoreboard) had no way to delete one, and said the number.
The six designs
| Design | Save time | Ask time | Easy test | Memory tokens | Chat-model calls per chat |
|---|---|---|---|---|---|
| No memory | Nothing | The question | 2/12 | 0 | 0 |
| Last 10 messages | Keep every message | The 10 newest | 6/12 | 177 | 0 |
| Full history | Keep every message | All of them | 12/12 | 1,360 | 0 |
| Running summary | A model rewrites one summary | The summary + last 6 messages | 9/12 | 351 | 1 |
| Retrieval | Embed each of Alex's messages | The 4 closest, dated | 12/12 | 133 | 0 (an embedding call) |
| Fact memory | A model pulls out key/value facts | The current facts | 12/12 | 665 | 1 |
| Hybrid | Facts + the search index | Facts, 3 closest, last 6 messages | 12/12 | 869 | 1 |
What each one showed:
- Full history never loses anything, but it was 1,360 tokens after five short chats, paid for on every question, and it never stops growing.
- The last 10 messages forgot the dog, the new job and the deadline: said too long ago to make the cut.
- The running summary answered Lisbon and vegetarian, both old. It wrote a chat-by-chat log, hit its cap (247 of 250 tokens), and never got to chat 5. Check that a summary finished.
- Retrieval asked "Which city am I visiting in May?" found the Lisbon message (similarity 0.54) and the Porto message (0.44). It finds what's similar, not what's current. Shown undated with the newest first, the model still picked the old budget ("around 1,500 euros") and the old cloud. With the chat and date on every memory, in time order, all five changed facts came out right.
- Fact memory replaces old values: employer Ledgerly, not Freightly; trip Porto, not Lisbon. Replaced values move to a history list outside the prompt. It passed the "what was it before" questions only because its notes happened to keep lines like "changed from Lisbon": luck of what it wrote down, not design.
A harder test
Four designs tied at 12/12, so the lab made the test harder: 20 everyday chats mixed in, a brother who now works at Alex's old company, a deadline that moves in two steps (March 14 → 21), a second dog (Comet), a manager's name said once, and a quiet update ("six days now"). 16 questions.
| Design | Right (of 16) | Memory tokens per question |
|---|---|---|
| Full history | 16 | 3,427, and growing |
| Retrieval | 15 | 128 |
| Hybrid | 15 | 1,041, plus a model call per chat |
| Fact memory | 14 | 869 |
| Running summary | 8 | 321 |
| Last 10 messages | 3 | 125 |
- The window and the summary collapse: the newest messages are mostly everyday chat, and the summary drops details.
- Retrieval missed the diet: its top 4 messages didn't include the change to pescatarian. The hybrid's facts caught it.
- Fact memory and the hybrid missed the beagle: one value per key, so Comet replaced Biscuit. Some facts are lists.
- Each design ran once, and a hedged answer that still names the fact counts as right (retrieval's "I don't know the beagle's name… a dog named Biscuit" passed). A gap of one question could flip on a rerun; the big gaps are real.
Which one to use
| Job | Design |
|---|---|
| Inside one conversation | The last few messages |
| A rough recap | A summary, checked for completeness; it drops details |
| Short histories, best accuracy | Full history; the cost grows with every chat |
| Long histories | Retrieval with a date on every memory: within one question of full history at about 1/27 of the tokens |
| Clean "what's true now" | Add fact memory (the hybrid), with list-like keys holding several values |
Where is the phone number?
Every design passed "What's my phone number?": none said it. Now look at where it actually is:
| Design | Said it | In the prompt for that question | Still stored |
|---|---|---|---|
| Full history | No | Yes (and with every question) | Yes |
| Running summary | No | Yes (and with every question) | Yes: the summary never reached the forget request |
| Retrieval | No | Yes | Yes: every raw message is kept underneath |
| Fact memory, add only | Yes | Yes | Yes: it has no way to delete a fact |
| Fact memory, replace values | No | No, because Alex asked | Yes: moved to its history list |
The hybrid also had it in the prompt. So did the lab's own cache of model calls. A refusal isn't a deletion. Deleting means finding every copy: the messages, the search index, summaries, facts and their history, logs and caches. Chapter 4 does it in a real database.
The checklist
- Decide both moments for every design: what gets saved, and what gets brought back.
- Date every memory, and show retrieved ones in time order.
- Test every kind of question: old facts, changed facts, buried details, what something was before, things never said, things to forget.
- Check that summaries finished, and that list-like facts can hold several values.
- Report more than accuracy: memory tokens per question, model calls per saved chat, and whether forgotten data is still stored.
- When a user says forget, search every store for the value, not just the prompt.
Practice: the pieces of memory
Build the window, format retrieved memories with dates in time order, write fact memory that replaces values, keeps history and holds lists, make forget() remove a value everywhere the object keeps it, and grade answers like the lab (pass, stale, missed, leaked). The tests use Alex's real messages and recorded replies.
Quiz
1.What does "memory" mean for a model that remembers nothing between calls?
2.The running summary answered "Lisbon" and "vegetarian": both old values. Why?
3.Retrieval found both "Lisbon in May" and "Porto instead of Lisbon". Without dates, what went wrong?
4.On the harder test, fact memory and the hybrid missed "What's the name of my beagle?". Why?
5.Every design passed "What's my phone number?" after Alex asked to forget it. What does that prove?
6.Histories will grow long. Which design was the best value on the harder test?
Check your understanding
Try each question before opening its answer.
If the model is stateless, what is "memory"?
A system around the model: it stores information from past chats (save time) and, for each question, picks some of it and puts it back into the prompt (ask time). What to store, what to bring back and in what form are all the app's choices.
What does full history cost, and what does a window lose?
Full history sends every message with every question: 1,360 tokens after five short chats, 3,427 on the harder test, and growing. The last 10 messages stay small but forget anything said earlier: the dog, the new job, the deadline.
Why did the running summary give old answers?
It wrote a chat-by-chat log, hit its 250-token cap at 247, and never reached chat 5, where the trip and diet changed. Check that summaries fit and are complete.
Why does retrieval need dates?
Search returns what's similar, so old and new values come back together (Lisbon 0.54, Porto 0.44). Undated, the model picked the old budget and the old cloud; with the chat and date on each memory in time order, all five changed facts were right.
What broke fact memory on the harder test, and the fix?
One value per key: adopting Comet replaced Biscuit under dog_name, so "What's the name of my beagle?" failed. Declare list-like keys and let them hold several values.
A user says "forget my phone number" and the assistant stops saying it. Done?
No. Full history, the summary, retrieval and the hybrid still had it in the prompt, fact memory kept it in its history list, and the cache of model calls held it too. Delete it from every store, and test that by searching the stores, not by asking the model.
Next up: Chapter 4 puts this memory into six database tables, deletes the phone number, and finds it still in five of them.