Memory System

The memory system provides hierarchical storage for conversations, knowledge, and agent state. It enables agents to maintain context, learn from interactions, and build persistent knowledge. For conceptual overview, see Memory and State. For runtime architecture, see Runtime Core.

Memory Types

Core Memory Types

enum MemoryType {
  MESSAGE = 'message',           // Conversation messages
  FACT = 'fact',                // Extracted knowledge
  DOCUMENT = 'document',         // Document storage
  RELATIONSHIP = 'relationship', // Entity relationships
  GOAL = 'goal',                // Agent goals
  TASK = 'task',                // Scheduled tasks
  ACTION = 'action',            // Action execution records
}

Memory Interface

interface Memory {
  id: UUID;
  type: MemoryType;
  roomId: UUID;
  userId?: UUID;
  agentId?: UUID;
  content: {
    text: string;
    [key: string]: any;
  };
  embedding?: number[];
  createdAt: Date;
  updatedAt?: Date;
  metadata?: Record<string, any>;
}

State Management

State Structure

State represents the agent’s current understanding of context:
interface State {
  // Key-value pairs for template access
  values: Record<string, any>;
  
  // Structured data from providers
  data: Record<string, any>;
  
  // Concatenated textual context
  text: string;
}

State Composition Pipeline

Memory Operations

Creating Memories

// Store a message
await runtime.createMemory({
  type: MemoryType.MESSAGE,
  content: { 
    text: "User message",
    role: 'user',
    name: 'John'
  },
  roomId: message.roomId,
  userId: message.userId,
  metadata: {
    platform: 'discord',
    channelId: '12345'
  }
});

// Store a fact
await runtime.createMemory({
  type: MemoryType.FACT,
  content: {
    text: "The user's favorite color is blue",
    subject: 'user',
    predicate: 'favorite_color',
    object: 'blue'
  },
  roomId: message.roomId
});

// Store an action result
await runtime.createMemory({
  type: MemoryType.ACTION,
  content: {
    text: "Generated image of a sunset",
    action: 'IMAGE_GENERATION',
    result: { url: 'https://...' }
  },
  roomId: message.roomId,
  agentId: runtime.agentId
});

Searching Memories

// Text search with embeddings
const memories = await runtime.searchMemories(
  "previous conversation about colors",
  10 // limit
);

// Search with filters
const facts = await runtime.searchMemories({
  query: "user preferences",
  type: MemoryType.FACT,
  roomId: currentRoom.id,
  limit: 5
});

// Search by time range
const recentMessages = await runtime.searchMemories({
  type: MemoryType.MESSAGE,
  roomId: currentRoom.id,
  after: new Date(Date.now() - 3600000), // Last hour
  limit: 20
});

Memory Retrieval

// Get specific memory
const memory = await runtime.getMemoryById(memoryId);

// Get memories by room
const roomMemories = await runtime.getMemoriesByRoom(
  roomId,
  { type: MemoryType.MESSAGE, limit: 50 }
);

// Get user memories
const userMemories = await runtime.getMemoriesByUser(
  userId,
  { type: MemoryType.FACT }
);

Embeddings and Similarity

Creating Embeddings

// Generate embedding for text
const embedding = await runtime.useModel(
  ModelType.TEXT_EMBEDDING,
  { input: "Text to embed" }
);

// Store with embedding
await runtime.createMemory({
  type: MemoryType.MESSAGE,
  content: { text: "Important message" },
  embedding: embedding,
  roomId: message.roomId
});
// Search by semantic similarity
const similarMemories = await runtime.searchMemoriesBySimilarity(
  embedding,
  {
    threshold: 0.8,  // Similarity threshold (0-1)
    limit: 10,
    type: MemoryType.MESSAGE
  }
);

// Find related facts
const queryEmbedding = await runtime.useModel(
  ModelType.TEXT_EMBEDDING,
  { input: "What does the user like?" }
);

const relatedFacts = await runtime.searchMemoriesBySimilarity(
  queryEmbedding,
  {
    type: MemoryType.FACT,
    threshold: 0.7,
    limit: 5
  }
);

Facts and Knowledge

Fact Extraction

Facts are automatically extracted from conversations:
interface Fact extends Memory {
  type: MemoryType.FACT;
  content: {
    text: string;
    subject?: string;    // Entity the fact is about
    predicate?: string;  // Relationship or property
    object?: string;     // Value or related entity
    confidence?: number; // Extraction confidence
    source?: string;     // Source message ID
  };
}

