Skip to content

Long-Term Memory: Persistent Storage Systems

Overview

Long-term memory is persistent storage that survives across conversations and sessions. It enables agents to learn over time and build up knowledge.

Unlike short-term memory (measured in tokens, cleared each turn), long-term memory is measured in GB/TB and keeps growing.


Types of Long-Term Memory

1. Episodic Memory (What Happened)

Event: "User asked about quantum computing on July 15"
Storage: Vector DB indexed by time + semantics
Purpose: Learn from past interactions
Access: "Retrieve similar past conversations"

Structure:

class EpisodEvent:
    timestamp: datetime
    user_input: str
    agent_response: str
    outcome: str  # Success/failure/quality
    embedding: np.array  # For semantic search
    tags: List[str]  # For filtering

2. Semantic Memory (Knowledge Base)

Fact: "Claude is an AI assistant made by Anthropic"
Storage: Knowledge graph or structured DB
Purpose: General knowledge
Access: "What is Claude?"

Structure:

class SemanticFact:
    subject: str  # "Claude"
    predicate: str  # "is_made_by"
    object: str  # "Anthropic"
    confidence: float  # 0.0-1.0
    source: str  # "Training data" / "User told me"

3. Procedural Memory (How to Do Things)

Procedure: "To analyze a dataset: 1) Load, 2) Explore, 3) Clean, 4) Analyze"
Storage: Procedure library with metrics
Purpose: Skill/strategy storage
Access: "How do I analyze data?"

Structure:

class Procedure:
    name: str  # "analyze_dataset"
    steps: List[str]
    success_rate: float  # Historical success
    prerequisites: List[str]
    estimated_tokens: int
    tags: List[str]


Storage Technologies

Option 1: Vector Database

Best for: Episodic memory (past events)

Characteristics: - Semantic search capability - Fast retrieval (using indices) - Good for similarity matching - Scales to millions of items

Examples: Chroma, Weaviate, Milvus, LanceDB, Pinecone

Implementation:

from chromadb import Client

vector_db = Client()
collection = vector_db.get_or_create_collection("episodic_memory")

# Store an episode
collection.add(
    documents=[user_input],
    embeddings=[embedding],
    metadatas=[{
        "timestamp": timestamp,
        "agent_response": response,
        "quality": 0.85
    }],
    ids=[episode_id]
)

# Retrieve similar episodes
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=5
)

Option 2: Knowledge Graph

Best for: Semantic memory (facts and relationships)

Characteristics: - Structured relationships - Reasoning over relationships - Good for complex queries - Moderate scalability

Examples: Neo4j, ArangoDB, MemGraph

Implementation:

from neo4j import GraphDatabase

driver = GraphDatabase.driver("bolt://localhost:7687")

# Store a fact
with driver.session() as session:
    session.run(
        """
        CREATE (a:Person {name: 'Alice'})
        CREATE (b:Company {name: 'Acme'})
        CREATE (a)-[:WORKS_AT]->(b)
        """
    )

# Query relationships
with driver.session() as session:
    result = session.run(
        "MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p, c"
    )

Option 3: Traditional Database

Best for: Procedural memory (procedures) and structured data

Characteristics: - Reliable and proven - Good for structured data - ACID transactions - Complex queries

Examples: PostgreSQL, MongoDB, MySQL

Implementation:

import sqlite3

conn = sqlite3.connect('memory.db')
cursor = conn.cursor()

# Store a procedure
cursor.execute('''
    INSERT INTO procedures (name, steps, success_rate)
    VALUES (?, ?, ?)
''', ('analyze_data', json.dumps(steps), 0.85))

# Query procedures
cursor.execute('SELECT * FROM procedures WHERE success_rate > 0.8')
results = cursor.fetchall()

conn.commit()


Hybrid Architecture

Most production systems use a hybrid approach:

- ┌─────────────────────────────────────────┐
    - Long-Term Memory System          │
  - ┤
│                                         │
    - Vector DB                              │
      - Episodic memories (events)         │
      - Semantic similarity search         │
      - ~1-10M items                       │
