Building with Episodic Memory

Episodic memories capture discrete events with full context -- what happened, when it happened, the emotional tone, and why it mattered. This guide covers creating, searching, filtering, and extracting insights from episodic memories.

Overview

Episodic memory is the foundation of an agent's experience. Each memory represents a single event: a user request, a debugging session, a deployment, a conversation turn. Unlike raw logs, episodic memories are semantically searchable and carry structured metadata about timing, emotional context, and importance.

When to use Episodic Memory

Use episodic memory when you need to record what happened rather than what something is (semantic) or how to do something (procedural). Think conversation turns, debugging sessions, user interactions, and noteworthy events.

Creating Episodic Memories

To create an episodic memory, you need at minimum a content string. The Rekall API will automatically generate embeddings, detect emotional tone, and assign a default importance score.

Create a basic episodic memory
import Rekall from '@rekall/agent-sdk';
const rekall = new Rekall({ apiKey: 'rk_your_key' });
const memory = await rekall.memories.create({
type: 'episodic',
content: 'User asked me to refactor the auth module to use JWT tokens instead of session cookies.',
metadata: {
source: 'conversation',
tags: ['refactoring', 'auth', 'jwt'],
},
});
console.log(memory.id); // mem_abc123
console.log(memory.importance); // 0.72 (auto-calculated)

Adding Rich Metadata

Episodic memories support structured metadata that enables powerful filtering later. You can specify emotional tone, importance, participants, location context, and custom tags.

Memory with rich metadata
const memory = await rekall.memories.create({
type: 'episodic',
content: 'Successfully debugged the race condition in the payment processing pipeline. The issue was a missing mutex on the balance check.',
metadata: {
source: 'debugging-session',
tags: ['bug-fix', 'payments', 'concurrency'],
participants: ['user:adam', 'agent:claude'],
project: 'payments-service',
},
emotionalTone: 'satisfied', // auto-detected if omitted
importance: 0.85, // override auto-calculation
context: {
sessionId: 'sess_xyz789',
platform: 'cursor',
file: 'src/payments/processor.ts',
},
});

Auto-detection

If you omit emotionalTone and importance, Rekall automatically analyzes the content to detect emotional tone and calculate an importance score between 0 and 1. You can always override these values if needed.

Episodic memories are indexed with vector embeddings, so you can search using natural language queries rather than exact keyword matches. The search engine understands meaning and context.

Search episodic memories
// Natural language semantic search
const results = await rekall.memories.search({
query: 'authentication bugs we fixed recently',
type: 'episodic',
limit: 10,
});
for (const result of results.memories) {
console.log(`[${result.similarity.toFixed(2)}] ${result.content}`);
console.log(` Created: ${result.createdAt}`);
console.log(` Importance: ${result.importance}`);
}

The search returns results ranked by semantic similarity. A score of 1.0 is a perfect match; anything above 0.7 is generally a strong result.

Filtering by Time, Emotion & Importance

Combine semantic search with structured filters to narrow results precisely. You can filter on timestamps, emotional tone, importance thresholds, tags, and custom metadata.

Time-Based Filters

Filter by time range
// Memories from the last 7 days
const recent = await rekall.memories.search({
query: 'deployment issues',
type: 'episodic',
filters: {
createdAfter: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
},
});
// Memories from a specific date range
const ranged = await rekall.memories.search({
query: 'deployment issues',
type: 'episodic',
filters: {
createdAfter: '2025-01-01T00:00:00Z',
createdBefore: '2025-01-31T23:59:59Z',
},
});

Emotion Filters

Filter memories by their detected emotional tone. Useful for finding frustrating debugging sessions or satisfying breakthroughs.

Filter by emotional tone
// Find frustrating experiences (to avoid repeating them)
const frustrating = await rekall.memories.search({
query: 'configuration problems',
type: 'episodic',
filters: {
emotionalTone: ['frustrated', 'confused'],
},
});
// Find positive breakthroughs
const breakthroughs = await rekall.memories.search({
query: 'solved the problem',
type: 'episodic',
filters: {
emotionalTone: ['satisfied', 'excited'],
},
});

Importance Filters

Filter by importance threshold
// Only high-importance memories
const critical = await rekall.memories.search({
query: 'production incidents',
type: 'episodic',
filters: {
minImportance: 0.8,
},
});
// Combine all filters
const filtered = await rekall.memories.search({
query: 'auth module changes',
type: 'episodic',
filters: {
createdAfter: '2025-01-01T00:00:00Z',
minImportance: 0.6,
emotionalTone: ['neutral', 'satisfied'],
tags: ['auth'],
},
limit: 5,
});

Building Conversation History

A common pattern is to store each conversation turn as an episodic memory, creating a searchable history that persists across sessions. Group related turns using session IDs.

Store conversation turns
// Store each turn as an episodic memory
async function recordConversationTurn(
sessionId: string,
role: 'user' | 'assistant',
message: string,
turnIndex: number,
) {
return rekall.memories.create({
type: 'episodic',
content: `[${role}] ${message}`,
metadata: {
source: 'conversation',
tags: ['conversation-turn'],
role,
turnIndex,
},
context: {
sessionId,
},
});
}
// Later, retrieve the full conversation
const history = await rekall.memories.search({
query: '*',
type: 'episodic',
filters: {
metadata: {
source: 'conversation',
},
context: {
sessionId: 'sess_abc123',
},
},
sort: 'createdAt',
order: 'asc',
limit: 100,
});
// Or search across all conversations semantically
const relevant = await rekall.memories.search({
query: 'when the user asked about database migrations',
type: 'episodic',
filters: {
metadata: { source: 'conversation' },
},
});

Session grouping

Use the context.sessionId field to group memories from the same conversation. This lets you retrieve an entire conversation thread or search across all threads simultaneously.

Extracting Insights from Events

Episodic memories become more powerful when you analyze patterns across them. Use aggregation queries to surface trends, frequent issues, and recurring themes.

Analyze patterns across memories
// Get a summary of recent activity
const summary = await rekall.memories.summarize({
type: 'episodic',
filters: {
createdAfter: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
},
});
console.log(summary.overview);
// "This week you primarily worked on auth refactoring (12 events),
// payment bug fixes (8 events), and documentation updates (5 events)."
console.log(summary.emotionalBreakdown);
// { satisfied: 14, frustrated: 6, neutral: 5 }
console.log(summary.topTags);
// ['auth', 'payments', 'refactoring', 'bug-fix', 'docs']
// Find recurring frustration points
const painPoints = await rekall.memories.search({
query: 'problems and issues encountered',
type: 'episodic',
filters: {
emotionalTone: ['frustrated', 'confused', 'stuck'],
minImportance: 0.5,
},
limit: 20,
});
// Use memories to build context for the next interaction
const recentContext = await rekall.memories.search({
query: 'what have we been working on',
type: 'episodic',
filters: {
createdAfter: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
},
limit: 10,
});

Memory consolidation

Over time, Rekall automatically consolidates episodic memories into long-term memory. High-importance memories are preserved longer, while low-importance ones naturally decay. See the Decay & Consolidation guide for details.

Next Steps

Rekall
rekall