Fact Management

// Create a fact
await runtime.createFact({
  subject: 'user',
  predicate: 'works_at',
  object: 'TechCorp',
  confidence: 0.95,
  source: message.id
});

// Query facts
const userFacts = await runtime.getFacts({
  subject: 'user',
  limit: 10
});

// Update fact confidence
await runtime.updateFact(factId, {
  confidence: 0.98
});

Relationships

Relationship Storage

interface Relationship extends Memory {
  type: MemoryType.RELATIONSHIP;
  userId: UUID;
  targetEntityId: UUID;
  relationshipType: string;
  strength: number;
  metadata?: {
    firstInteraction?: Date;
    lastInteraction?: Date;
    interactionCount?: number;
    sentiment?: number;
  };
}

Managing Relationships

// Create relationship
await runtime.createRelationship({
  userId: user.id,
  targetEntityId: otherUser.id,
  relationshipType: 'friend',
  strength: 0.8
});

// Get user relationships
const relationships = await runtime.getRelationships(userId);

// Update relationship strength
await runtime.updateRelationship(relationshipId, {
  strength: 0.9,
  metadata: {
    lastInteraction: new Date(),
    interactionCount: prevCount + 1
  }
});

State Cache

Cache Architecture

The runtime maintains an in-memory cache for composed states:
class StateCache {
  private cache: Map<UUID, State>;
  private timestamps: Map<UUID, number>;
  private maxSize: number;
  private ttl: number;
  
  constructor(maxSize = 1000, ttl = 300000) { // 5 min TTL
    this.cache = new Map();
    this.timestamps = new Map();
    this.maxSize = maxSize;
    this.ttl = ttl;
  }
  
  set(messageId: UUID, state: State): void {
    // Evict oldest if at capacity
    if (this.cache.size >= this.maxSize) {
      const oldest = this.getOldestEntry();
      if (oldest) {
        this.cache.delete(oldest);
        this.timestamps.delete(oldest);
      }
    }
    
    this.cache.set(messageId, state);
    this.timestamps.set(messageId, Date.now());
  }
  
  get(messageId: UUID): State | undefined {
    const timestamp = this.timestamps.get(messageId);
    
    // Check if expired
    if (timestamp && Date.now() - timestamp > this.ttl) {
      this.cache.delete(messageId);
      this.timestamps.delete(messageId);
      return undefined;
    }
    
    return this.cache.get(messageId);
  }
}

Cache Management

// Clear old cache entries
function cleanupCache(runtime: IAgentRuntime) {
  const now = Date.now();
  const maxAge = 5 * 60 * 1000; // 5 minutes
  
  for (const [messageId, timestamp] of runtime.stateCache.timestamps) {
    if (now - timestamp > maxAge) {
      runtime.stateCache.delete(messageId);
    }
  }
}

// Schedule periodic cleanup
setInterval(() => cleanupCache(runtime), 60000);

Document Storage

Document Memory

interface DocumentMemory extends Memory {
  type: MemoryType.DOCUMENT;
  content: {
    text: string;
    title?: string;
    source?: string;
    chunks?: string[];
    summary?: string;
  };
  embedding?: number[];
  metadata?: {
    mimeType?: string;
    size?: number;
    hash?: string;
    tags?: string[];
  };
}

Document Operations

// Store document
await runtime.createDocument({
  title: 'User Manual',
  content: documentText,
  source: 'https://example.com/manual.pdf',
  chunks: splitIntoChunks(documentText),
  metadata: {
    mimeType: 'application/pdf',
    size: 1024000,
    tags: ['manual', 'reference']
  }
});

// Search documents
const relevantDocs = await runtime.searchDocuments(
  "how to configure settings",
  { limit: 5 }
);

// Get document chunks
const chunks = await runtime.getDocumentChunks(
  documentId,
  { relevant_to: "specific query" }
);

Memory Cleanup

Automatic Cleanup

class MemoryCleanupService {
  private readonly MAX_MESSAGE_AGE = 30 * 24 * 60 * 60 * 1000; // 30 days
  private readonly MAX_FACTS_PER_ROOM = 1000;
  
