# Memory Management

## Overview

The **Memory Management Pattern** enables AI agents to maintain, retrieve, and utilize conversation history and long-term context across interactions. Unlike stateless models that treat each query independently, memory management allows agents to remember past conversations, learn from interactions, and provide personalized, context-aware responses.

This pattern is essential for building agents that feel natural and human-like, maintaining continuity across sessions, learning user preferences, and building long-term relationships. Memory management bridges the gap between ephemeral interactions and persistent, evolving knowledge.

## Why Use This Pattern?

Traditional stateless LLM interactions have critical limitations:

- **No continuity**: Each query is independent, losing all context from previous turns
- **No personalization**: Cannot remember user preferences, history, or context
- **Repetitive interactions**: Users must re-explain context every time
- **Limited context window**: Cannot handle conversations beyond token limits
- **No learning**: Cannot improve or adapt based on past interactions

Memory Management solves these by:
- **Context retention**: Maintains conversation history across multiple turns
- **Personalization**: Remembers user preferences, facts, and interaction patterns
- **Efficient retrieval**: Quickly finds relevant past information
- **Semantic understanding**: Retrieves based on meaning, not just recency
- **Adaptive learning**: Improves responses based on past feedback
- **Scalable context**: Handles conversations far beyond model context limits

### Example: Customer Support with Memory

```
Without Memory (Stateless):
Turn 1:
User: "I'm having trouble with my premium account"
Agent: "I'd be happy to help! What's your account type?"

Turn 2:
User: "The billing isn't working"
Agent: "What account are you using?" ← Already forgot
Agent: "What issue are you experiencing?" ← Already forgot

With Memory Management:
Turn 1:
User: "I'm having trouble with my premium account"
Agent: "I understand you're having issues with your premium account. Let me help!"
[Stores: account_type=premium, issue=unspecified, user=frustrated]

Turn 2:
User: "The billing isn't working"
Agent: "I see you're having billing issues with your premium account. Last time
       you mentioned payment methods. Is this related to the same card ending in 4532?"
[Retrieves: account_type=premium, Stores: issue=billing, prior_payment_method=card_4532]

Turn 3 (New Session, Same Day):
User: "Hi, any update on my billing?"
Agent: "Welcome back! I remember you were having billing issues with your premium
       account earlier today. Let me check the status for you."
[Retrieves from long-term: previous conversation, account details, issue context]
```

## How It Works

Memory management operates through a continuous cycle of storing, retrieving, and updating information:

1. **Store**: Save user messages, agent responses, and extracted facts
2. **Retrieve**: Find relevant memories based on current context
3. **Update**: Modify, consolidate, or summarize existing memories
4. **Prune**: Remove outdated or irrelevant information
5. **Persist**: Save memory across sessions for long-term recall

```
┌─────────────────────────────────────────────────────────┐
│                      User Message                        │
└────────────────────┬────────────────────────────────────┘
                     ↓
         ┌───────────────────────┐
         │   Memory Retrieval    │
         │  "Find relevant past  │
         │    conversations"     │
         └───────┬───────────────┘
                 ↓
         ┌───────────────────────┐
         │   Context Assembly    │
         │  Current + Retrieved  │
         └───────┬───────────────┘
                 ↓
         ┌───────────────────────┐
         │   Agent Processing    │
         │   (with context)      │
         └───────┬───────────────┘
                 ↓
         ┌───────────────────────┐
         │   Memory Storage      │
         │  Store new facts &    │
         │    conversation       │
         └───────┬───────────────┘
                 ↓
         ┌───────────────────────┐
         │   Memory Update       │
         │  Consolidate, prune   │
         │     summarize         │
         └───────┬───────────────┘
                 ↓
         ┌───────────────────────┐
         │  Agent Response       │
         └───────────────────────┘
```

## When to Use This Pattern

### ✅ Ideal Use Cases

- **Multi-turn conversations**: Chat applications, customer support, virtual assistants
- **Personalized agents**: Learning user preferences, habits, and context over time
- **Long-running sessions**: Conversations spanning days, weeks, or months
- **Relationship building**: Therapy bots, coaching assistants, personal companions
- **Contextual applications**: Project management, research assistants, tutoring
- **User profiling**: E-commerce recommendations, content personalization
- **Stateful workflows**: Multi-step processes requiring context across steps
- **Learning systems**: Agents that improve based on user feedback

