Modern language models are stateless. Each inference starts from scratch, with no memory of previous interactions. For a single-turn Q&A system, this is fine. For agents that must learn, adapt, and accumulate knowledge over hours, days, or weeks of operation, it's a fundamental limitation.

This is where long-term memory architectures enter the picture. By layering different memory systems atop your agent—working memory for immediate context, episodic memory for session history, semantic memory for facts and relationships, and procedural memory for learned strategies—you can build agents that genuinely remember and learn.

In this article, I'll walk through the four pillars of agent memory, the implementation patterns that make each work at scale, and how to integrate them into a unified architecture. This is core material from Building Agentic AI Systems, grounded in production experience.

Why Context Windows Aren't Memory

The first misconception to dispel: a large context window is not the same as memory. Claude 3 supports 200K tokens; GPT-4 supports 128K. But context windows are:

A context window is a working memory buffer—useful for immediate reasoning but not a substitute for persistent storage and retrieval.

The Four Memory Systems: A Human Analogy

Human memory isn't monolithic. We have four distinct systems that work in concert:

1. Working Memory

Capacity: 5–9 items, seconds to minutes. Function: Active manipulation of information. Example: Holding a phone number while dialing it.

Agent equivalent: The context window of the current LLM request. It's where the agent reasons, plans, and makes decisions. Limited but immediate.

2. Episodic Memory

Capacity: Unbounded. Duration: Years. Function: Autobiographical memory—recall specific events, conversations, and experiences. Example: "I remember the client call on Tuesday when they asked about deployment latency."

Agent equivalent: Session logs, conversation history, experience records. Indexed for fast retrieval, compressed for efficiency.

3. Semantic Memory

Capacity: Unbounded. Duration: Lifetime. Function: Facts, concepts, relationships. No context attached. Example: "Paris is the capital of France" or "recursion is a key algorithmic pattern."

Agent equivalent: Knowledge graphs, vector databases, fact stores. Structured relationships that enable reasoning across domains.

4. Procedural Memory

Capacity: Hundreds of procedures. Duration: Lifetime. Function: Skills and strategies. Implicit, not explicit. Example: How to ride a bike, or how to debug a performance issue.

Agent equivalent: Tool strategies, learned execution patterns, heuristics distilled from experience.

Why This Matters

Agents with only working memory are reactive: they respond to the current query with no history. Agents with episodic memory can learn from experience. Agents with semantic and procedural memory can reason deeply and adapt strategies. The combination creates genuine intelligence.

Implementation Pattern 1: Vector Store Memory (RAG-Based Recall)

The most common implementation of episodic and semantic memory is a vector database (Pinecone, Weaviate, Chroma, Milvus). The pattern is Retrieval-Augmented Generation (RAG):

  1. Embedding: Convert text (experiences, facts) into dense vectors using an embedding model (OpenAI text-embedding-3, Anthropic's embeddings, etc.).
  2. Storage: Index vectors in a database with metadata (timestamp, source, confidence).
  3. Retrieval: When the agent needs context, embed the query and retrieve the k nearest neighbors (most relevant items).
  4. Augmentation: Prepend retrieved items to the LLM prompt, so the agent reasons with relevant history.

Here's a skeleton implementation:

import openai
import pinecone

# Initialize
pinecone.init(api_key="YOUR_KEY", environment="us-west1-gcp")
index = pinecone.Index("agent-memory")

def store_experience(text: str, agent_id: str, timestamp: str):
    """Embed and store an experience."""
    embedding = openai.Embedding.create(
        input=text,
        model="text-embedding-3-small"
    )["data"][0]["embedding"]
    
    index.upsert([(
        f"{agent_id}-{timestamp}",
        embedding,
        {"text": text, "agent_id": agent_id, "timestamp": timestamp}
    )])

def retrieve_memories(query: str, agent_id: str, k: int = 5):
    """Retrieve relevant memories for a query."""
    query_embedding = openai.Embedding.create(
        input=query,
        model="text-embedding-3-small"
    )["data"][0]["embedding"]
    
    results = index.query(
        query_embedding,
        top_k=k,
        filter={"agent_id": {"$eq": agent_id}}
    )
    
    return [
        result.metadata["text"] 
        for result in results.matches
    ]

def agent_with_memory(query: str, agent_id: str):
    """Query the agent with memory augmentation."""
    # Retrieve relevant memories
    memories = retrieve_memories(query, agent_id)
    
    # Augment the prompt
    context = "\n".join([f"- {m}" for m in memories])
    augmented_query = f"""Relevant past experiences:
{context}

Current query: {query}"""
    
    # Call LLM
    response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{"role": "user", "content": augmented_query}]
    )
    
    # Store this interaction as a new experience
    store_experience(f"Q: {query}\nA: {response.choices[0].message.content}", agent_id, timestamp=str(datetime.now()))
    
    return response.choices[0].message.content

This pattern is production-proven. The key insights:

Implementation Pattern 2: Knowledge Graph Memory (Structured Relationships)

