10. Agent Memory

Chapter 10 of 16 · 20 min

Agents need memory to retain information across multiple turns. Without memory, each conversation turn starts from scratch and the agent cannot build on prior actions.

Message history as memory

The simplest memory is a message list. Every turn appends the new exchange to the list:

class SimpleAgentMemory:
    def __init__(self, max_messages: int = 40):
        self.max_messages = max_messages
        self.messages = []
    
    def add_turn(self, role: str, content: str, tool_calls: list = None):
        entry = {"role": role, "content": content}
        if tool_calls:
            entry["tool_calls"] = tool_calls
        self.messages.append(entry)
        self._trim()
    
    def _trim(self):
        """Remove oldest non-system messages if limit exceeded"""
        system = [m for m in self.messages if m["role"] == "system"]
        others = [m for m in self.messages if m["role"] != "system"]
        if len(self.messages) > self.max_messages:
            self.messages = system + others[-(self.max_messages - len(system)):]
    
    def get_context(self, limit: int = None) -> list:
        if limit:
            return self.messages[-limit:]
        return self.messages

Summarization-based compression

For long conversations, token usage grows linearly. Compress the history by summarizing turns that are no longer immediately relevant:

def summarize_old_history(memory: SimpleAgentMemory, model, summarize_before: int = 20):
    if len(memory.messages) < summarize_before:
        return
    
    old_messages = memory.messages[:-5]  # Keep last 5 turns
    last_turns = memory.messages[-5:]
    
    summary_prompt = (
        "Summarize the following conversation in 2-3 sentences. "
        "Focus on key decisions, facts learned, and tasks completed.\n\n" +
        "\n".join(f"{m['role']}: {m['content'][:200]}" for m in old_messages)
    )
    
    summary_response = model.chat([
        {"role": "user", "content": summary_prompt}
    ])
    
    memory.messages = [{"role": "system", "content": f"Previous context: {summary_response.content}"}] + last_turns

Structured memory

Store specific facts in structured slots to avoid re-discovering known information:

class StructuredMemory:
    def __init__(self):
        self.facts = {}  # key: value pairs
        self.tasks_completed = []
        self.pending_tasks = []
    
    def store_fact(self, key: str, value: Any):
        self.facts[key] = value
    
    def get_fact(self, key: str) -> Optional[Any]:
        return self.facts.get(key)
    
    def mark_complete(self, task: str):
        self.tasks_completed.append(task)
        if task in self.pending_tasks:
            self.pending_tasks.remove(task)
EXERCISE

Implement a memory system that automatically summarizes every 10 turns. Run a 20-turn conversation and verify that after summarization, the agent still remembers key facts from the early turns.