### ❌ When NOT to Use

- **Single-turn queries**: One-off questions with no follow-up
- **Public/anonymous agents**: No user identity to track
- **Privacy-sensitive contexts**: When storing data creates compliance risks
- **Stateless APIs**: When design explicitly requires no state
- **Resource-constrained**: When storage/retrieval overhead is prohibitive

## Rule of Thumb

**Use Memory Management when:**
1. Users expect the agent to **remember past interactions**
2. **Personalization** improves the experience
3. Conversations are **multi-turn** or span **multiple sessions**
4. Context from **previous exchanges** is valuable
5. You need to track **user preferences** or **facts over time**

**Don't use Memory Management when:**
1. Each query is **completely independent**
2. No user identity or continuity exists
3. Privacy/compliance prevents data storage
4. Storage costs outweigh benefits
5. Real-time performance is critical (retrieval adds latency)

## Core Components

### 1. Short-Term Memory (Buffer Memory)

Stores recent conversation turns in a buffer:
- **Fixed size**: Last N messages (e.g., 10 turns)
- **Fast access**: No retrieval needed
- **Complete context**: Full message content
- **Automatic eviction**: Oldest messages dropped
- **Use case**: Immediate conversation context

### 2. Long-Term Memory (Persistent Storage)

Stores all historical information permanently:
- **Unlimited storage**: All conversations and facts
- **Retrieval required**: Must search to access
- **Summarized or full**: Can store summaries or complete text
- **Cross-session**: Persists across app restarts
- **Use case**: Historical context, learned preferences

### 3. Memory Stores

**Buffer Store**: Simple list or queue
- Recent messages
- Fast, in-memory
- Limited capacity

**Summary Store**: Condensed conversation history
- Periodic summarization
- Reduces token usage
- Maintains key points

**Vector Store**: Semantic memory with embeddings
- Similarity-based retrieval
- Finds related content
- Scalable to millions of memories

**Entity Store**: Structured fact tracking
- People, places, preferences
- Key-value or graph database
- Queryable by entity

### 4. Retrieval Strategies

**Recency-Based**: Most recent memories first
- Simple, predictable
- Good for short conversations
- Misses older relevant context

**Relevance-Based**: Semantic similarity to current query
- Uses embeddings and vector search
- Finds topically related memories
- Can miss recent but important context

**Hybrid**: Combines recency + relevance
- Weighted scoring (e.g., 70% relevance, 30% recency)
- Balances both dimensions
- Best overall approach

**Importance-Based**: Prioritizes significant memories
- Scores memories by importance
- Retains critical information longer
- Requires importance scoring logic

## Implementation Approaches

### Approach 1: Buffer Memory (Simplest)

Store recent messages in a sliding window:

```python
from collections import deque

class BufferMemory:
    def __init__(self, max_size: int = 10):
        self.buffer = deque(maxlen=max_size)

    def add_message(self, role: str, content: str):
        self.buffer.append({"role": role, "content": content})

    def get_messages(self):
        return list(self.buffer)
```

**Pros**: Simple, fast, predictable
**Cons**: Limited context, no semantic search, loses old information

### Approach 2: Summary Memory

Periodically summarize conversation history:

```python
def summarize_conversation(messages):
    # Summarize every N messages
    if len(messages) >= 10:
        summary = llm.invoke(
            f"Summarize this conversation:\n{messages}"
        )
        return summary
    return messages
```

**Pros**: Reduces token usage, maintains key points
**Cons**: Loses details, summarization costs, quality depends on LLM

### Approach 3: Vector Memory (Semantic)

Store memories as embeddings for semantic retrieval:

```python
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams

# Initialize vector store
client = QdrantClient(":memory:")
client.create_collection(
    collection_name="memories",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)

# Store memory
embedding = embeddings_model.embed(text)
client.upsert(
    collection_name="memories",
    points=[{"id": memory_id, "vector": embedding, "payload": {"text": text}}]
)

# Retrieve similar memories
results = client.search(
    collection_name="memories",
    query_vector=query_embedding,
    limit=5
)
```

**Pros**: Semantic understanding, scalable, finds relevant context
**Cons**: Requires embeddings, more complex, retrieval latency

### Approach 4: Entity Memory

Track specific entities and relationships:

```python
class EntityMemory:
    def __init__(self):
        self.entities = {}

    def extract_and_store(self, text):
        # Extract entities (people, places, preferences)
        entities = llm.extract_entities(text)

        for entity in entities:
            if entity.name not in self.entities:
                self.entities[entity.name] = {}

            self.entities[entity.name].update(entity.attributes)

    def get_entity(self, name):
        return self.entities.get(name, {})
```

**Pros**: Structured, queryable, precise
**Cons**: Requires entity extraction, may miss unstructured context

### Approach 5: Hybrid Memory System

Combines multiple memory types:

```python
class HybridMemory:
    def __init__(self):
        self.buffer = BufferMemory(max_size=10)  # Recent context
        self.vector_store = VectorMemory()  # Semantic search
        self.entities = EntityMemory()  # Structured facts
        self.summaries = []  # Condensed history

    def add_memory(self, message):
        # Store in buffer
        self.buffer.add(message)

        # Store in vector store
        self.vector_store.add(message)

        # Extract entities
        self.entities.extract_and_store(message)

        # Summarize if buffer full
        if self.buffer.is_full():
            summary = self.summarize_buffer()
            self.summaries.append(summary)
            self.buffer.clear()

    def retrieve(self, query):
        # Get recent buffer
        recent = self.buffer.get_all()

        # Get semantically similar
        similar = self.vector_store.search(query, limit=3)

        # Get relevant entities
        entities = self.entities.find_relevant(query)

        # Combine and rank
        return self.merge_context(recent, similar, entities)
```

## Key Benefits

### 🧠 Continuity and Context
- **Seamless conversations**: Agent remembers what was discussed
- **No repetition**: Users don't re-explain context
- **Natural flow**: Conversations feel connected and coherent

### 🎯 Personalization
- **User preferences**: Remembers likes, dislikes, habits
- **Contextual responses**: Tailored to individual user
- **Relationship building**: Grows understanding over time

### 💾 Scalability Beyond Context Limits
- **Unlimited history**: Not constrained by model context window
- **Efficient retrieval**: Only loads relevant memories
- **Long-term learning**: Accumulates knowledge indefinitely

### 🔍 Intelligent Retrieval
- **Semantic search**: Finds relevant content, not just recent
- **Importance-based**: Prioritizes significant memories
- **Fast access**: Optimized indexing and search

### 📊 Analytics and Learning
- **Conversation patterns**: Analyze user behavior over time
- **Preference trends**: Track changing interests
- **Quality improvement**: Learn from past mistakes

## Trade-offs

### 💰 Storage Costs

**Issue**: Storing all conversations and embeddings requires significant storage

**Impact**: Costs scale with users and conversation length

**Mitigation**:
- Implement memory pruning strategies
- Use summary memory for old conversations
- Set retention policies (e.g., delete after 90 days)
- Compress or deduplicate similar memories
- Use cost-effective storage tiers (cold storage for old data)

### ⏱️ Retrieval Latency

**Issue**: Searching through large memory stores adds latency

**Impact**: 50-500ms added to response time

**Mitigation**:
- Use efficient vector databases (Qdrant, Pinecone)
- Cache frequently accessed memories
- Limit retrieval to top-K results (e.g., 5-10)
- Implement memory pre-fetching
- Use approximate nearest neighbor (ANN) algorithms

### 🔒 Privacy and Compliance

**Issue**: Storing user conversations raises privacy concerns (GDPR, CCPA)

**Impact**: Legal liability, user trust issues

**Mitigation**:
- Implement data retention policies
- Provide user controls (view, edit, delete memories)
- Encrypt sensitive data
- Anonymize or pseudonymize data
- Get explicit consent for memory storage
- Implement right-to-be-forgotten

### 🎯 Context Limits

**Issue**: Even with memory, model context windows limit what can be included

**Impact**: Cannot load all relevant memories into single prompt

**Mitigation**:
- Intelligent retrieval (quality over quantity)
- Hierarchical summarization
- Use modern long-context models or retrieval-backed memory instead of assuming one giant prompt
- Stream context in multiple rounds if needed

### ❌ Memory Errors

**Issue**: Incorrect memories (hallucinated facts, misattributions)

**Impact**: Agent propagates errors across conversations

**Mitigation**:
- Implement memory verification
- Track confidence scores
- Allow user corrections
- Periodic memory validation
- Source tracking for facts

## Best Practices

### 1. Hybrid Memory Architecture

