AI Agent Memory Architecture: Designing Long-Term Memory
A systematic guide to giving AI agents long-term memory. Learn how to build a practical memory system combining vector databases, episodic memory, and semantic search.
One of the biggest constraints facing modern AI agents is maintaining long-term context. When every conversation resets to zero, users end up re-explaining the same things, past lessons go unlearned, and the agent never picks up on preferences or habits. These friction points seriously undermine an agent's practical value.
"Agent memory architecture" — inspired by the memory systems of the human brain — is the key to solving this problem. An agent with a well-designed memory system can learn from past conversations, remember user preferences, draw lessons from failures, and grow smarter over time.
This article breaks down a three-layer memory architecture grounded in cognitive science and walks through its real-world implementation.
The Three-Layer Memory Architecture
Drawing from human memory research, we organize agent memory into three distinct layers.
Layer 1: Episodic Memory
This stores memories of specific events — "what happened, when, and where." Conversation logs, task execution history, errors encountered and how they were resolved all belong here.
Episodic memory is context-dependent. It holds information tied to specific situations: "The API key expired and caused a failure last Tuesday," "User A prefers concise responses."
Layer 2: Semantic Memory
This stores general knowledge and facts — knowledge extracted and abstracted from episodic memory. "This user prefers Markdown formatting," "This project is written in Python," "This type of error is usually caused by a timeout." These are insights that apply across situations.
Layer 3: Procedural Memory
This covers skills and procedures — "how to do things." It includes task patterns that have repeatedly succeeded, effective prompt templates, and commonly used code snippets. This is the most stable type of memory and changes rarely.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Three-layer agent memory design using episodic, semantic, and procedural memory
✦Vector memory implementation patterns with pgvector, Qdrant, and Pinecone
✦How to implement memory compression, prioritization, and forgetting algorithms
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Matching storage to memory type and use case is critical to building an effective memory system.
Selecting a Vector Database
Vector databases are the right tool for retrieving memories based on semantic similarity. Here are the main options:
pgvector (PostgreSQL extension): Adds vector search to an existing PostgreSQL setup. Excels at hybrid queries that combine SQL and vector search. A good fit for mid-sized systems — self-hostable and cost-efficient, making it popular from startups to mid-sized companies.
Qdrant: A high-performance open-source vector database. Its rich filtering capabilities shine for complex queries like "semantically search episodic memories from the past 30 days tagged as error handling."
Pinecone: A fully managed cloud service that abstracts away large-scale vector data management entirely, letting you focus on building the memory system itself. Best suited for large-scale production environments.
Combining with a Relational Database
Vector databases handle embeddings, but you also need a relational database for metadata management. A typical setup stores metadata (timestamps, categories, importance scores, etc.) in PostgreSQL and embedding vectors in the vector DB, combining both in searches.
Implementation: Core Memory System Components
Designing the Memory Object
Start by defining the fundamental memory structure:
The importance field determines retention priority. Emotionally significant experiences or frequently accessed information get higher importance scores and are retained longer.
Storing Memories and Generating Embeddings
class MemoryManager { private vectorStore: VectorDatabase; private db: PostgreSQLDatabase; private embedder: EmbeddingModel; async storeMemory( content: string, type: Memory['type'], metadata: Partial<Memory['metadata']> ): Promise<Memory> { // 1. Convert text to vector const embedding = await this.embedder.embed(content); // 2. Calculate importance (from length, emotion, reference count, etc.) const importance = await this.calculateImportance(content, metadata); // 3. Create memory object const memory: Memory = { id: generateUUID(), type, content, embedding, importance, timestamp: new Date(), lastAccessed: new Date(), accessCount: 0, metadata: { tags: [], source: 'agent', ...metadata } }; // 4. Store in both vector DB and relational DB await Promise.all([ this.vectorStore.upsert(memory.id, embedding, { type, importance }), this.db.insert('memories', memory) ]); return memory; }}
Memory Management: Compression, Prioritization, and Forgetting
Calculating Importance
The importance score is derived from multiple factors:
async calculateImportance( content: string, metadata: Partial<Memory['metadata']>): Promise<number> { let score = 0.5; // base score // Emotional content is more important const emotionalKeywords = ['critical', 'urgent', 'error', 'success', 'failure', 'warning']; const emotionalScore = emotionalKeywords.filter(k => content.toLowerCase().includes(k)).length * 0.1; score += Math.min(emotionalScore, 0.3); // Longer content carries more information const lengthScore = Math.min(content.length / 1000, 0.2); score += lengthScore; // User explicitly asked to remember this if (metadata.tags?.includes('explicit_remember')) { score += 0.3; } return Math.min(score, 1.0);}
Memory Management Based on the Forgetting Curve
Inspired by Ebbinghaus's forgetting curve from human memory research, memories that aren't accessed gradually decrease in importance and are eventually archived or deleted:
As episodic memories accumulate, consolidating similar memories into semantic memories becomes important:
async consolidateMemories(): Promise<void> { // Cluster similar memories const episodicMemories = await this.db.query( 'SELECT * FROM memories WHERE type = ? AND importance > ?', ['episodic', 0.5] ); const clusters = await this.clusterSimilarMemories(episodicMemories); for (const cluster of clusters) { if (cluster.length >= 3) { // Generate semantic memory from multiple similar episodes const summary = await this.generateSemanticMemory(cluster); await this.storeMemory( summary, 'semantic', { tags: ['consolidated', ...extractTags(cluster)], source: 'consolidation' } ); // Reduce importance of source episodic memories for (const memory of cluster) { await this.db.update('memories', memory.id, { importance: memory.importance * 0.5 }); } } }}
Integrating Memory into Agents
The Context Enrichment Flow
Here's how to integrate the memory system into an agent's prompt:
async buildContextWithMemory( userMessage: string, userId: string): Promise<string> { // 1. Retrieve relevant memories const relevantMemories = await this.recallMemories(userMessage, { limit: 10, minImportance: 0.3 }); // 2. Organize by memory type const episodicContext = relevantMemories .filter(m => m.type === 'episodic') .map(m => `[Past event] ${m.content}`) .join('\n'); const semanticContext = relevantMemories .filter(m => m.type === 'semantic') .map(m => `[Learned knowledge] ${m.content}`) .join('\n'); const proceduralContext = relevantMemories .filter(m => m.type === 'procedural') .map(m => `[Effective procedure] ${m.content}`) .join('\n'); // 3. Embed context into system prompt return `You have the following memories available:## Relevant Past Events${episodicContext || '(none)'}## Learned Knowledge${semanticContext || '(none)'}## Effective Patterns and Procedures${proceduralContext || '(none)'}Using these memories as context, respond to the following:${userMessage} `.trim();}
Updating Memory After Conversations
Automatically extract and store important information after each conversation ends:
async processConversationEnd( conversation: ConversationTurn[], userId: string): Promise<void> { // Use LLM to extract important information from the conversation const extractionPrompt = `Extract information from the following conversation that would be useful in future interactions.For each piece of information, determine its type (episodic/semantic/procedural) and importance (0-1).Conversation:${conversation.map(t => `${t.role}: ${t.content}`).join('\n')}Output in JSON format:{ "memories": [ {"content": "...", "type": "...", "importance": 0.x} ]} `; const extracted = await this.llm.complete(extractionPrompt); const { memories } = JSON.parse(extracted); for (const memory of memories) { await this.storeMemory(memory.content, memory.type, { userId, tags: ['auto_extracted'] }); }}
Production Optimization
Performance Considerations
The memory system directly impacts agent response time, making performance optimization essential.
Vector search caching: When the same query is issued multiple times in a short window, caching results in Redis or a similar store provides dramatic speedups.
In-memory storage: Keep episodic memories about the current conversation in memory rather than hitting the database on every turn. Persist to the database in a batch operation when the conversation ends.
Async memory updates: Run memory saves and updates asynchronously from the main processing flow so they never block the user's response.
Privacy and Security
Memory systems that may contain personal or sensitive user information require particularly careful privacy design.
Encrypt memories at rest and in transit. Provide users with the ability to view and delete their stored memories. Set explicit retention periods with automatic deletion policies. And filter out sensitive information (API keys, passwords, etc.) automatically before storage.
A Note from an Indie Developer
Closing Thoughts
AI agent memory architecture is a sophisticated system grounded in cognitive science — quite different from simple conversation history storage. By combining a three-layer structure (episodic, semantic, procedural), vector-based semantic retrieval, and mechanisms for forgetting and reinforcement, you can build an agent that continues to grow over time.
Rather than being daunted by the complexity, start with a simple episodic memory implementation and gradually add more advanced features. Watching an agent with memory build genuine trust with users and evolve toward a true personal assistant is one of the most rewarding parts of agent development.
We look forward to seeing the remarkable agents you build.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.