│                                         │
    - Knowledge Graph                        │
      - Semantic facts (relationships)     │
      - Relationship reasoning             │
      - ~100K-1M triples                   │
│                                         │
    - Relational DB                          │
      - Structured data                    │
      - Procedures & policies              │
      - User profiles                      │
│                                         │
    - File Storage                           │
      - Large documents                    │
      - Full conversations                 │
      - Backups                            │
│                                         │
  - ┘

Memory Consolidation

Process of organizing and compressing memories over time:

Stage 1: Encoding (Recording)

New experience → Encode into vectors → Store in vector DB

Stage 2: Consolidation (Organizing)

Similar memories → Group together → Extract patterns

Stage 3: Compression (Compacting)

1000 similar memories → Summarize into 10 concepts

Implementation:

class MemoryConsolidator:
    def consolidate(self, memory_db):
        """Run consolidation process"""

        # Stage 1: Clustering
        # Group similar memories
        clusters = self.cluster_similar_memories(memory_db)

        # Stage 2: Extracting Patterns
        # What patterns emerge?
        patterns = self.extract_patterns(clusters)

        # Stage 3: Summarization
        # Convert to semantic facts
        for pattern in patterns:
            semantic_fact = self.pattern_to_fact(pattern)
            self.semantic_db.add(semantic_fact)

        # Stage 4: Compression
        # Old specific memories can be deleted
        for cluster in clusters:
            self.mark_for_deletion(cluster)


Retrieval Strategies

# Find similar past episodes
query = "User asked about budget planning"
query_embedding = embed(query)

similar_episodes = vector_db.search(
    query_embedding,
    top_k=5
)

# Result: Past times we discussed budgets

Strategy 2: Relationship Navigation

# Follow relationships in knowledge graph
query = """
  Find all companies that Alice knows about
  through her colleagues
"""

results = knowledge_graph.query(query)

Strategy 3: Filtering

# Filter by metadata
recent_high_quality = memory_db.filter(
    timestamp__after=datetime(2025, 1, 1),
    quality__gte=0.8,
    tags__in=["research", "important"]
)

Practical Implementation

Complete Long-Term Memory System

class LongTermMemorySystem:
    def __init__(self):
        # Vector DB for episodic
        self.episodic = Chroma(collection_name="episodes")

        # Knowledge graph for semantic
        self.semantic = Neo4jDB()

        # SQL DB for procedures
        self.procedural = SQLiteDB("procedures.db")

    def remember_event(self, event: dict):
        """Store an episodic memory"""
        # Embed and store
        embedding = embed_text(event['description'])

        self.episodic.add(
            documents=[event['description']],
            embeddings=[embedding],
            metadatas=[{
                'timestamp': event['timestamp'],
                'outcome': event['outcome'],
                'tags': event['tags']
            }]
        )

    def remember_fact(self, subject, predicate, obj, confidence=1.0):
        """Store a semantic memory"""
        self.semantic.add_triple(
            subject=subject,
            predicate=predicate,
            object=obj,
            confidence=confidence
        )

    def remember_procedure(self, procedure: dict):
        """Store a procedural memory"""
        self.procedural.insert(
            name=procedure['name'],
            steps=json.dumps(procedure['steps']),
            success_rate=procedure['success_rate'],
            tags=','.join(procedure['tags'])
        )

    def recall_similar(self, query: str, k=5):
        """Recall similar past events"""
        query_embedding = embed_text(query)
        return self.episodic.search(query_embedding, top_k=k)

    def recall_fact(self, subject, predicate):
        """Recall a fact"""
        return self.semantic.query(subject, predicate)

    def recall_procedure(self, task_name):
        """Recall how to do something"""
        return self.procedural.query(
            "SELECT * FROM procedures WHERE name = ?",
            (task_name,)
        )

    def consolidate(self):
        """Periodically consolidate memories"""
        # Cluster similar episodes
        clusters = self.episodic.cluster_similar()

        # Extract patterns into semantic facts
        for cluster in clusters:
            pattern = self.extract_pattern(cluster)
            self.remember_fact(
                subject=pattern['subject'],
                predicate=pattern['predicate'],
                obj=pattern['object'],
                confidence=0.9
            )