```python
class MemorySystem:
    """Combines multiple memory types for optimal performance"""

    def __init__(self):
        # Immediate context (last 10 messages)
        self.buffer = BufferMemory(max_size=10)

        # Semantic long-term memory
        self.vector_store = VectorMemory()

        # Structured facts
        self.entity_store = EntityMemory()

        # Conversation summaries
        self.summaries = []

    def get_context(self, query: str, max_tokens: int = 2000):
        """Retrieve optimal context within token budget"""
        context = []

        # Always include recent buffer (highest priority)
        context.extend(self.buffer.get_all())

        # Add relevant semantic memories
        relevant = self.vector_store.search(query, limit=5)
        context.extend(relevant)

        # Add important entities
        entities = self.entity_store.find_relevant(query)
        context.extend(entities)

        # Add summaries if space remains
        if self.estimate_tokens(context) < max_tokens:
            context.extend(self.summaries[-3:])  # Last 3 summaries

        return self.rank_and_filter(context, max_tokens)
```

### 2. Intelligent Memory Pruning

```python
def prune_memories(self, strategy: str = "importance"):
    """Remove low-value memories to manage storage"""

    if strategy == "recency":
        # Keep only last 30 days
        cutoff = datetime.now() - timedelta(days=30)
        self.delete_before(cutoff)

    elif strategy == "importance":
        # Score each memory
        for memory in self.memories:
            score = self.calculate_importance(memory)
            if score < THRESHOLD:
                self.delete(memory)

    elif strategy == "redundancy":
        # Remove similar/duplicate memories
        clusters = self.cluster_similar_memories()
        for cluster in clusters:
            # Keep most important from each cluster
            best = max(cluster, key=lambda m: m.importance)
            for memory in cluster:
                if memory != best:
                    self.delete(memory)
```

### 3. Memory Importance Scoring

```python
def calculate_importance(self, memory: Memory) -> float:
    """Score memory importance for retention decisions"""
    score = 0.0

    # Recency factor (decay over time)
    days_old = (datetime.now() - memory.timestamp).days
    recency_score = 1.0 / (1.0 + days_old / 30.0)
    score += recency_score * 0.3

    # Access frequency
    access_score = min(memory.access_count / 10.0, 1.0)
    score += access_score * 0.3

    # User signals (explicit importance, bookmarks)
    if memory.user_marked_important:
        score += 0.4

    # Content signals (questions, decisions, preferences)
    if memory.is_question or memory.contains_decision:
        score += 0.2

    return min(score, 1.0)
```

### 4. Privacy-Preserving Memory

```python
class PrivateMemory:
    """Memory system with privacy controls"""

    def __init__(self, user_id: str):
        self.user_id = user_id
        self.retention_days = 90
        self.encryption_key = self.load_user_key(user_id)

    def store(self, content: str, metadata: dict):
        # Encrypt sensitive content
        encrypted = self.encrypt(content)

        # Set expiration
        expires_at = datetime.now() + timedelta(days=self.retention_days)

        # Store with privacy metadata
        self.db.insert({
            "user_id": self.user_id,
            "content": encrypted,
            "metadata": metadata,
            "expires_at": expires_at,
            "consent": True
        })

    def delete_user_data(self):
        """GDPR right-to-be-forgotten"""
        self.db.delete_all(user_id=self.user_id)
        self.vector_store.delete_all(user_id=self.user_id)
```

### 5. Memory Consolidation

```python
def consolidate_memories(self, time_window: timedelta):
    """Periodically merge and summarize related memories"""

    # Get memories from time window
    memories = self.get_memories_in_window(time_window)

    # Cluster by topic
    clusters = self.cluster_by_topic(memories)

    for cluster in clusters:
        if len(cluster) >= 3:
            # Summarize cluster
            summary = self.llm.summarize([m.content for m in cluster])

            # Create consolidated memory
            consolidated = Memory(
                content=summary,
                source_ids=[m.id for m in cluster],
                timestamp=max(m.timestamp for m in cluster),
                importance=max(m.importance for m in cluster)
            )

            # Replace individual memories with summary
            self.store(consolidated)
            for memory in cluster:
                self.delete(memory)
```

## Performance Metrics

Track these metrics for memory systems:

