Just as humans have different types of memory for different purposes, effective agents organize their long-term memory into specialized systems. Let's explore the three main types and understand when to use each.
Episodic memory
Episodic memory stores specific events and experiences from the agent's past interactions. Think of it as the agent's personal diary.
Characteristics:
Time-stamped events
User-specific experiences
Contextual details preserved
"What happened" memory
Example implementation:
class EpisodicMemory:
def store_episode(self, user_id, episode):
memory_entry = {
"user_id": user_id,
"timestamp": datetime.now(),
"conversation_id": str(uuid.uuid4()),
"summary": self.summarize(episode),
"key_events": self.extract_events(episode),
"user_preferences": self.extract_preferences(episode)
}
self.database.insert("episodic_memories", memory_entry)
def recall_user_history(self, user_id, context):
"""Retrieve relevant past interactions with this user"""
memories = self.database.query(
"episodic_memories",
filter={"user_id": user_id},
sort_by="relevance_to_context",
limit=5
)
return memoriesReal-world example:
User (March 2024): "I'm booking a trip to London for a tech conference"
Agent stores: {
"user_id": "alice123",
"event": "trip_planning",
"destination": "London",
"purpose": "tech_conference",
"preferences": ["business_travel", "city_center_hotels"]
}
User (May 2024): "I need to book another business trip"
Agent recalls: User previously traveled to London for conferences,
prefers city-center locationsProcedural memory
Procedural memory stores learned skills, workflows, and "how-to" knowledge. It's the agent's repertoire of actions and optimized procedures.
Characteristics:
Step-by-step procedures
Learned optimizations
Task completion patterns
"How to do" memory
Example implementation:
class ProceduralMemory:
def learn_procedure(self, task_type, successful_steps, outcome):
"""Store successful procedures for future use"""
procedure = {
"task_type": task_type,
"steps": successful_steps,
"success_rate": self.calculate_success_rate(outcome),
"optimizations": self.identify_optimizations(successful_steps),
"prerequisites": self.extract_prerequisites(successful_steps)
}
self.database.upsert("procedures", procedure)
def get_procedure(self, task_type):
"""Retrieve the best procedure for a task"""
return self.database.find_one(
"procedures",
filter={"task_type": task_type},
sort_by="success_rate"
)
Real-world example:
# Agent learns optimal flight booking procedure
learned_procedure = {
"task": "book_international_flight",
"steps": [
"1. Check visa requirements for destination",
"2. Verify passport expiry > 6 months",
"3. Search flights with 2-3 hour layovers minimum",
"4. Prefer morning departures for long-haul",
"5. Add seat selection for flights > 4 hours",
"6. Ensure 24-hour cancellation policy"
],
"learned_from": 50, # successful bookings
"success_rate": 0.94
}Semantic memory
Semantic memory stores general knowledge, facts, and conceptual understanding. It's the agent's knowledge base about the world.
Characteristics:
Facts and relationships
Domain knowledge
Conceptual understanding
"What is" memory
Example implementation:
class SemanticMemory:
def __init__(self):
self.vector_store = VectorDatabase()
def add_knowledge(self, fact, category, metadata=None):
"""Store factual knowledge with embeddings"""
embedding = self.generate_embedding(fact)
self.vector_store.insert({
"content": fact,
"embedding": embedding,
"category": category,
"metadata": metadata or {},
"confidence": self.assess_confidence(fact)
})
def query_knowledge(self, query, category=None):
"""Retrieve relevant facts using semantic search"""
query_embedding = self.generate_embedding(query)
results = self.vector_store.search(
query_embedding,
filter={"category": category} if category else None,
top_k=5
)
return resultsReal-world example:
# Semantic memory entries
knowledge_base = [
{
"fact": "US citizens need ESTA for UK visits under 90 days",
"category": "travel_requirements",
"countries": ["US", "UK"],
"condition": "stay_duration < 90"
},
{
"fact": "Heathrow Express takes 15 minutes to Paddington",
"category": "transport",
"route": "LHR->Paddington",
"duration_minutes": 15
}
]Choosing the right memory type
Different applications require different memory strategies:
Application type | Primary memory need | Example |
|---|---|---|
Personal assistant | Episodic | User preferences and past conversations |
Customer service bot | Semantic | Product knowledge and policies |
Task automation agent | Procedural | Optimized workflows and best practices |
Research assistant | Semantic + Episodic | Domain knowledge + user's research history |
Travel planner | All three | Facts + user history + booking procedure |
Hybrid memory systems
Most sophisticated agents use all three types together:
class HybridMemorySystem:
def __init__(self):
self.episodic = EpisodicMemory()
self.procedural = ProceduralMemory()
self.semantic = SemanticMemory()
def process_interaction(self, user_id, conversation):
# Store the episode
self.episodic.store_episode(user_id, conversation)
# Extract and store any new facts learned
facts = self.extract_facts(conversation)
for fact in facts:
self.semantic.add_knowledge(fact)
# Learn from successful task completions
if task_completed := self.identify_completed_task(conversation):
self.procedural.learn_procedure(
task_completed.type,
task_completed.steps,
task_completed.outcome
)
def prepare_context(self, user_id, current_query):
"""Retrieve relevant memories from all systems"""
return {
"user_history": self.episodic.recall_user_history(user_id),
"relevant_procedures": self.procedural.get_relevant(current_query),
"background_knowledge": self.semantic.query_knowledge(current_query)
}Implementation flexibility
An interesting aspect of memory systems is that despite having distinct conceptual abstractions, the underlying implementation mechanisms can be shared across memory types. For example:
Episodic and semantic memory might both use vector stores for similarity-based retrieval.
Procedural memory could be stored in a traditional database or also in a vector store.
All three types could potentially use the same storage system with different indexing strategies.
The specific implementation depends on several factors:
Usage patterns – How frequently each memory type is accessed.
Performance requirements – Latency and throughput constraints.
Quality needs – Retrieval accuracy and relevance.
Cost considerations – Storage and query expenses.
Scale – Volume of memories and number of users.
As the architect of your agentic system, you'll need to evaluate these trade-offs between quality, speed, and cost to choose the implementation approach that best fits your requirements. There's no single "correct" solution – the right choice depends on your specific use case.
Conclusion
Let's revise the key points of the topic:
Different memory types serve different purposes – Choose based on your application needs.
Episodic memory personalizes interactions – Critical for user-facing applications.
Procedural memory improves efficiency – Learn from successes to optimize future performance.
Semantic memory provides foundation – General knowledge that applies across users.
Hybrid systems are most powerful – Combine all three for human-like memory capabilities.
Implementation is flexible – Multiple storage mechanisms can work for each memory type; choose based on your specific requirements.
The power of a well-designed memory system lies in both selecting the right memory types and choosing implementation strategies that balance performance, cost, and quality. By treating episodic, procedural, and semantic memory as separate concerns, you can optimize each differently. As your agent evolves, different memory types may benefit from different storage approaches.