SmartMemory
SmartMemory
Overview
SmartMemory provides a comprehensive memory system for AI agents with four distinct memory layers that work together to create persistent, intelligent applications. The system manages active conversations, historical sessions, structured knowledge, and reusable procedures through a unified interface.
The architecture separates concerns across memory types: working memory handles active sessions, episodic memory stores completed session summaries, semantic memory holds structured knowledge documents, and procedural memory maintains templates and skills. This separation allows AI agents to maintain context while building long-term capabilities.
Key benefits include multi-layered memory architecture, vector-based semantic search, automatic session summarization, and isolated session management with timeline organization.
Prerequisites
- Active Raindrop application with services or actors
- Understanding of AI agent architecture and conversation management
- Familiarity with TypeScript async patterns and vector embeddings
- Knowledge of memory hierarchies and session-based storage patterns
Creating/Getting Started
Configure SmartMemory in your application manifest with optional AI model settings:
application "ai-assistant" { smartmemory "agent_memory" { # Optional: AI model for summarization (defaults to system model) model = "llama-3.2-3b-instruct"
# Optional: custom summarization prompt system_prompt = "Create concise summaries focusing on key decisions and outcomes." }}
The framework generates strongly-typed environment bindings:
interface Env { AGENT_MEMORY: SmartMemory; // Other resources...}
No additional configuration files or setup steps are required. SmartMemory automatically handles schema creation and vector indexing.
Accessing/Basic Usage
Use SmartMemory through the environment binding to access actor-based memory management:
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); const memory = this.env.AGENT_MEMORY;
if (url.pathname === '/chat/start') { // Start new working memory session const { sessionId, workingMemory } = await memory.startWorkingMemorySession();
return Response.json({ sessionId, message: "Conversation started" }); }
if (url.pathname === '/chat/message') { const { sessionId, message } = await request.json();
// Get working memory session const workingMemory = await memory.getWorkingMemorySession(sessionId);
// Store user message await workingMemory.putMemory({ content: `User: ${message}`, key: "user_input", timeline: "conversation" });
// Search for relevant context const context = await workingMemory.searchMemory({ terms: message, timeline: "conversation", nMostRecent: 5 });
// Store assistant response const response = `Assistant response based on: ${message}`; await workingMemory.putMemory({ content: response, key: "assistant_response", timeline: "conversation" });
return Response.json({ response, contextFound: context?.length || 0 }); }
return new Response("Not found", { status: 404 }); }}
Core Concepts
SmartMemory provides a four-layer memory architecture that mimics human cognitive memory systems for AI agents.
Memory Layers: Working memory handles active sessions, episodic memory stores completed session summaries, semantic memory maintains structured knowledge, and procedural memory holds reusable templates and skills.
Session Management: Working memory sessions are managed through actor instances that provide isolated storage and timeline organization for conversation threads.
Vector Search: Semantic and episodic memories use AI embeddings for similarity-based search across stored documents and session summaries.
Automatic Summarization: Sessions can be summarized and moved from working memory to episodic memory using configurable AI models and custom prompts.
Memory Entry Structure
All memory entries follow a consistent structure for metadata and content:
interface MemoryEntry { id: string; // Unique memory identifier in: SessionId; // Session containing this memory timeline: string; // Timeline organization by: string; // Originating user/cause dueTo: string; // Originating user/cause content: string; // The actual memory content at: Date; // Creation timestamp key?: string; // Optional metadata key for filtering agent?: string; // Optional agent identifier}
Query Interfaces
Different query patterns for retrieving and searching memories:
// Exact memory retrieval with filtersinterface MemoryQuery { timeline?: string; // Filter by timeline key?: string; // Filter by metadata key n_most_recent?: number; // Limit to N most recent entries}
// Semantic search across memory contentinterface MemorySearchQuery { query: string; // Search terms or question timeline?: string; // Optional timeline filter limit?: number; // Maximum results to return}
Working Memory Sessions
Manage active conversation sessions through actor-based memory instances with isolated storage and timeline organization.
startWorkingMemorySession()
Create a new working memory session that returns both session ID and actor stub:
startWorkingMemorySession(): Promise<{ sessionId: SessionId; workingMemory: ActorStub<SmartWorkingMemory>}>
Returns: Object containing session identifier and working memory actor stub
const { sessionId, workingMemory } = await memory.startWorkingMemorySession();
console.log(`Started session: ${sessionId}`);
// Use the working memory actor directlyawait workingMemory.putMemory({ content: "Session initialized", timeline: "setup"});
getWorkingMemorySession(sessionId)
Retrieve an existing working memory session by ID:
getWorkingMemorySession(sessionId: string): Promise<ActorStub<SmartWorkingMemory>>
Parameters:
sessionId
: Session identifier to retrieve
Returns: Actor stub for the working memory session
const workingMemory = await memory.getWorkingMemorySession(sessionId);
// Access session dataconst recentMemories = await workingMemory.getMemory({ nMostRecent: 10});
Working Memory Operations
Store and retrieve memories using the working memory actor instance.
putMemory(entry)
Store a memory entry in the working memory session:
// Via working memory actorawait workingMemory.putMemory(entry: NewMemoryEntry): Promise<string>
interface NewMemoryEntry { timeline?: string; // Optional, defaults to "*defaultTimeline" key?: string; // Optional, for metadata filtering content: string; // Required memory content agent?: string; // Optional agent identifier sessionId?: string; // Optional explicit session ID at?: Date; // Optional timestamp (defaults to current time)}
Parameters:
entry
:NewMemoryEntry
- Memory entry with content and optional metadata
Returns: Promise<string>
- Unique memory entry ID
const memoryId = await workingMemory.putMemory({ content: "User asked about refund policy for electronics", key: "user_question", timeline: "support_conversation", agent: "support-bot"});
console.log(`Stored memory: ${memoryId}`);
getMemory(query)
Retrieve memories from working memory with filtering options:
// Via working memory actorawait workingMemory.getMemory(entry: WorkingMemoryQuery): Promise<MemoryEntry[] | null>
interface WorkingMemoryQuery { timeline?: string; // Optional timeline filter key?: string; // Optional metadata key filter nMostRecent?: number; // Optional limit to N most recent startTime?: Date; // Optional time range start endTime?: Date; // Optional time range end}
Parameters:
entry
:WorkingMemoryQuery
- Query parameters for filtering memories
Returns: Promise<MemoryEntry[] | null>
- Array of matching memories or null if none found
// Get recent conversation historyconst recentMemories = await workingMemory.getMemory({ nMostRecent: 10});
// Get specific memory type from timelineconst userQuestions = await workingMemory.getMemory({ key: "user_question", timeline: "support_conversation"});
// Get memories within time rangeconst todayMemories = await workingMemory.getMemory({ startTime: new Date(Date.now() - 24 * 60 * 60 * 1000), endTime: new Date()});
searchMemory(query)
Perform semantic search across working memory content:
// Via working memory actorawait workingMemory.searchMemory(terms: WorkingMemorySearchQuery): Promise<MemoryEntry[] | null>
interface WorkingMemorySearchQuery { timeline?: string; // Optional timeline filter terms: string; // Required search terms nMostRecent?: number; // Optional result limit startTime?: Date; // Optional time range start endTime?: Date; // Optional time range end}
Parameters:
terms
:WorkingMemorySearchQuery
- Search query with terms and optional filters
Returns: Promise<MemoryEntry[] | null>
- Array of matching memories ranked by relevance
const relevantMemories = await workingMemory.searchMemory({ terms: "refund policy electronics return", timeline: "support_conversation", nMostRecent: 5});
if (relevantMemories) { console.log(`Found ${relevantMemories.length} relevant memories`);}
Episodic Memory Management
Store and retrieve summaries of completed conversation sessions for long-term context.
endSession(params)
End a working memory session with optional automatic summarization:
endSession(params: { smartmemory_name: string; application_name: string; session_id: string; flush?: string; // "true" or "false" system_prompt?: string;}): Promise<void>
Parameters:
flush
: Whether to summarize and store in episodic memory (“true”/“false”)system_prompt
: Custom summarization instructions
await memory.endSession({ smartmemory_name: "agent_memory", application_name: "ai-assistant", session_id: session.session_id, flush: "true", system_prompt: "Summarize key issues resolved and customer satisfaction outcome"});
searchEpisodicMemory(params)
Search across episodic memory summaries from previous sessions:
searchEpisodicMemory(params: { smartmemory_name: string; application_name: string; query: string; n_most_recent?: number;}): Promise<EpisodicMemoryResult[]>
const pastSessions = await memory.searchEpisodicMemory({ smartmemory_name: "agent_memory", application_name: "ai-assistant", query: "electronics refund customer complaints", n_most_recent: 5});
rehydrateSession(params)
Restore a previous session from episodic memory back to working memory:
rehydrateSession(params: { smartmemory_name: string; application_name: string; session_id: string; summary_only?: string; // "true" or "false"}): Promise<{ session_id: string }>
const restoredSession = await memory.rehydrateSession({ smartmemory_name: "agent_memory", application_name: "ai-assistant", session_id: "previous-session-123", summary_only: "false" // Restore full conversation history});
Semantic Knowledge Storage
Store and search structured knowledge documents for persistent agent knowledge.
putSemanticMemory(params)
Store a structured knowledge document in semantic memory:
putSemanticMemory(params: { smartmemory_name: string; application_name: string; document: string; // JSON-encoded document}): Promise<{ object_id: string }>
const knowledgeDoc = { title: "Electronics Return Policy", category: "customer_service", content: "Electronics can be returned within 30 days...", tags: ["returns", "electronics", "policy"], lastUpdated: new Date().toISOString()};
await memory.putSemanticMemory({ smartmemory_name: "agent_memory", application_name: "ai-assistant", document: JSON.stringify(knowledgeDoc)});
searchSemanticMemory(params)
Search semantic memory documents using natural language queries:
searchSemanticMemory(params: { smartmemory_name: string; application_name: string; needle: string; // Search query}): Promise<SemanticMemoryResult[]>
const knowledgeResults = await memory.searchSemanticMemory({ smartmemory_name: "agent_memory", application_name: "ai-assistant", needle: "return policy for damaged electronics warranty"});
Procedural Memory Templates
Store and retrieve reusable templates, prompts, and procedures for agent skills.
getProceduralMemory(id?)
Retrieve a procedural memory actor instance:
getProceduralMemory(id?: string): Promise<ActorStub<SmartProceduralMemory>>
Parameters:
id
: Optional procedural memory instance identifier
Returns: Actor stub for procedural memory operations
putProcedure(key, value)
Store a procedure or template for reuse across sessions:
// Via procedural memory actorputProcedure(key: string, value: string): Promise<void>
Parameters:
key
: Unique procedure identifiervalue
: Procedure content or template
const proceduralMemory = await memory.getProceduralMemory();
await proceduralMemory.putProcedure( "RefundProcessingPrompt", `When processing a refund request:1. Verify purchase date and return window2. Check item condition requirements3. Calculate refund amount including taxes4. Process refund to original payment method5. Send confirmation email to customer`);
getProcedure(key)
Retrieve a stored procedure by key:
// Via procedural memory actorgetProcedure(key: string): Promise<string | null>
Parameters:
key
: Procedure identifier
Returns: Procedure content or null if not found
const proceduralMemory = await memory.getProceduralMemory();
const refundProcess = await proceduralMemory.getProcedure( "RefundProcessingPrompt");
if (refundProcess) { console.log("Refund procedure:", refundProcess);}
searchProcedures(query)
Search procedures by key name or content:
// Via procedural memory actorsearchProcedures(query: ProceduralMemorySearchQuery): Promise<ProcedureEntry[] | null>
interface ProceduralMemorySearchQuery { terms: string; // Required search terms nMostRecent?: number; // Optional result limit searchKeys?: boolean; // Search in procedure keys (default: true) searchValues?: boolean; // Search in procedure values (default: true)}
interface ProcedureEntry { key: string; value: string; createdAt: Date; updatedAt: Date;}
Parameters:
query
: Search query with terms and options
Returns: Array of matching procedure entries
const proceduralMemory = await memory.getProceduralMemory();
const refundProcedures = await proceduralMemory.searchProcedures({ terms: "refund return process", searchValues: true, nMostRecent: 5});
if (refundProcedures) { refundProcedures.forEach(procedure => { console.log(`${procedure.key}: ${procedure.value.substring(0, 100)}...`); });}
listProcedures()
Retrieve all stored procedures:
// Via procedural memory actorlistProcedures(): Promise<ProcedureEntry[]>
Returns: Array of all procedure entries
const proceduralMemory = await memory.getProceduralMemory();
const allProcedures = await proceduralMemory.listProcedures();console.log(`Total procedures: ${allProcedures.length}`);
allProcedures.forEach(procedure => { console.log(`${procedure.key} (updated: ${procedure.updatedAt})`);});
deleteProcedure(key)
Remove a procedure by key:
// Via procedural memory actordeleteProcedure(key: string): Promise<boolean>
Parameters:
key
: Procedure identifier to delete
Returns: Boolean indicating success
const proceduralMemory = await memory.getProceduralMemory();
const deleted = await proceduralMemory.deleteProcedure("RefundProcessingPrompt");if (deleted) { console.log("Procedure deleted successfully");}
Code Examples
Complete SmartMemory implementations demonstrating agent conversation management and knowledge systems.
AI Customer Support Agent
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); const memory = this.env.AGENT_MEMORY;
if (url.pathname === '/support/chat') { return this.handleSupportChat(request, memory); }
if (url.pathname === '/support/knowledge') { return this.handleKnowledgeQuery(request, memory); }
return new Response("Not found", { status: 404 }); }
private async handleSupportChat(request: Request, memory: SmartMemory): Promise<Response> { const { sessionId, message, userId } = await request.json();
let currentSessionId = sessionId;
// Start new session if needed if (!sessionId) { const session = await memory.startWorkingMemorySession(); currentSessionId = session.sessionId; }
// Get working memory session const workingMemory = await memory.getWorkingMemorySession(currentSessionId);
// Store user message await workingMemory.putMemory({ content: `Customer: ${message}`, key: "customer_message", timeline: "conversation" });
// Search for relevant context from this session const sessionContext = await workingMemory.searchMemory({ terms: message, timeline: "conversation", nMostRecent: 3 });
// Search episodic memory for similar past issues const episodicResults = await memory.searchEpisodicMemory(message, { nMostRecent: 2 }); const similarIssues = episodicResults.results;
// Search knowledge base const semanticResult = await memory.searchSemanticMemory(message); const knowledgeResults = semanticResult.success ? semanticResult.documentSearchResponse?.results || [] : [];
// Get relevant procedures const proceduralMemory = await memory.getProceduralMemory(); const procedures = await proceduralMemory.searchProcedures({ terms: message, searchValues: true, nMostRecent: 2 }) || [];
// Generate response using context (simplified) const response = await this.generateResponse( message, sessionContext, similarIssues, knowledgeResults, procedures );
// Store assistant response await workingMemory.putMemory({ content: `Assistant: ${response}`, key: "assistant_response", timeline: "conversation" });
return Response.json({ sessionId: currentSessionId, response, contextSources: { sessionMemories: sessionContext?.length || 0, similarCases: similarIssues.length, knowledgeArticles: knowledgeResults.length, procedures: procedures.length } }); }
private async handleKnowledgeQuery(request: Request, memory: SmartMemory): Promise<Response> { const { query, action } = await request.json();
if (action === 'search') { const searchResult = await memory.searchSemanticMemory(query);
if (searchResult.success && searchResult.documentSearchResponse) { return Response.json({ results: searchResult.documentSearchResponse.results.map(r => ({ source: r.source, text: r.text, score: r.score })) }); } else { return Response.json({ results: [], error: searchResult.error }); } }
if (action === 'add') { const { document } = await request.json();
const result = await memory.putSemanticMemory({ ...document, createdAt: new Date().toISOString(), source: "manual_entry" });
return Response.json(result); }
return new Response("Invalid action", { status: 400 }); }
private async generateResponse( message: string, sessionContext: any[], similarIssues: any[], knowledgeResults: any[], procedures: any[] ): Promise<string> { // Simple response generation - in practice, use AI model const contextInfo = sessionContext?.map(m => m.content).join('\n') || ''; const knowledgeInfo = knowledgeResults.map(k => k.text || '').join('\n');
return `Based on our conversation and knowledge base, here's how I can help with: "${message}".
Context from conversation: ${contextInfo.substring(0, 200)}...Relevant knowledge: ${knowledgeInfo.substring(0, 200)}...`; }}
Long-term Learning Agent
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); const memory = this.env.AGENT_MEMORY;
if (url.pathname === '/agent/session/end') { return this.endSessionWithLearning(request, memory); }
if (url.pathname === '/agent/knowledge/update') { return this.updateKnowledge(request, memory); }
return new Response("Not found", { status: 404 }); }
private async endSessionWithLearning(request: Request, memory: SmartMemory): Promise<Response> { const { sessionId, learningOutcomes } = await request.json();
// Store learning outcomes as semantic knowledge if (learningOutcomes && learningOutcomes.length > 0) { for (const outcome of learningOutcomes) { await memory.putSemanticMemory({ type: "learning_outcome", sessionId, outcome: outcome.description, evidence: outcome.evidence, confidence: outcome.confidence, category: outcome.category, timestamp: new Date().toISOString() }); } }
// End session with summarization const workingMemory = await memory.getWorkingMemorySession(sessionId); await workingMemory.endSession(true);
return Response.json({ success: true, learningOutcomes: learningOutcomes.length }); }
private async updateKnowledge(request: Request, memory: SmartMemory): Promise<Response> { const { topic, insights, sources } = await request.json();
// Search existing knowledge for this topic const searchResult = await memory.searchSemanticMemory(topic); const existingKnowledge = searchResult.success ? searchResult.documentSearchResponse?.results || [] : [];
// Create comprehensive knowledge entry const knowledgeEntry = { topic, insights, sources, relatedEntries: existingKnowledge.map(k => k.source), lastUpdated: new Date().toISOString(), version: existingKnowledge.length + 1 };
await memory.putSemanticMemory(knowledgeEntry);
// Update learning procedures if patterns emerge if (insights.length >= 3) { const procedureKey = `${topic}_analysis_pattern`; const procedure = `For analyzing ${topic}:${insights.map((insight, i) => `${i + 1}. ${insight}`).join('\n')}
Sources to consult: ${sources.join(', ')}Related knowledge entries: ${existingKnowledge.length}`;
const proceduralMemory = await memory.getProceduralMemory(); await proceduralMemory.putProcedure(procedureKey, procedure); }
return Response.json({ success: true, knowledgeId: "generated-id", relatedEntries: existingKnowledge.length, procedureUpdated: insights.length >= 3 }); }}
## raindrop.manifest
Configure SmartMemory instances in your manifest for intelligent memory management:
```hclapplication "ai-assistant" { smartmemory "user-context" { # SmartMemory for user conversation context }
smartmemory "knowledge-base" { # Long-term knowledge and learning }
service "chat-api" { domain = "chat.example.com" # Service can access SmartMemory via env.USER_CONTEXT, env.KNOWLEDGE_BASE }
actor "conversation-manager" { # Actors can also use SmartMemory for context management }}