Conversation history
-
Introduction to generative AI and agents
-
Large language models (LLMs)
-
Prompts
-
AI agents
Chat with any AI assistant and it seems to remember you. You say your name once, and three questions later it still knows it. But in Lesson 8 you saw that a model only ever reads one prompt and continues it.
So where does the memory live? The answer is LLM chat history: your app keeps the conversation and sends all of it again, every single time.
In this lesson you’ll build a chat loop with and without history, watch the prompt grow turn by turn, and try the tricks real systems use to keep it under control. Then you’ll break it twice: once by running out of context window, and once by mixing up two users’ conversations.
| Time | You need | By the end you can |
|---|---|---|
| About 60 minutes | Python 3 and Ollama with llama3.2:1b | Explain why models are stateless, build a chat loop with history, measure how the prompt grows, choose a trimming strategy, and keep each user’s history separate |
What it is, in one line
Chat history is the list of earlier messages your app sends along with every new question, because the model itself remembers nothing between requests.
In Simple Words
A friend who forgets everything overnight
Imagine a very clever friend who forgets everything as soon as they finish answering. Every time you call, they pick up the phone with a blank mind.
So you keep a notebook. Every call, before asking your new question, you read out the whole notebook: “Last time I told you my name is Ravi. You told me to check the switch.
Then I asked about saving the settings…” Only then do you ask today’s question. Your friend sounds like they remember, but really, you reminded them.
The notebook gets longer every call, and reading it out takes longer every time. One day it’s so long you can’t read it all in one call, so you start skipping the oldest pages. That’s when your friend suddenly “forgets” your name.
The model is the friend, and the notebook is the chat history your app sends with each question.
Engineer's View
Stateless models, growing prompts
The model is stateless. Each call to /api/chat is independent. Nothing about your last request is stored in the model.
If you want the model to know what was said, you send it again, as a list of messages with the roles system, user and assistant (Lesson 8). Ollama joins them with the chat template into one prompt.
The prompt grows every turn. Turn 5 sends turns 1 to 4 again, plus the new question. So the prompt for turn n grows roughly in a straight line, and the total tokens across a whole conversation grow with roughly the square of the number of turns. A 50-turn conversation re-reads the first message 50 times.
Caching helps speed, not space. When the start of the prompt is the same as last time, Ollama (through llama.cpp) can reuse the work it already did for those tokens, so re-reading old history is faster than reading new text. But every token still takes room in the context window and in the KV cache (Lesson 4).
The context window. num_ctx is how many tokens the model can hold at once: the prompt plus the answer. llama3.2:1b supports up to 131,072, but Ollama picks a smaller default based on your graphics memory: 4,096 tokens with less than 24 GB, which covers almost every laptop. Run ollama ps while a model is loaded and the CONTEXT column shows the window it’s using. Bigger windows need more memory.
When the history doesn’t fit. Ollama’s chat endpoint drops the oldest messages until the prompt fits, keeping the system message and the newest message. It does this silently: no error, just a model that suddenly forgets.
Many hosted APIs reject the request with an error instead. Either way, your code should decide what to drop, not the server.
Ways to manage history:
- Full history: simple and accurate, until it gets too long or too slow.
- Sliding window: keep only the last N messages. Cheap and predictable, but early facts fall off the edge.
- Pinned facts: store the important facts (name, ticket number, preferences) separately and put them in the system message every time.
- Summary memory: when the history gets long, ask the model to summarise the older part into a short note, and send the note instead.
- Retrieval memory: save every message, and fetch only the ones relevant to the new question. That’s the idea behind RAG, the next lesson.
History is user data. It contains whatever people typed: names, ticket numbers, sometimes passwords. Keep each user’s history separate, decide how long to keep it, and don’t write it into general logs.
Why it matters on the job: chat history decides what a bot remembers, how fast it answers late in a conversation, how much each conversation costs, and whether one user can ever see another user’s words.
How it works, step by step
Here’s what your app actually sends on the first three turns of the lab conversation:
- Turn 1. The system message and Ravi’s first message. 55 tokens.
- The model answers. Your app appends the answer to the history as an
assistantmessage. The model has already forgotten it. - Turn 2. System message, turn 1, the answer, and the new question. The prompt is now about twice as long.
- Turn 3 and on. Every turn adds a question and an answer. The prompt keeps growing until something trims it: your code, or the context window.
And here’s how much the prompt grows over five turns with each strategy you’ll try in the lab:
With no history, every prompt is small, but the bot forgets everything. With full history, it remembers, and the prompt grows every turn.
The last four messages keep the prompt small, but turn 5 has already lost Ravi’s name. Pinning the facts in the system message costs 14 tokens per turn and brings the name back.
Memory in ChatGPT and other apps
When a chat app says it “remembers” you across conversations, it’s doing the same thing one level up: it saves facts about you and adds them to the prompt of new chats. The model underneath is still stateless.
Hands-on Lab: manage LLM chat history
You’ll run one script, chat.py, that plays a fixed five-turn conversation with llama3.2:1b. Ravi introduces himself, asks three questions about his switch, and then asks the bot what his name is.
You’ll change one setting at a time and watch two things: whether the bot remembers, and how many prompt tokens each turn costs. On macOS or Linux, use python3 and nano.
1Chat with no history
Run this:
mkdir C:\ai-lab\history
cd C:\ai-lab\history
notepad chat.pyPaste this whole block into chat.py, then save:
import json
import urllib.request
MODEL = "llama3.2:1b"
SYSTEM = "You are a helpful IT support assistant. Keep every answer to two sentences."
FACTS = "" # facts to pin into the system message
HISTORY = "none" # "none", "all" or "last4"
NUM_CTX = 4096 # the model's context window, in tokens
TURNS = [
"Hi, I'm Ravi. I look after the Hyderabad office network.",
"Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?",
"Thanks. How do I save the config after I change a port?",
"Which command shows how long the switch has been running?",
"What's my name, and which office do I look after?",
]
def ask(messages):
body = {"model": MODEL, "messages": messages, "stream": False,
"options": {"temperature": 0, "num_ctx": NUM_CTX}}
req = urllib.request.Request("http://localhost:11434/api/chat", data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
return json.load(r)
system = {"role": "system", "content": SYSTEM + (" " + FACTS if FACTS else "")}
history = [] # everything said so far, without the system message
total = 0
for turn, text in enumerate(TURNS, 1):
history.append({"role": "user", "content": text})
if HISTORY == "all":
sent = history
elif HISTORY == "last4":
sent = history[-4:]
else:
sent = history[-1:]
messages = [system] + sent
r = ask(messages)
reply = r["message"]["content"].strip()
history.append({"role": "assistant", "content": reply})
total += r["prompt_eval_count"]
print(f"Turn {turn} | messages sent: {len(messages):>2} | prompt tokens: {r['prompt_eval_count']:>4}")
print(f" You: {text}")
print(f" Bot: {reply}")
print(f"History: {HISTORY} | Total prompt tokens for {len(TURNS)} turns: {total}")With HISTORY = "none", each turn sends only the system message and the new question, the way a single API call works.
Run this:
python chat.pyYou should see something like this (with no history, the prompt tokens are exact):
Turn 1 | messages sent: 2 | prompt tokens: 55
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 2 | prompt tokens: 60
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 2 | prompt tokens: 54
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
Turn 4 | messages sent: 2 | prompt tokens: 51
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
Turn 5 | messages sent: 2 | prompt tokens: 53
You: What's my name, and which office do I look after?
Bot: I'm sorry, I don't know your name or which office you look after. Could you tell me?
History: none | Total prompt tokens for 5 turns: 273The bot greeted Ravi by name in turn 1, and four turns later it has no idea who he is. Nothing was remembered, because nothing was sent. Every prompt is about the same size, 51 to 60 tokens.
✅ Checkpoint: Turn 5 doesn’t know the name, and the total is 273 prompt tokens.
2Send the whole history
Change this line:
HISTORY = "all"Run this:
python chat.py
You should see something like this (the replies, and so the later token counts, will differ a little):
Turn 1 | messages sent: 2 | prompt tokens: 55
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 4 | prompt tokens: 104
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 6 | prompt tokens: 159
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
Turn 4 | messages sent: 8 | prompt tokens: 203
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
Turn 5 | messages sent: 10 | prompt tokens: 246
You: What's my name, and which office do I look after?
Bot: You're Ravi, and you look after the Hyderabad office network.
History: all | Total prompt tokens for 5 turns: 767Now the bot remembers. Look at the counts: messages sent goes 2, 4, 6, 8, 10, and the prompt grows from 55 to 246 tokens.
The whole conversation cost 767 prompt tokens, almost three times as much as step 1, for the same five questions. Turn 1 is exact; the later counts depend on how long the bot’s answers were.
✅ Checkpoint: Turn 5 knows Ravi’s name, and the prompt tokens grow every turn.
3Keep only the last four messages
Change this line:
HISTORY = "last4"Run this:
python chat.pyYou should see something like this (the replies, and so the later token counts, will differ a little):
Turn 1 | messages sent: 2 | prompt tokens: 55
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 4 | prompt tokens: 104
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 5 | prompt tokens: 139
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
Turn 4 | messages sent: 5 | prompt tokens: 134
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
Turn 5 | messages sent: 5 | prompt tokens: 122
You: What's my name, and which office do I look after?
Bot: I'm sorry, I don't know your name or which office you look after. Could you tell me?
History: last4 | Total prompt tokens for 5 turns: 554A sliding window. After turn 3, the prompt stops growing, and the total drops to 554.
But turn 5 has forgotten Ravi again: his introduction was more than four messages ago, so it fell off the edge. Cheap, but the oldest facts are exactly the ones a window throws away.
✅ Checkpoint: The prompt tokens stop growing after turn 3, and turn 5 forgets the name.
4Pin the facts that matter
Keep the window, and put the important facts in the system message, where they’re sent every turn:
Change this line:
FACTS = "The user is Ravi, who looks after the Hyderabad office network."Run this:
python chat.pyYou should see something like this (the replies, and so the later token counts, will differ a little):
Turn 1 | messages sent: 2 | prompt tokens: 69
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 4 | prompt tokens: 118
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 5 | prompt tokens: 153
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
Turn 4 | messages sent: 5 | prompt tokens: 148
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
Turn 5 | messages sent: 5 | prompt tokens: 136
You: What's my name, and which office do I look after?
Bot: You're Ravi, and you look after the Hyderabad office network.
History: last4 | Total prompt tokens for 5 turns: 624The best of both: the prompt stays small, and the bot knows who Ravi is. The facts cost 14 tokens on every turn, which is nothing compared with re-sending a long conversation. Real systems fill this in automatically, for example from the user’s login, the open ticket, or a summary the model wrote of the older messages.
✅ Checkpoint: With FACTS set, turn 5 knows the name again, and the first prompt is 14 tokens bigger: 69 instead of 55.
Break It, Then Fix It
Two failures from real chat systems. The first loses memory without telling anyone. The second shares memory with the wrong person.
1Break it: the context window fills up
Go back to full history, remove the facts, and shrink the context window. 200 tokens is tiny on purpose, so you hit the limit in five turns instead of fifty:
Change these three lines:
FACTS = ""
HISTORY = "all"
NUM_CTX = 200Run this:
python chat.pyYou’ll see something like this:
Turn 1 | messages sent: 2 | prompt tokens: 55
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 4 | prompt tokens: 104
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 6 | prompt tokens: 159
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
Turn 4 | messages sent: 8 | prompt tokens: 183
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
Turn 5 | messages sent: 10 | prompt tokens: 177
You: What's my name, and which office do I look after?
Bot: I'm sorry, I don't know your name or which office you look after. Could you tell me?
History: all | Total prompt tokens for 5 turns: 678No error anywhere, and yet the bot forgot Ravi. Look at turns 4 and 5: the script sent 8 and then 10 messages, but the prompt was only 183 and then 177 tokens, against 203 and 246 in step 2. It even shrank.
Ollama quietly dropped the oldest messages to make the prompt fit, and Ravi’s introduction was the first to go. In a real bot this happens after a long conversation, and it looks like the model got confused.
The fix: first, make it visible. Add these two lines to the loop, right after the line print(f" Bot: {reply}"), with the same indent:
Add these two lines:
if r["prompt_eval_count"] > 0.75 * NUM_CTX:
print(f" WARNING: prompt uses {r['prompt_eval_count'] / NUM_CTX:.0%} of the context window. Old messages may be dropped.")Run this:
python chat.pyYou’ll see something like this:
Turn 1 | messages sent: 2 | prompt tokens: 55
You: Hi, I'm Ravi. I look after the Hyderabad office network.
Bot: Nice to meet you, Ravi. How can I help with the Hyderabad office network today?
Turn 2 | messages sent: 4 | prompt tokens: 104
You: Our main switch is a Cisco Catalyst 9300. How do I see which ports are down?
Bot: Run show interfaces status and look for ports marked notconnect or err-disabled. For more detail on one port, use show interfaces followed by the port name.
Turn 3 | messages sent: 6 | prompt tokens: 159
You: Thanks. How do I save the config after I change a port?
Bot: Use copy running-config startup-config, or the shorter write memory. Both save your changes so they survive a reboot.
WARNING: prompt uses 80% of the context window. Old messages may be dropped.
Turn 4 | messages sent: 8 | prompt tokens: 183
You: Which command shows how long the switch has been running?
Bot: Run show version. The uptime line near the top tells you how long the switch has been running.
WARNING: prompt uses 92% of the context window. Old messages may be dropped.
Turn 5 | messages sent: 10 | prompt tokens: 177
You: What's my name, and which office do I look after?
Bot: I'm sorry, I don't know your name or which office you look after. Could you tell me?
WARNING: prompt uses 88% of the context window. Old messages may be dropped.
History: all | Total prompt tokens for 5 turns: 678Now your logs say what happened. Then fix the cause: give the model a sensible window with NUM_CTX = 4096, and manage the history yourself with a window and pinned facts, so you choose what gets dropped. With NUM_CTX = 4096 the warnings disappear and turn 5 remembers Ravi again.
✅ Checkpoint: With NUM_CTX = 200 the warning appears and the bot forgets. With 4096, no warnings, and it remembers.
2Break it again: one history for everybody
A web developer turns the chat loop into a help desk page. They keep the history in one list at the top of the file, and test it alone.
It works. Then two people use it at the same time:
Run this:
notepad app.pyPaste this whole block, then save:
import json
import urllib.request
MODEL = "llama3.2:1b"
SYSTEM = "You are a helpful IT support assistant. Keep every answer to two sentences."
HISTORY = [] # one list for everybody
def ask(messages):
body = {"model": MODEL, "messages": messages, "stream": False, "options": {"temperature": 0}}
req = urllib.request.Request("http://localhost:11434/api/chat", data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
return json.load(r)["message"]["content"].strip()
def handle(user, text):
HISTORY.append({"role": "user", "content": text})
reply = ask([{"role": "system", "content": SYSTEM}] + HISTORY)
HISTORY.append({"role": "assistant", "content": reply})
print(f"[{user}] {text}\n Bot: {reply}")
# Two people use the help desk web page at the same time
handle("ravi", "Hi, I'm Ravi from the Hyderabad office. My VPN token stopped working.")
handle("anita", "Hello! Do you know my name and where I work?")Run this:
python app.py
The text version of the first run:
[ravi] Hi, I'm Ravi from the Hyderabad office. My VPN token stopped working.
Bot: Sorry to hear that, Ravi. Try resyncing the token in the VPN app, and if it still fails, ask the help desk to issue a new one.
[anita] Hello! Do you know my name and where I work?
Bot: Yes, you're Ravi from the Hyderabad office. Is your VPN token working again?Anita has never met this bot, and it greets her as Ravi and asks about his VPN token. Both users share one HISTORY list, so Anita’s question was sent together with Ravi’s conversation. In a real company that’s a privacy incident: one employee seeing another’s name, problem, or worse.
The fix: keep one history per user (or per chat session). Replace the HISTORY = [] line and the handle function with this:
Replace them with this:
HISTORIES = {} # one list per user
def handle(user, text):
history = HISTORIES.setdefault(user, [])
history.append({"role": "user", "content": text})
reply = ask([{"role": "system", "content": SYSTEM}] + history)
history.append({"role": "assistant", "content": reply})
print(f"[{user}] {text}\n Bot: {reply}")Run this:
python app.pyYou should see something like this:
[ravi] Hi, I'm Ravi from the Hyderabad office. My VPN token stopped working.
Bot: Sorry to hear that, Ravi. Try resyncing the token in the VPN app, and if it still fails, ask the help desk to issue a new one.
[anita] Hello! Do you know my name and where I work?
Bot: I don't know your name or where you work yet. Could you tell me?Now each user has their own notebook. In a real web app, the key is the signed-in user or a random session ID, never something the user can type or guess.
✅ Checkpoint: With HISTORIES, Anita’s bot doesn’t know Ravi exists.
This happened at work
The chat that never ended
A company added an AI helper to its IT self-service portal. It kept the full chat history for each browser tab, which seemed sensible. Response times were great in testing.
A few weeks after launch, the team noticed the GPU server running hot every afternoon, and answers taking 15 to 20 seconds. The logs showed why.
Many staff opened the helper in the morning and left the tab open all day, asking a question every hour or so. By afternoon, some conversations had 120 messages, and every new question re-sent all of them: over 20,000 prompt tokens to answer “how do I reset my printer?”
Worse, some users complained that the helper “forgot” their ticket number. Their conversations had passed the context window, and the oldest messages, including the ticket number they’d given at the start, were being dropped.
The fix: a new conversation after 30 minutes of inactivity, a window of the last 10 messages, and the ticket number, user name and device pinned in the system message from the ticketing system, so they could never fall off. Average prompt size dropped by over 90%, and the afternoon slowdowns stopped.
The lesson: “keep everything” is not a memory strategy. Decide what the bot must always know, pin it, and let the rest go on purpose.
Common mistakes and troubleshooting
| Mistake | What happens | How to avoid it |
|---|---|---|
| Thinking the model remembers between calls | The bot “forgets” everything | Keep the history in your app and send it with each request |
| Sending the full history forever | Slow, expensive replies late in long chats | Use a sliding window, summaries or pinned facts |
| Letting the server trim the history for you | Important early facts vanish without an error | Trim in your own code and warn when the prompt nears num_ctx |
| Keeping one history for all users | One user sees another user’s conversation | Store history per signed-in user or session ID |
| Forgetting to append the model’s replies | The bot repeats itself or loses the thread | Append every reply as an assistant message before the next turn |
| Raising num_ctx without checking memory | Out-of-memory errors or a very slow model | The KV cache grows with the window (Lesson 4); size it for real use |
| Storing chat history forever, in plain logs | Personal data and secrets leak into places they shouldn’t be | Set a retention period and keep history out of general logs |
Troubleshooting the lab
| Problem | Fix |
|---|---|
urllib.error.URLError: <urlopen error [WinError 10061] | Ollama isn’t running. Start the Ollama app, or run ollama serve in another window |
| The first run after changing NUM_CTX is slow | Ollama reloads the model with the new context size. Later runs are fast again |
| Turn 5 remembers the name even with HISTORY = “none” | Check the setting is exactly “none”. With no history the model can’t know the name |
| Your prompt token counts differ after turn 1 | Expected: they include the bot’s earlier answers, which vary a little. With HISTORY = “none” they should match exactly |
| No WARNING lines appear with NUM_CTX = 200 | The two lines must be inside the loop, indented by 4 spaces like the print lines above them |
NameError: name 'HISTORY' is not defined in app.py | You replaced the list but not the handle function. Replace both |
Interview questions
Click a question to open it. Under each one you’ll find what the interviewer is actually looking for, and one good way to answer.
Fresher level
1. How does a chatbot remember what you said earlier?
What they’re really checking: Do you know the model is stateless?
How you could answer: The model doesn’t remember anything between requests. The application keeps the conversation as a list of messages and sends the whole list, or part of it, with every new question. The model reads it all again each time.
2. What is a context window?
What they’re really checking: Do you know the limit and what it includes?
How you could answer: It’s the maximum number of tokens the model can handle in one go, including the prompt and the answer it writes. If the conversation is longer than that, some of it has to be dropped or summarised.
3. Why does a long chat get slower?
What they’re really checking: Can you connect history to cost?
How you could answer: Because every new message re-sends the whole conversation, so the prompt the model has to read keeps growing. More prompt tokens means more work per reply, more memory, and higher cost.
2–5 years experience
4. What strategies do you know for managing chat history?
What they’re really checking: Do you know more than one approach and their trade-offs?
How you could answer: Full history for short chats; a sliding window of the last N messages; pinned facts in the system message; summarising older messages into a short note; and retrieval, where all messages are stored and only relevant ones are fetched. I’d usually combine a window with pinned facts, and add summaries for long sessions.
5. What happens in Ollama when the conversation is longer than num_ctx?
What they’re really checking: Have you looked at what the server actually does?
How you could answer: The chat endpoint drops the oldest messages until the prompt fits, keeping the system messages and the newest message. It doesn’t return an error, so the model just appears to forget. That’s why I trim in my own code and log a warning when the prompt gets close to the limit.
6. How would you store chat history in a multi-user web application?
What they’re really checking: Do you think about isolation and privacy?
How you could answer: Keyed by the authenticated user and a server-generated session ID, never by anything the client can choose, in a store with access controls and a retention period. History often contains personal data, so I’d keep it out of general application logs and support deletion on request.
Senior level
7. How do summary memory and retrieval memory differ, and when would you use each?
What they’re really checking: Can you design memory for real products?
How you could answer: Summary memory compresses older turns into a short note that’s always sent, which keeps the overall story but loses detail and can drift if summaries are summarised again. Retrieval memory stores all messages and fetches only the ones relevant to the new question, which keeps details but can miss context the search doesn’t find. Long support sessions suit a summary plus pinned facts; assistants that need to recall specific past details suit retrieval.
8. Prompt caching is on. Does that mean long histories are free?
What they’re really checking: Do you understand what caching saves and what it doesn’t?
How you could answer: No. Caching saves recomputing the shared prefix, so latency and compute drop, but the tokens still occupy the context window and the KV cache memory, and hosted APIs usually still bill them, often at a discount. It also only works if the prefix is identical, so editing early messages or the system prompt breaks the cache.
9. Users say the assistant “forgets” things late in conversations. How do you investigate?
What they’re really checking: Can you debug memory problems systematically?
How you could answer: Log the number of messages and prompt tokens sent per turn and compare them with the context window. If the prompt is near the limit, messages are being dropped.
Then check what my own trimming removes, whether replies are appended correctly, and whether key facts are pinned. I’d fix it with explicit trimming, pinned facts or summaries, and a test conversation long enough to hit the limit.
Quick quiz
Q1. In step 1, why didn’t the bot know Ravi’s name at turn 5?
- The model was too small
- Only the new question was sent, so the earlier messages never reached the model
- Temperature was 0
Show answer
B — The model is stateless. It only knows what’s in the current prompt.
Q2. With HISTORY = “all”, what happened to the prompt tokens?
- They stayed the same
- They grew every turn, from 55 to about 246
- They went down
Show answer
B — Every turn re-sends all earlier messages plus the new one.
Q3. Why did “last4” forget the name?
- The introduction was more than four messages back, so it wasn’t sent
- Ollama deleted it
- The facts were wrong
Show answer
A — A sliding window throws away the oldest messages, even important ones.
Q4. With NUM_CTX = 200, the script sent more messages but the prompt got smaller. Why?
- The model compressed them
- Ollama silently dropped the oldest messages to fit the window
- The answers were shorter
Show answer
B — Ollama’s chat endpoint trims from the front, keeping the system message and the newest message.
Q5. What was the bug in app.py?
- Temperature was too high
- One history list was shared by all users
- The system message was missing
Show answer
B — Each user or session needs its own history.
Quick recap
- The model is stateless. Chat memory is your app re-sending the conversation every turn.
- The prompt grows every turn: 55 to about 246 tokens over five turns in the lab, and far more in long chats.
- num_ctx caps it. When the history doesn’t fit, Ollama silently drops the oldest messages.
- Manage it yourself: a sliding window, pinned facts, summaries, or retrieval.
- Keep one history per user, and treat it as personal data.
Next lesson
Retrieval augmented generation (RAG) → Pinned facts work for a name and a ticket number, but not for a 300-page policy manual. Next you’ll build RAG: search your own documents and put only the relevant pieces into the prompt.
Further reading: LLM chat history
Want to go deeper into LLM chat history and context windows? Check the official references: the Ollama API documentation for the chat endpoint and its messages, and the Ollama context length guide for num_ctx. You can also follow the full Generative AI and Agents course on SaatVedha, step by step.
