Skip to content

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 filters
interface 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 content
interface 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 directly
await 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 data
const 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 actor
await 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 actor
await 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 history
const recentMemories = await workingMemory.getMemory({
nMostRecent: 10
});
// Get specific memory type from timeline
const userQuestions = await workingMemory.getMemory({
key: "user_question",
timeline: "support_conversation"
});
// Get memories within time range
const 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 actor
await 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 actor
putProcedure(key: string, value: string): Promise<void>

Parameters:

  • key: Unique procedure identifier
  • value: Procedure content or template
const proceduralMemory = await memory.getProceduralMemory();
await proceduralMemory.putProcedure(
"RefundProcessingPrompt",
`When processing a refund request:
1. Verify purchase date and return window
2. Check item condition requirements
3. Calculate refund amount including taxes
4. Process refund to original payment method
5. Send confirmation email to customer`
);

getProcedure(key)

Retrieve a stored procedure by key:

// Via procedural memory actor
getProcedure(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 actor
searchProcedures(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 actor
listProcedures(): 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 actor
deleteProcedure(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:
```hcl
application "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
}
}