Vector stores excel at recall by similarity. But they don't capture relationships between concepts. For that, you need a knowledge graph—a graph database (Neo4j, AWS Neptune) that stores facts as nodes and relationships.

Example: Instead of storing "Client X uses technology Y," you store:

CREATE (client:Client {name: "Acme Corp"})
CREATE (tech:Technology {name: "Kubernetes"})
CREATE (client)-[:USES]->(tech)

Now the agent can query:

MATCH (client:Client {name: "Acme Corp"})-[:USES]->(tech)
RETURN tech.name

And reason: "Acme Corp uses Kubernetes, so they likely care about container orchestration reliability."

For agents, knowledge graphs are particularly powerful because:

A hybrid approach combines both: use the knowledge graph for reasoning and structured queries, and a vector store for fuzzy recall and semantic search.

Implementation Pattern 3: Episodic Memory (Conversation Summaries and Experience Logs)

Raw conversation logs are verbose and expensive to index. Instead, compress and summarize them:

def summarize_session(messages: list[dict]) -> str:
    """Summarize a session into a compact experience record."""
    conversation_text = "\n".join([
        f"{m['role']}: {m['content']}"
        for m in messages
    ])
    
    summary_prompt = f"""Summarize this conversation in 2-3 sentences. 
Focus on:
- What the user asked
- What the agent did
- What the outcome was

Conversation:
{conversation_text}"""
    
    summary = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{"role": "user", "content": summary_prompt}]
    ).choices[0].message.content
    
    return summary

def store_session_memory(session_id: str, messages: list[dict], agent_id: str):
    """Compress and store a session."""
    summary = summarize_session(messages)
    
    # Store both the summary (for retrieval) and metadata
    metadata = {
        "session_id": session_id,
        "agent_id": agent_id,
        "timestamp": datetime.now().isoformat(),
        "message_count": len(messages),
        "first_message": messages[0]["content"][:100],
        "outcome": "success"  # or "failure", from agent logs
    }
    
    # Index in vector store
    store_experience(summary, agent_id, str(datetime.now()))

The benefit: a 50-message conversation becomes a 2-sentence summary, reducing storage and retrieval costs by 20x while retaining the essential information.

Implementation Pattern 4: Procedural Memory (Tool Strategies and Learned Heuristics)

As an agent executes tasks repeatedly, it learns which tools work best in which contexts. Procedural memory captures these learned strategies.

Example: An agent tasked with debugging performance issues learns:

Store this as a procedural policy:

class ProcedurePolicy:
    """Learn and store tool execution strategies."""
    
    def __init__(self, agent_id: str):
        self.agent_id = agent_id
        self.strategies = {}  # task_type -> [sequence of tools]
        self.success_rates = {}  # tool -> success rate
        self.db = None  # Persist to DB
    
    def record_execution(self, task_type: str, tools_used: list[str], success: bool):
        """Record the outcome of a tool sequence."""
        if task_type not in self.strategies:
            self.strategies[task_type] = []
        
        self.strategies[task_type].append(tools_used)
        
        for tool in tools_used:
            if tool not in self.success_rates:
                self.success_rates[tool] = {"successes": 0, "attempts": 0}
            
            self.success_rates[tool]["attempts"] += 1
            if success:
                self.success_rates[tool]["successes"] += 1
    
    def get_recommended_tools(self, task_type: str) -> list[str]:
        """Return tools ranked by learned success."""
        if task_type not in self.strategies:
            return []
        
        # Most common successful sequence
        sequences = self.strategies[task_type]
        return sequences[-1] if sequences else []
    
    def save(self):
        """Persist to database."""
        # Serialize to JSON and store
        pass

Over time, the agent's tool selection becomes more efficient—it learns to reach for the right tools first, reducing wasted steps and token consumption.

Memory Management: Forgetting and Consolidation

Unlimited memory is a liability. Agents need to forget strategically:

1. Time-Decay Forgetting

Older memories matter less. Decay their weight:

def decay_memory_relevance(timestamp: str, decay_rate: float = 0.01):
    """Compute relevance decay based on age."""
    age_days = (datetime.now() - datetime.fromisoformat(timestamp)).days
    relevance = math.exp(-decay_rate * age_days)
    return relevance

When retrieving memories, multiply by this decay factor. Old experiences matter, but less than recent ones.

2. Consolidation (Merging Similar Memories)

After many similar experiences, consolidate them into a single higher-level memory:

This reduces retrieval cost and prevents redundancy clutter.

3. Relevance-Based Pruning

Delete memories that the agent never retrieves:

def prune_memories(agent_id: str, retrieval_threshold: int = 2):
    """Delete memories that haven't been retrieved in N months."""
    old_unused = index.query(
        filter={
            "agent_id": {"$eq": agent_id},
            "retrieval_count": {"$lt": retrieval_threshold},
            "age_days": {"$gt": 90}
        }
    )
    
    for memory in old_unused:
        index.delete(memory.id)

Hybrid Architecture: Bringing It All Together

In production, you use all four memory systems simultaneously:

Memory Type Storage Use Case Query Pattern
Working LLM context window Immediate reasoning All (in every request)
Episodic Vector store + DB Recall past sessions Semantic search
Semantic Knowledge graph Fact lookup, reasoning Cypher/SPARQL
Procedural Policy DB Tool selection, strategies Direct lookup

The architecture looks like this:

┌─────────────────────────────────────────────────┐
│               User Query                        │
└────────────────┬────────────────────────────────┘
                 │
        ┌────────▼────────┐
        │  Working Memory │  (LLM Context)
        └────────┬────────┘
                 │
      ┌──────────┼──────────┐
      │          │          │
   ┌──▼──┐  ┌───▼────┐  ┌──▼───┐
   │ RAG │  │ Knowledge Graph  │  │Policy│
   │(Vec)│  │(Semantic Memory) │  │ DB  │
   └──┬──┘  └───┬────┘  └──┬───┘
      │         │          │
      └─────────┼──────────┘
              │
        ┌─────▼──────┐
        │   LLM      │
        │  Reasoning │
        └─────┬──────┘
              │
        ┌─────▼──────────┐
        │ Tool Execution │
        └─────┬──────────┘
              │
        ┌─────▼──────┐
        │   Store    │
        │  Outcome   │  (Update Memory Systems)
        └────────────┘

Code Example: A Complete Agentic Memory System

Here's a production-ready sketch of all four systems integrated:

from datetime import datetime
from typing import List, Dict, Any
import json

class AgentMemorySystem:
    """Unified agent memory architecture."""
    
    def __init__(self, agent_id: str):
        self.agent_id = agent_id
        self.vector_store = VectorStore()  # Episodic + Semantic
        self.knowledge_graph = KnowledgeGraph()
        self.policy_db = PolicyDatabase()
        self.working_memory = []  # Current context
    
    def observe(self, event: Dict[str, Any]):
        """Record an observation (tool call, result, etc.)."""
        # Add to working memory
        self.working_memory.append({
            "timestamp": datetime.now(),
            "event": event
        })
        
        # Limit working memory size
        if len(self.working_memory) > 20:
            # Consolidate oldest items
            self._consolidate_working_memory()
    
    def _consolidate_working_memory(self):
        """Compress working memory into episodic memory."""
        if len(self.working_memory) < 5:
            return
        
        # Summarize oldest 5 items
        old_items = self.working_memory[:5]
        summary = self._summarize_events(old_items)
        
        # Store in episodic memory
        self.vector_store.store(
            text=summary,
            agent_id=self.agent_id,
            timestamp=datetime.now()
        )
        
        # Remove from working memory
        self.working_memory = self.working_memory[5:]
    
    def recall_episodic(self, query: str, k: int = 3) -> List[str]:
        """Retrieve relevant episodic memories."""
        return self.vector_store.retrieve(
            query=query,
            agent_id=self.agent_id,
            k=k
        )
    
    def recall_semantic(self, query: str) -> List[Dict]:
        """Query the knowledge graph."""
        return self.knowledge_graph.query(query)
    
    def get_tool_strategy(self, task_type: str) -> List[str]:
        """Retrieve learned tool sequence."""
        return self.policy_db.get_strategy(self.agent_id, task_type)
    
    def record_tool_outcome(self, task_type: str, tools_used: List[str], success: bool):
        """Update procedural memory."""
        self.policy_db.record(
            agent_id=self.agent_id,
            task_type=task_type,
            tools_used=tools_used,
            success=success
        )
    
    def augment_prompt(self, base_query: str) -> str:
        """Augment the LLM prompt with all relevant memories."""
        episodic = self.recall_episodic(base_query)
        semantic = self.recall_semantic(base_query)
        working = [e["event"] for e in self.working_memory[-3:]]
        
        augmented = f"""Working Context:
{json.dumps(working, default=str)}

Relevant Past Experiences:
{json.dumps(episodic)}

Knowledge Base:
{json.dumps(semantic)}

User Query: {base_query}"""
        
        return augmented

Key Takeaways

Summary

1. Four memory systems are essential: Working memory (context), episodic (experiences), semantic (facts), procedural (strategies).

2. Vector stores enable semantic search: Use them for fast retrieval of relevant memories by meaning, not just keywords.

3. Knowledge graphs encode relationships: They enable multi-hop reasoning and are essential for structured domains.

4. Compression is critical: Summarize sessions, consolidate memories, and prune old unused data to keep costs manageable.

5. Integrate all systems: The agent's reasoning power comes from having multiple sources of truth (working + episodic + semantic + procedural) to draw from.

6. Memory management matters: Implement time-decay, consolidation, and pruning to prevent memory bloat and hallucination.

7. This is from production systems: All patterns here come from Chapter 5 of Building Agentic AI Systems and real deployed agents at scale.

Next Steps

Start with episodic memory (vector store + retrieval). It's the highest-impact, easiest-to-implement first step. Once you have working retrieval, add the knowledge graph for structured reasoning, then procedural memory for tool optimization.

For deeper exploration—implementation details, benchmarks, failure modes, and advanced architectures—see Building Agentic AI Systems, Chapter 5: "Memory and Learning."