Memory
DSPy.rb provides a memory system that allows agents to store and retrieve information across interactions. The memory system includes data structures for storing memories, storage backends, and tools for agent integration.
Basic Usage
Memory Manager
The memory system is accessed through DSPy::Memory.manager
:
# Store a memory
record = DSPy::Memory.manager.store_memory(
"Dark mode enabled",
user_id: "user123",
tags: ["ui", "preference"]
)
# Retrieve a memory by ID
memory = DSPy::Memory.manager.get_memory(record.id)
puts memory.content # => "Dark mode enabled"
# Search memories
results = DSPy::Memory.manager.search_memories("preference")
puts results.first.content # => "Dark mode enabled"
Memory Records
Memory records are the basic unit of storage:
# Create a memory record
record = DSPy::Memory::MemoryRecord.new(
content: "User prefers email notifications",
user_id: "user123",
tags: ["notification", "email"],
metadata: { source: "user_input" }
)
# Access record properties
puts record.id # => "generated-uuid"
puts record.content # => "User prefers email notifications"
puts record.tags # => ["notification", "email"]
puts record.created_at # => 2025-01-01 12:00:00 UTC
Memory with Agents
Using Memory Toolset
The MemoryToolset
provides tools that agents can use to interact with memory:
class PersonalAssistant < DSPy::Signature
description "Personal assistant with memory"
input do
const :query, String
end
output do
const :response, String
end
end
# Create agent with memory tools
memory_tools = DSPy::Tools::MemoryToolset.to_tools
agent = DSPy::ReAct.new(
PersonalAssistant,
tools: memory_tools
)
# Agent can now store and retrieve memories
result = agent.call(query: "Remember that I like dark mode")
# Agent will use memory_store tool to save this preference
result = agent.call(query: "What theme do I prefer?")
# Agent will use memory_search tool to find the preference
Available Memory Operations
The memory toolset provides these operations to agents:
memory_store(key:, value:, tags: nil)
- Store a memorymemory_retrieve(key:)
- Retrieve by keymemory_search(pattern:)
- Search memoriesmemory_list()
- List all memoriesmemory_update(key:, value:)
- Update existing memorymemory_delete(key:)
- Delete a memorymemory_clear()
- Clear all memoriesmemory_count()
- Count stored memoriesmemory_get_metadata(key:)
- Get memory metadata
Storage Backends
In-Memory Storage (Default)
The default storage keeps memories in memory:
# Uses in-memory storage by default
manager = DSPy::Memory::MemoryManager.new
manager.store(key: "test", value: "data")
Custom Storage
You can implement custom storage backends by extending MemoryStore
:
class CustomMemoryStore < DSPy::Memory::MemoryStore
def store(record)
# Your storage implementation
end
def retrieve(id)
# Your retrieval implementation
end
# Implement other required methods...
end
# Use custom storage
manager = DSPy::Memory::MemoryManager.new(store: CustomMemoryStore.new)
Memory Compaction
The memory system includes automatic compaction to prevent unlimited growth:
# Configure compaction thresholds
manager = DSPy::Memory::MemoryManager.new
# Compaction is configured when creating the manager
# Default settings: max 1000 memories, 90 days max age, 0.95 similarity threshold
# Compaction runs automatically during normal operations
manager.store_memory("test content", user_id: "user123") # May trigger compaction
# You can also force compaction
manager.force_compact!
Best Practices
1. Use Descriptive Content
# Good: Clear, descriptive content
manager.store_memory(
"User prefers dark theme for UI",
user_id: "user123",
tags: ["preference", "ui"]
)
# Avoid: Generic content
manager.store_memory("dark", user_id: "user123")
2. Use Tags for Organization
# Tag memories for easy searching
manager.store_memory(
"User prefers email notifications",
user_id: "user123",
tags: ["notification", "user_preference", "email"]
)
# Search by category
email_prefs = manager.search_by_tags(["email", "notification"])
3. Structure Memory Content
# Store structured data as JSON
preference_data = {
theme: "dark",
notifications: true,
language: "en"
}
manager.store_memory(
preference_data.to_json,
user_id: "user123",
tags: ["preferences", "user_settings"]
)
4. Handle Memory Retrieval Gracefully
def get_user_preference(memory_id)
memory = DSPy::Memory.manager.get_memory(memory_id)
return memory&.content || "default_value"
end
# Or with error handling
def get_user_preference(memory_id)
memory = DSPy::Memory.manager.get_memory(memory_id)
memory ? memory.content : "default_value"
rescue => e
logger.warn("Failed to retrieve memory: #{e.message}")
"default_value"
end
Testing Memory
Test Memory Operations
RSpec.describe "Memory operations" do
before do
DSPy::Memory.reset! # Clear memory for each test
end
it "stores and retrieves memories" do
record = DSPy::Memory.manager.store_memory(
"test_value",
user_id: "test_user",
tags: ["test"]
)
memory = DSPy::Memory.manager.get_memory(record.id)
expect(memory.content).to eq("test_value")
expect(memory.tags).to include("test")
end
end
Test Agents with Memory
RSpec.describe "Agent with memory" do
let(:memory_tools) { DSPy::Tools::MemoryToolset.to_tools }
let(:agent) { DSPy::ReAct.new(TestSignature, tools: memory_tools) }
before do
DSPy::Memory.reset!
end
it "can store and recall information" do
# Agent stores information
agent.call(query: "Remember my name is John")
# Agent recalls information
result = agent.call(query: "What is my name?")
expect(result.response).to include("John")
end
end
Limitations
- Storage: Default in-memory storage is not persistent across process restarts
- Concurrency: Memory operations are not thread-safe by default
- Search: Text search is basic pattern matching, not semantic search
- Scaling: Large memory stores may impact performance
For production use with persistent storage, implement a custom storage backend using your preferred database or storage system.
Next Steps
- Learn about Toolsets to understand how memory tools work
- See Advanced Memory Systems for compaction and optimization
- Read about Stateful Agents for production patterns