Episodic & Procedural Memory¶
Overview¶
Different types of information should be stored and retrieved differently.
- Episodic: "What happened?" - Specific events
- Procedural: "How do I do it?" - Skills and procedures
This file covers their differences, storage, and retrieval.
Episodic Memory: What Happened¶
Definition¶
Memory of specific events that occurred at specific times in specific contexts.
Examples:
- "On Jan 15, I analyzed quarterly sales"
- "The user mentioned they prefer concise responses"
- "We discovered that customers want faster shipping"
Characteristics¶
| Aspect | Value |
|---|---|
| What | Specific events/experiences |
| When | Time-stamped |
| Where | Context-rich |
| Why | Learn from experience |
| Storage | Vector DB (episodic store) |
| Retrieval | Semantic similarity search |
| Retention | Variable (important events kept longer) |
Implementation¶
class EpisodicMemory:
def __init__(self):
self.vector_db = VectorStore()
self.events = []
def record_event(self, event: dict):
"""Record a specific event"""
episode = {
"timestamp": event['timestamp'],
"description": event['description'],
"context": event.get('context', {}),
"outcome": event.get('outcome'),
"emotional_valence": event.get('valence', 0), # -1 to 1
"importance": event.get('importance', 0.5)
}
# Embed and store
embedding = embed(episode['description'])
self.vector_db.add(
text=episode['description'],
embedding=embedding,
metadata={
'timestamp': episode['timestamp'],
'context': episode['context'],
'outcome': episode['outcome'],
'importance': episode['importance']
}
)
self.events.append(episode)
def recall_similar(self, query: str, k: int = 5):
"""Recall similar past episodes"""
query_embedding = embed(query)
# Search by similarity
similar = self.vector_db.search(query_embedding, k)
# Return with timestamps (temporal context)
return sorted(similar, key=lambda x: x['metadata']['timestamp'], reverse=True)
def recall_recent(self, hours: int = 24):
"""Recall recent episodes"""
cutoff = now() - timedelta(hours=hours)
return [
e for e in self.events
if e['timestamp'] > cutoff
]
# Usage
episodic = EpisodicMemory()
# Record an event
episodic.record_event({
'timestamp': now(),
'description': 'Analyzed quarterly sales data from Q1',
'context': {'user': 'alice', 'domain': 'sales'},
'outcome': 'Found 15% growth trend',
'importance': 0.9
})
# Later recall similar events
results = episodic.recall_similar("What analysis have we done on sales?")
Procedural Memory: How Do I Do It¶
Definition¶
Memory of skills, procedures, and strategies—how to do things.
Examples:
- "To analyze data: 1) Load, 2) Explore, 3) Clean, 4) Analyze"
- "When writing reports: Always include context and limitations"
- "The most effective search strategy is to try multiple queries"
Characteristics¶
| Aspect | Value |
|---|---|
| What | Skills, procedures, strategies |
| When | Not time-specific |
| Where | Domain-specific |
| Why | Improve performance through practice |
| Storage | Procedure DB, policy store |
| Retrieval | Keyword/topic lookup |
| Retention | Persistent (keep forever) |
| Updates | Success rate tracking |
Implementation¶
class ProceduralMemory:
def __init__(self):
self.procedures = {}
self.success_rates = {}
self.usage_counts = {}
def learn_procedure(self, name: str, steps: List[str], domain: str = "general"):
"""Learn a new procedure"""
self.procedures[name] = {
"name": name,
"steps": steps,
"domain": domain,
"learned_at": now(),
"version": 1
}
self.success_rates[name] = 0.5 # Start neutral
self.usage_counts[name] = 0
def execute_procedure(self, name: str, **kwargs):
"""Execute a learned procedure"""
if name not in self.procedures:
raise ProcedureNotFound(f"Unknown procedure: {name}")
procedure = self.procedures[name]
try:
# Execute steps
for step in procedure['steps']:
execute_step(step, **kwargs)
# Record success
self.record_success(name)
return True
except Exception as e:
# Record failure
self.record_failure(name)
raise
def record_success(self, procedure_name: str):
"""Update success rate when procedure succeeds"""
current = self.success_rates.get(procedure_name, 0.5)
# Update with exponential moving average
self.success_rates[procedure_name] = 0.9 * current + 0.1 * 1.0
self.usage_counts[procedure_name] += 1
def record_failure(self, procedure_name: str):
"""Update success rate when procedure fails"""
current = self.success_rates.get(procedure_name, 0.5)
# Update with exponential moving average
self.success_rates[procedure_name] = 0.9 * current + 0.1 * 0.0
self.usage_counts[procedure_name] += 1
def get_best_procedure(self, domain: str):
"""Get most successful procedure for domain"""
domain_procedures = [
(name, self.success_rates[name])
for name, proc in self.procedures.items()
if proc['domain'] == domain
]
if not domain_procedures:
return None
# Return highest success rate
best_name = max(domain_procedures, key=lambda x: x[1])[0]
return self.procedures[best_name]
# Usage
procedural = ProceduralMemory()
# Learn a procedure
procedural.learn_procedure(
name="analyze_sales_data",
steps=[
"Load data from database",
"Explore dimensions and metrics",
"Clean anomalies",
"Calculate growth trends",
"Identify patterns"
],
domain="sales"
)
# Execute and track success
try:
procedural.execute_procedure("analyze_sales_data", data=sales_df)
except Exception as e:
print(f"Procedure failed: {e}")
Comparing Episodic vs Procedural¶
Use Cases¶
| Scenario | Which Memory |
|---|---|
| "What did we learn last time?" | Episodic |
| "How did we solve this before?" | Episodic (find similar case) |
| "How do I do task X?" | Procedural |
| "What worked best for this domain?" | Procedural (best procedure) |
| "When did we discover this?" | Episodic (temporal) |
| "Why did that strategy work?" | Both (combine) |
Retrieval¶
Episodic:
# Find similar past events
recent_analyses = episodic.recall_similar("sales analysis", k=5)
# Returns
Procedural:
# Find how to do something
procedure = procedural.get_best_procedure("sales_analysis")
# Returns
Learning¶
Episodic:
# Learn from experience
# Just record what happened
episodic.record_event(...)
# Over time, patterns emerge from many episodes
Procedural:
# Learn through practice
# Try procedure, track success
try:
procedural.execute_procedure(...)
success = True # Update success rate
except:
success = False # Update failure rate
# Success rate improves with practice
Integration: Combined Memory System¶
class IntegratedMemorySystem:
def __init__(self):
self.episodic = EpisodicMemory()
self.procedural = ProceduralMemory()
def learn_from_experience(self, task_name: str, outcome: dict):
"""Learn both episodic and procedural"""
# Store what happened
self.episodic.record_event({
'timestamp': outcome['timestamp'],
'description': f"Performed {task_name}",
'outcome': outcome['result'],
'importance': outcome['success']
})
# If new successful approach, learn procedure
if outcome['success'] and outcome['is_novel_approach']:
self.procedural.learn_procedure(
name=outcome['procedure_name'],
steps=outcome['steps'],
domain=task_name.split('_')[0]
)
# Track success for existing procedures
if task_name in self.procedural.procedures:
if outcome['success']:
self.procedural.record_success(task_name)
else:
self.procedural.record_failure(task_name)
def solve_new_problem(self, problem: dict):
"""Solve using both episodic and procedural"""
# Step 1: Look for similar past problems (episodic)
similar_past = self.episodic.recall_similar(problem['description'], k=3)
# Step 2: Get best procedure for this domain (procedural)
domain = problem['domain']
best_procedure = self.procedural.get_best_procedure(domain)
# Step 3: Combine insights
if best_procedure:
# Use proven procedure
return self.procedural.execute_procedure(best_procedure['name'])
elif similar_past:
# Adapt solution from similar past case
past_solution = similar_past[0]['outcome']
return adapt_solution(past_solution, problem)
else:
# Fall back to general approach
return solve_from_scratch(problem)
-
Competing for Storage: Which to Keep?¶
When storage is limited, which memories to keep?
Priority Matrix¶
Low Success Rate High Success Rate
Old: Delete Keep (maybe compress)
Recent: Replace Keep
High Importance: Keep Keep
Low Importance: Delete Keep
Implementation¶
def prioritize_for_retention(memory, other_options):
"""Score memory for retention"""
score = 0
# Episodic factors
if memory.type == 'episodic':
score += memory['importance'] * 50
# Older = lower priority
age_days = (now() - memory['timestamp']).days
score -= min(age_days / 30, 40) # Cap penalty
# Frequently accessed = higher priority
score += memory['access_count'] * 5
# Procedural factors
if memory.type == 'procedural':
score += memory['success_rate'] * 50 # Very important
score += memory['usage_count'] * 2
score -= (memory.get('failure_count', 0) * 5)
return score
Best Practices¶
1. Episodic: Keep Context¶
# Good
episode = {
'what': 'Analyzed sales',
'when': datetime.now(),
'where': 'sales_dashboard',
'who': 'alice',
'why': 'Quarterly review',
'outcome': 'Found 15% growth'
}
# Bad
episode = {'text': 'Analyzed sales'}
2. Procedural: Track Metrics¶
# Good
procedure['success_rate'] = 0.87
procedure['times_used'] = 15
procedure['last_success'] = datetime.now()
# Bad
procedure['steps'] = [...] # Don't know if it works
3. Combine for Better Decisions¶
# Good
best_procedure = procedural.get_best(domain)
similar_past = episodic.recall_similar(query)
decision = combine_insights(best_procedure, similar_past)
# Bad
decision = execute_procedure(best_procedure) # Ignore past context
Real-World Example¶
# A sales agent learns and improves
# Day 1
agent.analyze_sales(data) # Records event, learns procedure
# Day 2
agent.analyze_sales(data) # Uses learned procedure, tracks success
# Day 5
agent.analyze_sales(difficult_data)
# Recalls similar past difficult cases (episodic)
# Uses best procedure (procedural)
# Adapts based on context
# Records success
# Month 1
agent.analyze_sales(data) # Procedure highly successful
-
Key Takeaways¶
- Episodic: Store specific events with context
- Procedural: Store and improve skills/procedures
- Different storage: Episodic in vector DB, procedural in procedure DB
- Different retrieval: Episodic by similarity, procedural by domain
- Integration: Use both for better decisions
- Learning: Track success rates for procedures
- Retention: Keep both, prioritize high-value
-
Next Steps¶
- Go To Planning & Reasoning - Next chapter
- Review all Memory Systems files to solidify understanding
-
Last Updated: August 9, 2026