Episodic memory system for AI agents with vector search, exposed via MCP server.
- Design Rationale - Design decisions, formulas, algorithms, and research
- Interface Design - SOLID principles and trait design
- Schema - Database schema and migrations
MemoryManager (Facade)
├── EpisodicMemoryStore - Raw interaction events
└── HybridRetrievalEngine - BM25 + Vector search
- Episodic Memory: Stores specific events with full context
- Event type, timestamp, conversation ID
- Outcome, valence (emotional value)
- Vector embeddings for semantic search
use memory_rs::{Database, MemoryManager};
use memory_rs::models::dtos::Episode;
// Initialize
let db = Database::new("memory.db")?;
let manager = MemoryManager::new(db);
// Store episode
let episode = Episode {
workspace_id: 1,
event_type: "task".to_string(),
timestamp: "2026-01-31 10:00:00".to_string(),
outcome: Some("success".to_string()),
valence: Some(0.8),
// ... other fields
};
manager.store_episode(episode).await?;
// Retrieve memories
let results = manager.retrieve("task", workspace_id, 10)?;# Start MCP server
cargo run --bin agent-memory-mcp my-workspace
# View statistics
memory-cli stats --workspace 1
# Query memories
memory-cli query "rust programming" --limit 10
# Prune old memories
memory-cli prune --dry-runStore interaction events with full context:
- Event type, timestamp, conversation ID
- Outcome, valence (emotional value)
- Vector embeddings for semantic search
let episode = Episode::new("user_query", context, Some("outcome"), Some(0.8));
manager.store_episode(episode).await?;Combines BM25 keyword search with vector similarity:
- Cosine distance retrieval on episode embeddings
- BM25 search with proper IDF calculation
- Configurable result limits
let results = manager.retrieve("rust programming", workspace_id, 10)?;workspaces- Project isolationagents- Multi-agent supportepisodes- Episodic events
vec0- Episode embeddings (384 dims)
Storage:
store_episode(episode)→i64
Retrieval:
retrieve(query, workspace_id, limit)→Vec<HybridSearchResult>
Management:
get_memory_stats(workspace_id)→MemoryStats
- Episode storage: ~5ms
- Hybrid search: ~20ms (1000 memories)
- Batch Operations: Use batch methods for bulk inserts
- Index Usage: Ensure indexes on timestamp, workspace_id
- Token Budget: Adjust context budget based on LLM limits
cargo test --quiettest_episodic_store- Episode CRUDtest_hybrid_retrieval- BM25 + vector searchtest_full_pipeline- End-to-end workflow
Total: 29 tests
Each service has one clear purpose:
EpisodicMemoryStore- Episode storage onlyHybridRetrievalEngine- Search only
Extend via traits without modifying existing code:
impl MemoryStore for CustomStore { ... }All stores implement MemoryStore trait interchangeably.
Clients depend only on methods they use:
MemoryStore- CRUD operationsMemoryRetriever- Search only
High-level modules depend on abstractions (traits), not concrete implementations.
let system = MemorySystem::new(db_path)?;
system.learn(text, workspace_id, tags)?;
let results = system.search(query, workspace_id, limit)?;let manager = MemoryManager::new(db);
// Store as episode
manager.store_episode(episode).await?;
// Retrieve with hybrid search
let results = manager.retrieve(query, workspace_id, limit)?;The system maintains compatibility with existing episode data.
Issue: Old tests failing after schema changes Solution: Update tests to use new Episode struct fields
Issue: Search performance degrading Solution: Ensure proper indexing on timestamp and workspace_id fields
- Use minimal, focused implementations
- Follow SOLID principles
- Add tests for new features
- Document public APIs
# Run specific test
cargo test --test test_name
# Run with output
cargo test -- --nocapture
# Check compilation
cargo checkMIT
Built with SOLID principles and minimal code philosophy.