### Retrieval Quality
- **Precision**: % of retrieved memories that are relevant
- **Recall**: % of relevant memories that are retrieved
- **MRR (Mean Reciprocal Rank)**: Quality of ranking
- **User satisfaction**: Explicit feedback on memory quality

### Performance
- **Retrieval latency**: Time to fetch relevant memories (target: <100ms)
- **Storage size**: Total memory footprint per user
- **Query throughput**: Searches per second
- **Memory access patterns**: Hot vs. cold memories

### Usage
- **Memory growth rate**: New memories per user per day
- **Retrieval frequency**: How often memories are accessed
- **Memory lifespan**: Average time before pruning
- **User engagement**: Conversation length and retention

### Quality
- **Memory accuracy**: % of factually correct memories
- **Staleness**: Age of retrieved memories
- **Consolidation ratio**: Original vs. consolidated memory count
- **Context fit**: Relevance of retrieved context to current query

## Example Scenarios

### Scenario 1: Personal Assistant

```
Day 1, Morning:
User: "I have a meeting with Sarah at 2pm"
Agent: "Got it! I'll remember your meeting with Sarah at 2pm today."
[Stores: event={type: meeting, person: Sarah, time: 2pm, date: today}]

Day 1, 1:30pm:
User: "What's on my schedule?"
Agent: "You have a meeting with Sarah in 30 minutes at 2pm."
[Retrieves: upcoming events for today]

Day 3:
User: "When did I last meet with Sarah?"
Agent: "You met with Sarah 2 days ago at 2pm on Monday."
[Retrieves: past events with entity=Sarah, sorted by recency]

Week 2:
User: "Schedule another meeting with Sarah"
Agent: "Sure! Last time you met Sarah on a Monday afternoon at 2pm.
       Would you like the same time?"
[Retrieves: past patterns with entity=Sarah, extracts preferences]
```

### Scenario 2: Customer Support

```
Session 1:
User: "My order #12345 hasn't arrived"
Agent: "I'm sorry to hear that. Let me check order #12345 for you..."
[Stores: {order: 12345, issue: delivery, status: in_progress, sentiment: frustrated}]

Session 2 (Next Day):
User: "Hi, any update on my order?"
Agent: "Welcome back! I remember you contacted us yesterday about order #12345
       not arriving. Let me get the latest status for you..."
[Retrieves: recent conversation with this user, identifies context]

Session 3 (Week Later):
User: "I want to order again but I'm worried about delivery"
Agent: "I understand your concern. Last time we had an issue with order #12345,
       but we resolved it and you received your items. We've since improved
       our delivery process. Would you like to proceed?"
[Retrieves: past delivery issues, shows learning and improvement]
```

### Scenario 3: Learning Tutor

```
Week 1:
Student: "I'm struggling with algebra"
Tutor: "No problem! Let's work through it together. What specifically is challenging?"
Student: "Quadratic equations"
[Stores: {subject: algebra, topic: quadratic_equations, difficulty: high, date: week1}]

Week 2:
Student: "Can you help me with math homework?"
Tutor: "Of course! Last week we worked on quadratic equations in algebra.
       Is this related, or a new topic?"
[Retrieves: recent math topics, identifies context]

Week 4:
Student: "I have a test coming up"
Tutor: "Great! Over the past few weeks, we've covered quadratic equations,
       linear systems, and factoring. Let's review the areas where you
       needed the most practice: completing the square and word problems."
[Retrieves: all past sessions, identifies weak areas, creates personalized review]

Month 2:
Tutor: "I've noticed you're really improving! Your algebra scores have gone
       from struggling with quadratics to confidently solving complex problems.
       Ready to move on to more advanced topics?"
[Retrieves: longitudinal performance data, shows growth over time]
```

## Advanced Patterns

### 1. Episodic Memory

Store memories as discrete episodes with context:

```python
class EpisodicMemory:
    """Stores memories as time-bounded episodes"""

    def create_episode(self, conversation: List[Message]):
        episode = {
            "id": generate_id(),
            "start_time": conversation[0].timestamp,
            "end_time": conversation[-1].timestamp,
            "messages": conversation,
            "summary": self.summarize(conversation),
            "entities": self.extract_entities(conversation),
            "topic": self.extract_topic(conversation),
            "sentiment": self.analyze_sentiment(conversation)
        }

        self.store_episode(episode)
        return episode

    def recall_episode(self, query: str):
        """Find relevant past episodes"""
        candidates = self.vector_search(query)
        return self.rank_by_relevance(candidates)
```

