Build a Real AI Agent · Part 2
Five Lines to Talk to a Model (and Why It Forgets You)
Part 2 of 4Build a Real AI Agent
- Your AI Can’t Check the Weather (Yet): What Is an AI Agent?
- Five Lines to Talk to a Model (and Why It Forgets You)
- Why AI Types Word by Word
- The Model Has No Hands
Before an agent can act, it has to talk. So step one is the smallest program that sends a message to a model and prints the reply. But first, a place to run it.
Set up your lab (about 10 minutes, once)
You need two tools. That's the whole lab.
- uv manages Python for you: it installs
Python, creates the project, and adds packages. One tool instead of
pythonvenv+pip.
- Ollama runs AI models on your own machine. Free, no account, no API key.
1. Install uv
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Close and reopen your terminal, then check: uv --version.
2. Install Ollama and pull a model
Download Ollama from ollama.com/download
(macOS and Windows), or on Linux run
curl -fsSL https://ollama.com/install.sh | sh. Then:
ollama pull qwen2.5 # about 4.7 GB, one time
ollama run qwen2.5 "Say hi in five words"
If it answers, your model works.
3. Create the project
uv init agent-lab
cd agent-lab
uv add ollama
uv init makes the folder and a pyproject.toml; uv add installs the
ollama Python package into the project's own environment. You don't need to
install Python first: uv fetches it if it's missing.
You'll run every example in this series the same way: save the code as a file
inside agent-lab, then uv run hello.py.
New to the terminal, uv or Git? Two free guides on this site walk through it step by step: Git & uv Quick Start (16 pages) and The AI Agent Developer's Toolbelt (terminal, uv, Ollama, Git, secrets and more).
Hello, model
Save this as hello.py:
import ollama
messages = [
{"role": "system", "content": "You are a friendly assistant. Keep answers short."},
{"role": "user", "content": "Tell me a very short joke about AI."},
]
response = ollama.chat(model="qwen2.5", messages=messages)
print(response.message.content)
Run it with uv run hello.py. You'll get something like:
Why did the AI break up with the calculator? It felt like it was being taken for granted — every single time.
Okay, the joke is bad. But look at what just happened: your Python code had a conversation with an AI. No website, no account, no magic.
A conversation is just a list
The most important thing on that page is not the chat call. It's this:
messages = [ {...}, {...} ]
A conversation, to a model, is a plain Python list of messages. Each
message has a role and some content:
| Role | Who's speaking | Example |
|---|---|---|
system | You, the developer — setting the rules | "Keep answers short." |
user | The human | "Tell me a joke." |
assistant | The model's earlier replies | "Why did the AI…" |
That's it. Every chat app you've ever used — however shiny — is building a list like this behind the scenes and sending it to a model.
The experiment that surprises everyone
Let's have a two-message conversation. Two separate calls:
import ollama
ollama.chat(model="qwen2.5", messages=[
{"role": "user", "content": "Hi! My name is Sara."},
])
reply = ollama.chat(model="qwen2.5", messages=[
{"role": "user", "content": "What's my name?"},
])
print(reply.message.content)
The answer?
I'm sorry, I don't know your name. Could you tell me?
Sara told it her name one second ago. Gone.
The model is stateless
This is the single most important fact about LLMs, and almost nobody tells beginners:
The model remembers nothing between calls.
Every call is a brand-new brain that has never met you. The only thing it knows is the list you send it right now.
So how does ChatGPT remember what you said three messages ago? Simple: the app sends the whole conversation again, every single time. Let's do the same:
import ollama
messages = [{"role": "user", "content": "Hi! My name is Sara."}]
first = ollama.chat(model="qwen2.5", messages=messages)
messages.append(first.message) # keep the model's reply
messages.append({"role": "user", "content": "What's my name?"})
second = ollama.chat(model="qwen2.5", messages=messages)
print(second.message.content) # "Your name is Sara!"
Now it "remembers." But notice: the model didn't remember anything. Your list did. You are the memory.
Hold on to that idea. It comes back in a big way later in the series, when we give our agent a memory that survives even after you close the program.
Three things to keep
- A conversation is a list of messages with roles.
- The model is stateless — it forgets everything between calls.
- "Memory" in a chat is just your code sending the history again.
What's next
Our program works, but it's awkward: you stare at a blank screen until the whole answer arrives at once. Real AI chats type word by word. In the next article, we find out why — and make ours do it too, with one tiny change.
Comments
Loading comments…