Make it remember you.It forgets everything.

Prove it forgets.

New here: no new code. We replay the agent.py you ended lesson 2 with, to see the problem.

Two quick questions from last time. Guessing first helps it stick, even when it feels harder.

A real run on our PC (qwen3:8b), recorded . Your own run will word the answers differently.

The head keeps nothing.

New here: no new code. This step shows why step 1 happened.

the head in its jar. A note goes in, it answers, and the note is gone. Drag the jar to turn it.
My name is Ana. What is my name? ? no idea the head two separate trips

Keep a list outside the loop.

New here: a list kept outside the loop, so it survives from one message to the next.

Three changes to agent.py. The list is called messages in the code. The loop is while True: it repeats the lines under it for every message you type. Each box shows the whole file; only the yellow lines are new, and the rest is lesson 2's file. The drawing on the right builds the list as you read each change.

First, create the list once, above the loop, with the system message already inside it.

import json
import urllib.error
import urllib.request

SYSTEM = "You are a burned-out morgue attendant. Answer in one grumpy sentence."
messages = [{"role": "system", "content": SYSTEM}]

while True:
    prompt = input("You: ")
    if prompt == "quit":
        break

    body = {
        "model": "qwen3:8b",
        "messages": [{"role": "system", "content": SYSTEM}, {"role": "user", "content": prompt}],
        "stream": False,
        "think": False,
    }

    URL = "http://localhost:11434/api/chat"
    note = json.dumps(body).encode()
    request = urllib.request.Request(URL, note, {"Content-Type": "application/json"})

    try:
        with urllib.request.urlopen(request) as response:
            answer = json.load(response)
    except urllib.error.URLError as error:
        raise SystemExit(f"Could not reach Ollama ({error}). "
                         "If it is not running, open another terminal and run: ollama serve")

    print(answer["message"]["content"])
messages = [ ... ]
messages is a variable. Square brackets make a list: things in order that you can add to later.
{"role": "system", "content": SYSTEM}
Curly brackets make a dictionary: labelled values, like a JavaScript object. role says who is speaking and content says what they said. SYSTEM is the text from lesson 2 that sets the character.
position 0
Python counts from 0, so the first item in a list sits at position 0.
the old list in body
Further down, body still builds its own one-message list. Change 3 removes it.
"stream": False and "think": False
Lesson 1's settings: one whole reply, no private notes. You never change them.
note
In the code, note is lesson 1's request on its way to Ollama. It is not one of the notes in the drawing, which are messages.
above the loop
This line runs once. Inside while True it would start a fresh list every time round, and the memory would vanish again.

messages role: "system" content: SYSTEM 0 role: "user" content: prompt = "My name is Ana." 1 added at the end to the head "messages": messages,

Keep its words too.

New here: the head's own answers go into the list too.

Every note in the list has a role, a label for who said it. system is the standing orders from lesson 2, user is you, and assistant is the head's own earlier replies. So far the list holds only the first two. After the print line, add the replies too. This is the whole of agent.py at the end of lesson 3; only the yellow line is new.

Talk to it

Press the button to send each message. Each press is one call. The jar shows what goes in on that call: your whole list, and nothing stays in the head between calls.

When all three are sent, tick the yellow box, no list (lesson 2's code). It runs the same three calls with nothing kept, like step 1.

the head. The notes are the list your code sent on this call. Its memory is that list, in your code.

The messages list in your code, right now:

A real run on our PC (qwen3:8b), recorded , replayed here.

The list only grows.

New here: no new code. This step shows what the list costs.

A real run on our PC (qwen3:8b): the tokens the head read on each call, as Ollama reported them, for the same three messages.

That's lesson 3.

The head remembers nothing between messages. Your code gives it a memory by keeping the conversation in a list and sending the whole list every time.

That list grows with every message. What happens when it gets too big is lesson 4.

Messages
The list your code keeps and sends to the head on every call: the head's only memory.
Conversation history
The same list, seen as the whole chat so far.
Role
The label on each message that says who said it: system (the standing orders), user (you) or assistant (the head's own replies).
Call
One trip: your code sends the list, and the head answers.
Token
A piece of text, often part of a word; the head reads and writes text in tokens. Lesson 0's 112-word paragraph became 131 tokens. Lesson 4 is about how many fit.