### 2. Semantic Memory

Store facts and knowledge separate from episodes:

```python
class SemanticMemory:
    """Stores factual knowledge and relationships"""

    def __init__(self):
        self.facts = {}
        self.relationships = nx.Graph()

    def store_fact(self, subject: str, predicate: str, object: str):
        """Store a fact triple"""
        fact_id = self.generate_fact_id(subject, predicate, object)

        self.facts[fact_id] = {
            "subject": subject,
            "predicate": predicate,
            "object": object,
            "confidence": 1.0,
            "source": "user_stated",
            "timestamp": datetime.now()
        }

        # Add to knowledge graph
        self.relationships.add_edge(subject, object, relation=predicate)

    def query_fact(self, subject: str, predicate: str = None):
        """Query facts about a subject"""
        matching = []
        for fact in self.facts.values():
            if fact["subject"] == subject:
                if predicate is None or fact["predicate"] == predicate:
                    matching.append(fact)
        return matching
```

### 3. Memory Consolidation

Merge related memories during "sleep" periods:

```python
class MemoryConsolidator:
    """Consolidates memories during idle time"""

    def consolidate_overnight(self):
        """Run consolidation during off-peak hours"""

        # Get today's memories
        today = self.get_memories(date=datetime.now().date())

        # Cluster by similarity
        clusters = self.cluster_by_similarity(today)

        for cluster in clusters:
            if len(cluster) >= 3:
                # Create consolidated memory
                summary = self.llm.summarize_memories(cluster)

                # Extract key facts
                facts = self.extract_facts(cluster)

                # Create consolidated memory
                consolidated = ConsolidatedMemory(
                    summary=summary,
                    facts=facts,
                    source_count=len(cluster),
                    date_range=(min(m.timestamp for m in cluster),
                               max(m.timestamp for m in cluster))
                )

                # Replace individual memories
                self.replace_with_consolidated(cluster, consolidated)
```

### 4. Hierarchical Memory

Multi-level memory structure (working memory → short-term → long-term):

```python
class HierarchicalMemory:
    """Three-tier memory system"""

    def __init__(self):
        # Tier 1: Working memory (current conversation)
        self.working = []  # Last 5-10 messages

        # Tier 2: Short-term (recent session)
        self.short_term = deque(maxlen=50)  # Last 50 messages

        # Tier 3: Long-term (all history)
        self.long_term = VectorStore()

    def add_message(self, message: Message):
        # Always goes to working memory
        self.working.append(message)

        # Promote to short-term
        if len(self.working) > 10:
            oldest = self.working.pop(0)
            self.short_term.append(oldest)

        # Consolidate to long-term periodically
        if len(self.short_term) >= 50:
            self.consolidate_to_long_term()

    def retrieve(self, query: str):
        # Search all tiers, prioritize by tier
        working_context = self.working  # Always include
        short_term_relevant = self.search_short_term(query, limit=5)
        long_term_relevant = self.long_term.search(query, limit=3)

        return self.merge_context(
            working_context,
            short_term_relevant,
            long_term_relevant
        )
```

### 5. Associative Memory Network

Connect related memories through associations:

```python
class AssociativeMemory:
    """Memory network with associative connections"""

    def __init__(self):
        self.memories = {}
        self.associations = nx.Graph()  # Memory graph

    def add_memory(self, memory: Memory):
        self.memories[memory.id] = memory

        # Find related memories
        related = self.find_related(memory)

        # Create associations
        for related_memory in related:
            strength = self.calculate_association_strength(
                memory, related_memory
            )
            self.associations.add_edge(
                memory.id,
                related_memory.id,
                weight=strength
            )

    def activate_memory(self, memory_id: str):
        """Spreading activation through network"""
        activated = {memory_id: 1.0}

        # Spread activation to connected memories
        for neighbor in self.associations.neighbors(memory_id):
            edge_weight = self.associations[memory_id][neighbor]["weight"]
            activated[neighbor] = edge_weight * 0.7  # Decay factor

        # Recursive activation (depth=2)
        for neighbor_id, activation in list(activated.items()):
            if activation > 0.3:  # Threshold
                for second_order in self.associations.neighbors(neighbor_id):
                    if second_order not in activated:
                        weight = self.associations[neighbor_id][second_order]["weight"]
                        activated[second_order] = activation * weight * 0.5

        # Return activated memories above threshold
        return {mid: score for mid, score in activated.items() if score > 0.2}
```