Scaling Long-Term Memory

Challenge: Storage Growth

Day 1:   10 memories    (0.1 MB)
Day 30:  300 memories   (3 MB)
Month 6: 5,000 memories (50 MB)
Year 1:  15,000 memories (150 MB)
Year 5:  75,000 memories (750 MB)

Solutions

1. Partitioning by Time

vector_db_2025_q1 = Chroma("episodes_2025_q1")
vector_db_2025_q2 = Chroma("episodes_2025_q2")

# Query recent
recent = vector_db_2025_q2.search(query)

# Query historical (if needed)
historical = vector_db_2025_q1.search(query)

2. Partitioning by User

user_memories = {
    "alice": Chroma("alice_episodes"),
    "bob": Chroma("bob_episodes")
}

3. Pruning Old Memories

def prune_old_memories(memory_db, days=90):
    """Delete old, low-quality memories"""

    cutoff = now() - timedelta(days=days)

    old_memories = memory_db.filter(
        timestamp__before=cutoff
    )

    for memory in old_memories:
        if memory['quality'] < 0.5:
            memory_db.delete(memory['id'])

Privacy & Security

Challenge: PII in Memories

Dangerous to store:
  ✗ Full credit card numbers
  ✗ Social security numbers
  ✗ Passwords
  ✗ Medical information

Solution: Anonymization

def anonymize_event(event):
    """Remove PII before storing"""
    # Replace credit cards with "CARD_XXXX"
    event['text'] = re.sub(
        r'\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}',
        'CARD_XXXX',
        event['text']
    )

    # Replace emails with "USER_EMAIL"
    event['text'] = re.sub(
        r'\S+@\S+',
        'USER_EMAIL',
        event['text']
    )

    return event

Monitoring Long-Term Memory

def monitor_memory_health(memory_system):
    """Check memory system health"""

    print(f"Vector DB size: {memory_system.episodic.size()}")
    print(f"Knowledge graph nodes: {memory_system.semantic.count_nodes()}")
    print(f"Procedures stored: {memory_system.procedural.count()}")

    # Check retrieval quality
    test_queries = [
        "Tell me about past research discussions",
        "What facts do we know about AI?",
        "How do we analyze data?"
    ]

    for query in test_queries:
        results = memory_system.recall_similar(query)
        if not results:
            print(f"WARNING: No results for '{query}'")

    # Check memory freshness
    avg_age = memory_system.episodic.average_age_days()
    print(f"Average memory age: {avg_age} days")

Best Practices

1. Structure Matters

# ✅ Good: Clean structure
memory = {
    "timestamp": datetime.now(),
    "event_type": "research_completed",
    "subject": "quantum_computing",
    "quality": 0.9
}

# ❌ Bad: Messy
memory = {"data": "stuff happened"}

2. Version Control Memories

# ✅ Good: Track updates
memory.update(
    fact="New discovery",
    version=2,  # Updated
    previous_version=1
)

# ❌ Bad: Overwrite without history
memory["fact"] = "New discovery"  # Old value lost

3. Index Frequently Accessed

# ✅ Good: Index for speed
memory_db.create_index("timestamp")
memory_db.create_index("quality")
memory_db.create_index("tags")

# ❌ Bad: No indexing
# Each query scans everything

4. Regular Maintenance

# ✅ Good: Scheduled consolidation
schedule.every().day.at("02:00").do(
    memory_system.consolidate
)
schedule.every().week.at("03:00").do(
    memory_system.prune_old_memories
)

# ❌ Bad: Never clean up
# Memory bloats, gets slow

Key Takeaways

  1. Multiple storage types needed - Vector DB, Knowledge Graph, SQL DB
  2. Consolidation is critical - Compress memories over time
  3. Hybrid architecture - Use right tool for each memory type
  4. Scalability challenges exist - Plan for growth
  5. Privacy matters - Anonymize before storing
  6. Monitor health - Check retrieval quality regularly

Next Steps


Last Updated: August 9, 2026