Building your first agent
Memory in an agent is just a Python list that accumulates messages. Every user message, every assistant response, all stored in order. When you call the LLM, you send the entire list.
That simplicity is exactly the point. No database, no vector store, just a Python list. The complexity comes later when conversations get long and you need strategies to manage the list size. But the core idea never changes.
class SimpleAgent:
def __init__(self, system_message=None):
self.memory = []
if system_message:
self.memory.append(
{"role": "system", "content": system_message}
)
def respond(self, user_message: str) -> str:
# 1. Add user message to memory
self.memory.append(
{"role": "user", "content": user_message}
)
# 2. Send FULL history to LLM
response = completion(
model=DEFAULT_MODEL,
messages=self.memory
)
assistant_msg = response.choices[0].message.content
# 3. Store assistant response in memory
self.memory.append(
{"role": "assistant", "content": assistant_msg}
)
return assistant_msgThe SimpleAgent class: memory is a list, respond() appends messages and sends the full history.
Let us take the SimpleAgent for a spin. We will create one with a travel advisor persona and test multi-turn conversation.
# Create an agent with a persona
agent = SimpleAgent(
system_message="You are a helpful travel advisor."
)
# Multi-turn conversation
print(agent.respond("My name is Alex."))
# "Nice to meet you, Alex! Where would you like to travel?"
print(agent.respond("I love warm beaches."))
# "Great taste! I'd recommend Bali or the Maldives..."
print(agent.respond("What's my name and what do I like?"))
# "Your name is Alex and you love warm beaches!"The agent remembers context across multiple turns because it sends the full history each time.
It's an illusion! The LLM itself still has no memory. What happens is your Python code stores every message in a list. On each call, the entire list is sent to the model. The LLM reads the full conversation from scratch and generates a contextually appropriate response. The memory lives in your code, not in the model.
How agent memory works
Each turn, the full message history is sent to the LLM. The memory list grows with every interaction.
The system message is special: it sets the persona and rules for the agent. It is always the first message in the memory list and is sent with every call. Think of it as the agent's "personality configuration."
AI prompt: Try it with AI
Loading practice…
Fill in the blanks: Complete the agent's respond() Method
Loading practice…
Quiz: Quiz
Loading practice…
# Peek inside the agent's memory after 3 turns
for msg in agent.memory:
print(f"[{msg['role']}]: {msg['content'][:60]}...")
# [system]: You are a helpful travel advisor...
# [user]: My name is Alex...
# [assistant]: Nice to meet you, Alex!...
# [user]: I love warm beaches...
# [assistant]: Great taste! I'd recommend Bali...
# [user]: What's my name and what do I like?...
# [assistant]: Your name is Alex and you love warm beaches!...The memory list contains every message in order: system, user, assistant, repeating.
You might have noticed the problem: this memory list grows forever. After 100 turns, you're sending 100+ messages with every call. That's expensive and eventually hits the context window limit. We'll solve this later, with sliding window and summarization strategies.
You just built your first AI agent! The key insight: memory is just a Python list. The LLM is stateless, but your code creates the illusion of statefulness by replaying history. Next, let's test your understanding.