## Comparison with Related Patterns

| Pattern | Focus | Storage | Retrieval | When to Use |
|---------|-------|---------|-----------|-------------|
| **Memory Management** | Long-term context | Persistent, structured | Semantic + recency | Multi-turn, personalization |
| **Context Management** | Current context | Ephemeral | N/A (included directly) | Single session, within context limits |
| **RAG** | External knowledge | Static documents | Semantic search | Factual Q&A, knowledge lookup |
| **Planning** | Future actions | Plan state | Goal-based | Task execution, workflows |
| **Reflection** | Past actions | Execution history | Chronological | Learning, improvement |

**Memory vs. Context Management**: Memory is long-term and persistent across sessions; context is ephemeral and session-scoped.

**Memory vs. RAG**: Memory stores personal/conversational data; RAG retrieves from static knowledge bases.

**Memory vs. Planning**: Memory looks backward (what happened); planning looks forward (what to do).

## Common Pitfalls

### 1. Over-Storing (Memory Bloat)

**Problem**: Storing every message creates massive storage costs and slow retrieval

**Solution**:
- Implement aggressive pruning
- Summarize old conversations
- Store only important messages
- Use compression for old data

### 2. Retrieval Irrelevance

**Problem**: Retrieved memories aren't actually relevant to current context

**Solution**:
- Fine-tune retrieval parameters
- Use hybrid relevance + recency scoring
- Implement user feedback loops
- A/B test retrieval strategies

### 3. Stale Memories

**Problem**: Outdated information retrieved as if still current

**Solution**:
- Track memory freshness
- Deprecate old facts when new ones arrive
- Add temporal context to retrieval
- Implement expiration policies

### 4. Privacy Violations

**Problem**: Storing sensitive data without proper controls

**Solution**:
- Implement data classification
- Encrypt sensitive memories
- Provide user visibility and control
- Follow privacy regulations (GDPR, CCPA)

### 5. Memory Conflicts

**Problem**: Contradictory memories (user said X, then later said opposite)

**Solution**:
- Track memory versions
- Prioritize recent over old
- Explicitly handle updates vs. contradictions
- Ask user to clarify conflicts

### 6. Cold Start Problem

**Problem**: New users have no memory, limiting personalization

**Solution**:
- Onboarding questionnaires
- Import from other sources
- Use population-level defaults
- Explicitly learn preferences early

## Conclusion

Memory Management is essential for creating AI agents that feel natural, personalized, and intelligent. By maintaining context across conversations, learning user preferences, and retrieving relevant history, memory-enabled agents provide superior experiences compared to stateless alternatives.

**Use Memory Management when:**
- Building conversational agents with multi-turn interactions
- Personalization improves user experience
- Users expect the agent to remember them
- Conversations span multiple sessions
- Learning from history is valuable

**Implementation checklist:**
- ✅ Choose appropriate memory types (buffer, semantic, entity)
- ✅ Implement efficient retrieval (vector search, hybrid ranking)
- ✅ Set up memory persistence across sessions
- ✅ Design pruning strategies to manage storage
- ✅ Add privacy controls and compliance features
- ✅ Track memory quality metrics (precision, recall, latency)
- ✅ Implement memory consolidation for long-term efficiency
- ✅ Handle memory conflicts and updates gracefully
- ✅ Provide user visibility into stored memories
- ✅ Monitor storage costs and retrieval performance

**Key Takeaways:**
- 💾 Memory enables continuity and personalization across conversations
- 🧠 Multiple memory types serve different purposes (buffer, semantic, entity)
- 🔍 Intelligent retrieval combines relevance, recency, and importance
- ⚖️ Balance storage costs, retrieval latency, and context quality
- 🔒 Privacy and compliance are critical considerations
- 📊 Monitor retrieval quality and system performance
- 🔄 Memory consolidation and pruning maintain long-term efficiency
- 🎯 Hybrid approaches combining memory types work best

---

*Memory Management transforms stateless AI interactions into continuous, personalized relationships—enabling agents that truly remember, learn, and grow with their users over time.*

## Corporate SSL proxy note

If you're behind a corporate SSL-inspecting proxy, run examples with:

```bash
AGENTIC_DISABLE_SSL=1 bash run.sh
```