  async cleanup(runtime: IAgentRuntime) {
    // Remove old messages
    await this.cleanupOldMessages(runtime);
    
    // Consolidate facts
    await this.consolidateFacts(runtime);
    
    // Remove orphaned relationships
    await this.cleanupOrphanedRelationships(runtime);
  }
  
  private async cleanupOldMessages(runtime: IAgentRuntime) {
    const cutoffDate = new Date(Date.now() - this.MAX_MESSAGE_AGE);
    
    await runtime.deleteMemories({
      type: MemoryType.MESSAGE,
      before: cutoffDate
    });
  }
  
  private async consolidateFacts(runtime: IAgentRuntime) {
    const rooms = await runtime.getAllRooms();
    
    for (const room of rooms) {
      const facts = await runtime.getFacts({ roomId: room.id });
      
      if (facts.length > this.MAX_FACTS_PER_ROOM) {
        // Keep only high-confidence recent facts
        const toKeep = facts
          .sort((a, b) => {
            const scoreA = a.confidence * (1 / (Date.now() - a.createdAt));
            const scoreB = b.confidence * (1 / (Date.now() - b.createdAt));
            return scoreB - scoreA;
          })
          .slice(0, this.MAX_FACTS_PER_ROOM);
        
        const toDelete = facts.filter(f => !toKeep.includes(f));
        for (const fact of toDelete) {
          await runtime.deleteMemory(fact.id);
        }
      }
    }
  }
}

Manual Cleanup

// Delete specific memories
await runtime.deleteMemory(memoryId);

// Bulk delete
await runtime.deleteMemories({
  type: MemoryType.MESSAGE,
  roomId: roomId,
  before: cutoffDate
});

// Clear room memories
await runtime.clearRoomMemories(roomId);

Memory Optimization

Indexing Strategies

// Database indexes for performance
CREATE INDEX idx_memories_room_type ON memories(roomId, type);
CREATE INDEX idx_memories_user_created ON memories(userId, createdAt);
CREATE INDEX idx_memories_embedding ON memories USING ivfflat (embedding);
CREATE INDEX idx_facts_subject_predicate ON facts(subject, predicate);

Batch Operations

// Batch insert memories
await runtime.createMemoriesBatch([
  { type: MemoryType.MESSAGE, content: { text: "Message 1" }, roomId },
  { type: MemoryType.MESSAGE, content: { text: "Message 2" }, roomId },
  // ... more memories
]);

// Batch embedding generation
const texts = memories.map(m => m.content.text);
const embeddings = await runtime.useModel(
  ModelType.TEXT_EMBEDDING,
  { input: texts, batch: true }
);

Memory Compression

// Compress old memories
async function compressMemories(runtime: IAgentRuntime, roomId: UUID) {
  const messages = await runtime.getMemories({
    type: MemoryType.MESSAGE,
    roomId,
    before: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // 7 days
  });
  
  // Generate summary
  const summary = await runtime.useModel(
    ModelType.TEXT_LARGE,
    {
      prompt: `Summarize these messages: ${messages.map(m => m.content.text).join('\n')}`,
      maxTokens: 500
    }
  );
  
  // Store summary
  await runtime.createMemory({
    type: MemoryType.DOCUMENT,
    content: {
      text: summary,
      title: 'Conversation Summary',
      source: 'compressed_messages'
    },
    roomId,
    metadata: {
      originalCount: messages.length,
      dateRange: {
        start: messages[0].createdAt,
        end: messages[messages.length - 1].createdAt
      }
    }
  });
  
  // Delete original messages
  for (const message of messages) {
    await runtime.deleteMemory(message.id);
  }
}

Best Practices

Memory Design

  • Type Selection: Use appropriate memory types for different data
  • Embedding Strategy: Generate embeddings for searchable content
  • Metadata Usage: Store relevant metadata for filtering
  • Relationship Tracking: Maintain entity relationships
  • Fact Extraction: Extract and store facts from conversations

Performance

  • Indexing: Create appropriate database indexes
  • Batch Operations: Use batch operations for multiple items
  • Caching: Cache frequently accessed memories
  • Cleanup: Implement regular cleanup routines
  • Compression: Compress old data to save space

Data Integrity

  • Validation: Validate memory content before storage
  • Deduplication: Prevent duplicate facts and relationships
  • Consistency: Maintain referential integrity
  • Versioning: Track memory updates and changes
  • Backup: Regular backup of critical memories

What’s Next?