Learning outcomes
- Separate persistence from learning
- Classify memory types
- Track provenance and time
- Store memories in SQLite
- Retrieve relevant context
- Restore state safely
1. What does it mean to remember?
Adam finds water at (7, 2), the program closes and the next session begins tomorrow. Ollama does not automatically retain that experience. The application must store it and deliberately retrieve it when it becomes relevant.
- Persistence means data survives process shutdown.
- Memory means an agent can retain and retrieve prior information.
- Observable learning means prior information changes later behaviour.
A successful SQLite insert proves persistence only. Demonstrating learning requires a behavioural comparison.
2. State, events and memory are different records
World state says what is true now. An event says what happened at a particular tick. A memory says what an agent retains from an observation, result, message or inference. A memory may be incomplete or outdated.
Three useful memory classes
- Working memory: current objective, recent observation and pending tool call.
- Episodic memory: a specific experience tied to a tick and source.
- Knowledge: information synthesised from multiple experiences.
3. Preserve provenance and time
“I saw water” and “Eve told me there is water” are not equivalent evidence. Record a source such as direct_observation, tool_result, agent_message or inference. Keep the observed tick and, later, a confidence or validity field.
memory = {
"agent_id": "adam",
"kind": "episodic",
"content": "Observed water at (7, 2)",
"source": "direct_observation",
"observed_tick": 12,
}If the water disappears at tick 30, the tick-12 memory remains historically correct. Mark it stale or verify it; do not rewrite the past.
4. Create the first SQLite repository
from pathlib import Path
import sqlite3
DB_PATH = Path("data/eden.db")
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(DB_PATH) as connection:
connection.execute("""
CREATE TABLE IF NOT EXISTS memories (
id INTEGER PRIMARY KEY,
agent_id TEXT NOT NULL,
observed_tick INTEGER NOT NULL,
kind TEXT NOT NULL,
content TEXT NOT NULL,
source TEXT NOT NULL
)
""")
connection.execute(
"INSERT INTO memories "
"(agent_id, observed_tick, kind, content, source) "
"VALUES (?, ?, ?, ?, ?)",
("adam", 12, "episodic", "Water at (7, 2)", "direct_observation"),
)Use parameters, not string interpolation. Hide SQL behind a repository such as MemoryRepository so the agent does not own connection details.
5. Retrieve useful context
Do not place every stored memory in the prompt. Filter by agent, type, recency, location or keyword; apply a strict limit; then format a bounded context. Retrieval is part of application policy and must be testable without Ollama.
6. Snapshot the world separately
World restoration and agent memories have different authority. A snapshot serialises current world state and is restored through validation. Memories remain historical records. Keep SQLite work off the Tkinter thread and use short transactions.
Persist without AI
Insert and query one episode using sqlite3.
Build the repository
Move SQL behind typed methods.
Select context
Retrieve only Adam's three most relevant memories.
Restart
Restore a world snapshot and preserve its separate memories.
Protocol completion
After restart, Adam can recover a sourced memory of water and use it in a later decision. You can explain why this demonstrates persistence and memory, but not necessarily general learning.