Skip to content

Memory System

Judah Paul edited this page Feb 18, 2026 · 5 revisions

🧠 Memory System

GPT Home uses LangMem for persistent memory storage, enabling the assistant to remember user preferences, past interactions, and important context across conversations.

Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Memory System                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                     MemoryManager                           β”‚   β”‚
β”‚  β”‚                     (Facade Pattern)                        β”‚   β”‚
β”‚  β”‚                                                             β”‚   β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚   β”‚
β”‚  β”‚  β”‚    Semantic     β”‚        β”‚    Episodic     β”‚             β”‚   β”‚
β”‚  β”‚  β”‚    Manager      β”‚        β”‚    Manager      β”‚             β”‚   β”‚
β”‚  β”‚  β”‚                 β”‚        β”‚                 β”‚             β”‚   β”‚
β”‚  β”‚  β”‚ User prefs,     β”‚        β”‚ Conversation    β”‚             β”‚   β”‚
β”‚  β”‚  β”‚ facts, topics   β”‚        β”‚ summaries,      β”‚             β”‚   β”‚
β”‚  β”‚  β”‚                 β”‚        β”‚ interactions    β”‚             β”‚   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚   β”‚
β”‚  β”‚           β”‚                          β”‚                      β”‚   β”‚
β”‚  β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β”‚   β”‚
β”‚  β”‚                      β–Ό                                      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                         β”‚                                          β”‚
β”‚                         β–Ό                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚              AsyncPostgresStore (LangGraph)                 β”‚   β”‚
β”‚  β”‚                                                             β”‚   β”‚
β”‚  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚   β”‚
β”‚  β”‚   β”‚  PostgreSQL + pgvector                               β”‚  β”‚   β”‚
β”‚  β”‚   β”‚                                                      β”‚  β”‚   β”‚
β”‚  β”‚   β”‚  β€’ Vector embeddings (1536 dims)                     β”‚  β”‚   β”‚
β”‚  β”‚   β”‚  β€’ Semantic similarity search                        β”‚  β”‚   β”‚
β”‚  β”‚   β”‚  β€’ Namespaced storage per user                       β”‚  β”‚   β”‚
β”‚  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Memory Types

GPT Home implements three types of memories following LangMem's conceptual model:

1. Semantic Memories

Facts and knowledge about the user

class MemoryType(Enum):
    SEMANTIC = auto()    # Facts and knowledge

Examples:

  • User preferences: "User prefers Celsius for temperature"
  • Personal facts: "User's name is John"
  • Recurring interests: "User often asks about stocks"

2. Episodic Memories

Past experiences and interactions

class MemoryType(Enum):
    EPISODIC = auto()    # Past experiences

Examples:

  • "User asked about weather in Paris last week"
  • "User scheduled a meeting with Bob on Tuesday"
  • "Successful alarm set for 7 AM worked well"

3. Procedural Memories

Learned system behaviors (future enhancement)

class MemoryType(Enum):
    PROCEDURAL = auto()  # System behavior

Examples:

  • Optimized prompts for specific tasks
  • Learned response patterns

MemoryManager Class

The MemoryManager in src/memory/manager.py provides a unified interface:

class MemoryManager:
    """Manages agent memories using the Facade pattern.
    
    Provides a unified interface for:
    - Semantic memories (user preferences, facts)
    - Episodic memories (conversation history)
    - Procedural memories (learned behaviors)
    """
    
    def __init__(
        self,
        store: BaseStore,
        model: str = "gpt-4o-mini",
        user_id: str = "default"
    ):
        self.store = store
        self.model = model
        self.user_id = user_id
        
        # Semantic memory manager
        self._semantic_manager = create_memory_store_manager(
            model,
            store=store,
            namespace=("memories", "{user_id}", "semantic"),
            instructions="""Extract and manage semantic memories about the user.
Focus on:
- Personal preferences (display mode, communication style, etc.)
- Important facts shared by the user
- Recurring topics of interest
Consolidate related memories to avoid redundancy."""
        )
        
        # Episodic memory manager
        self._episodic_manager = create_memory_store_manager(
            model,
            store=store,
            namespace=("memories", "{user_id}", "episodic"),
            instructions="""Extract episodic memories from conversations.
Focus on:
- Successful interaction patterns
- Notable events or requests
- Context that might be relevant for future interactions"""
        )

Key Methods

Method Description
add_semantic_memory(content, user_id) Store a fact about the user
add_episodic_memory(content, user_id) Store an interaction record
search_memories(query, user_id, limit) Semantic search for relevant memories
get_all_memories(user_id) Retrieve all memories for a user

Memory Storage Architecture

PostgreSQL Schema

-- Memory Store Table (LangGraph AsyncPostgresStore)
CREATE TABLE store (
    prefix TEXT,              -- Namespace: ("memories", "user_id", "type")
    key TEXT,                 -- Unique memory ID
    value JSONB,              -- Memory content
    created_at TIMESTAMPTZ,
    updated_at TIMESTAMPTZ,
    embedding VECTOR(1536),   -- pgvector embedding
    PRIMARY KEY (prefix, key)
);

-- Vector similarity index for semantic search
CREATE INDEX store_embedding_idx 
ON store USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

Namespace Structure

memories/
β”œβ”€β”€ user_1/
β”‚   β”œβ”€β”€ semantic/
β”‚   β”‚   β”œβ”€β”€ pref_temperature_celsius
β”‚   β”‚   β”œβ”€β”€ fact_name_john
β”‚   β”‚   └── interest_stocks
β”‚   └── episodic/
β”‚       β”œβ”€β”€ interaction_20240115_weather
β”‚       └── interaction_20240116_meeting
β”œβ”€β”€ user_2/
β”‚   β”œβ”€β”€ semantic/
β”‚   └── episodic/
└── default/
    β”œβ”€β”€ semantic/
    └── episodic/

Memory Flow

Storing Memories (Background Processing)

User: "I prefer temperatures in Celsius"
              β”‚
              β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚  Agent    │──────▢ Response: "Got it! I'll use Celsius."
        β”‚  Response β”‚
        β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚    Background Memory Processing               β”‚
        β”‚    (_background_memory_processing)            β”‚
        β”‚                                               β”‚
        β”‚    1. Analyze conversation                    β”‚
        β”‚    2. Extract memory-worthy content           β”‚
        β”‚    3. Classify as semantic/episodic           β”‚
        β”‚    4. Generate embedding via LiteLLM          β”‚
        β”‚    5. Store in PostgreSQL                     β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Retrieving Memories (Agent Invocation)

User: "What's the weather?"
              β”‚
              β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚    Agent Initialization                       β”‚
        β”‚                                               β”‚
        β”‚    1. Build system prompt                     β”‚
        β”‚    2. Search memories for "weather"           β”‚
        β”‚       β€’ Vector similarity search              β”‚
        β”‚       β€’ Returns: "User prefers Celsius"       β”‚
        β”‚    3. Include memories in prompt context      β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚    System Prompt with Memory                  β”‚
        β”‚                                               β”‚
        β”‚    ## User Memories                           β”‚
        β”‚    <memories>                                 β”‚
        β”‚    - User prefers Celsius for temperature     β”‚
        β”‚    </memories>                                β”‚
        β”‚                                               β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
        Agent responds with temperature in Celsius

Memory Tools (LangMem)

The agent has access to memory tools for explicit memory management:

manage_memory_tool

Created via LangMem:

from langmem import create_manage_memory_tool

manage_memory = create_manage_memory_tool(
    namespace=("memories", "{user_id}")
)

The agent can invoke this to:

  • Save new memories explicitly
  • Update existing memories
  • Delete outdated memories

search_memory_tool

from langmem import create_search_memory_tool

search_memory = create_search_memory_tool(
    namespace=("memories", "{user_id}")
)

The agent can invoke this to:

  • Search for relevant context
  • Find specific memories
  • Retrieve user preferences

Store Factory

The create_memory_store factory in src/memory/store.py handles store creation:

async def create_memory_store(
    database_url: Optional[str] = None,
    embedding_model: str = "openai:text-embedding-3-small",
    embedding_dims: int = 1536,
) -> BaseStore:
    """Factory function to create appropriate memory store.
    
    Uses Strategy pattern to select between PostgreSQL and in-memory storage.
    """
    index_config = {
        "dims": embedding_dims,
        "embed": embedding_model,
        "fields": ["content", "summary", "$"],
    }
    
    if database_url:
        try:
            store = AsyncPostgresStore.from_conn_string(
                database_url,
                index=index_config
            )
            await store.setup()
            return store
        except Exception as e:
            print(f"Warning: PostgreSQL failed, using in-memory: {e}")
    
    # Fallback to in-memory store
    return InMemoryStore(index=index_config)

Embedding Configuration

Embedding Model Options

Model Provider Dimensions Notes
text-embedding-3-small OpenAI 1536 Default, fast
text-embedding-3-large OpenAI 3072 Higher quality
cohere/embed-english-v3.0 Cohere 1024 Alternative
voyage/voyage-large-2 Voyage 1024 Specialized

Configuration

# .env
EMBEDDING_MODEL=openai:text-embedding-3-small
EMBEDDING_DIMS=1536

Memory Retrieval in System Prompt

The agent's system prompt includes relevant memories:

def _build_system_prompt(self, state: AgentState) -> list:
    """Build system prompt with memory context."""
    memories_text = ""
    
    if self.store and state.get("user_id"):
        # Semantic search based on current message
        memories = store.search(
            ("memories", state["user_id"]),
            query=state["messages"][-1].content,
            limit=5
        )
        if memories:
            memories_text = "\n".join([
                f"- {m.value.get('content', m.value)}" 
                for m in memories
            ])
    
    system_content = f"""...
    
## User Memories
<memories>
{memories_text if memories_text else "No memories stored yet."}
</memories>

When users share preferences or important information, 
use the memory tools to save them."""

    return [{"role": "system", "content": system_content}, *state["messages"]]

Background Memory Processing

After each interaction, memories are processed asynchronously:

async def _background_memory_processing(
    user_input: str,
    response: str,
    user_id: str
):
    """Background task to extract and store memories from conversation."""
    try:
        memory_manager = await _get_memory_manager()
        
        # Combine user input and response for analysis
        conversation = f"User: {user_input}\nAssistant: {response}"
        
        # Extract and store semantic memories (preferences, facts)
        await memory_manager.extract_and_store_semantic(
            conversation, 
            user_id
        )
        
        # Extract and store episodic memories (interactions)
        await memory_manager.extract_and_store_episodic(
            conversation,
            user_id
        )
        
    except Exception as e:
        logger.error(f"Background memory processing failed: {e}")

Memory Consolidation

LangMem automatically consolidates similar memories to avoid redundancy:

Before Consolidation:
β”œβ”€β”€ "User likes jazz music"
β”œβ”€β”€ "User prefers jazz over rock"
β”œβ”€β”€ "User mentioned liking jazz"

After Consolidation:
β”œβ”€β”€ "User prefers jazz music over other genres"

This is controlled by the instructions parameter:

create_memory_store_manager(
    model,
    store=store,
    namespace=("memories", "{user_id}", "semantic"),
    instructions="""...
Consolidate related memories to avoid redundancy."""
)

Persistence Fallback

When PostgreSQL is unavailable, the system falls back to in-memory storage:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Persistence Strategy                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                           β”‚
β”‚   DATABASE_URL set?                                       β”‚
β”‚        β”‚                                                  β”‚
β”‚        β”œβ”€β”€ Yes ──▢ Try PostgreSQL                        β”‚
β”‚        β”‚              β”‚                                   β”‚
β”‚        β”‚              β”œβ”€β”€ Success ──▢ AsyncPostgresStore |
β”‚        β”‚              β”‚                                   β”‚
β”‚        β”‚              └── Fail ──▢ InMemoryStore         β”‚
β”‚        β”‚                          (with warning)          β”‚
β”‚        β”‚                                                  β”‚
β”‚        └── No ───▢ InMemoryStore                         β”‚
β”‚                                                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Warning: In-memory storage is lost on restart. Always configure PostgreSQL for production.


Debugging Memories

View Stored Memories

Connect to PostgreSQL:

docker compose exec db psql -U gpt_home -d gpt_home

Query memories:

-- List all memories for a user
SELECT prefix, key, value, created_at 
FROM store 
WHERE prefix LIKE 'memories,default%'
ORDER BY created_at DESC;

-- Search by content
SELECT * FROM store 
WHERE value->>'content' ILIKE '%weather%';

-- Check embedding dimensions
SELECT key, array_length(embedding, 1) as dims 
FROM store LIMIT 5;

Clear Memories

Via Web Interface (Recommended):

Navigate to Settings β†’ Quick Actions β†’ Clear Memories button. This will:

  • Clear all conversation history (checkpoints)
  • Clear all stored memories (semantic & episodic)
  • Require confirmation before proceeding

Via API:

curl -X POST http://gpt-home.local/clearMemory

Response:

{
  "success": true,
  "message": "Memory cleared successfully"
}

Via SQL (Manual):

-- Delete all memories for a user
DELETE FROM store WHERE prefix LIKE 'memories,user_id%';

-- Clear all memories and conversation history
TRUNCATE TABLE checkpoints CASCADE;
TRUNCATE TABLE checkpoint_blobs CASCADE;
TRUNCATE TABLE checkpoint_writes CASCADE;
TRUNCATE TABLE store CASCADE;

⚠️ Warning: Clearing memory is irreversible. The assistant will lose all learned preferences and conversation history.


Configuration Reference

Variable Default Description
DATABASE_URL - PostgreSQL connection string
EMBEDDING_MODEL openai:text-embedding-3-small Model for embeddings (format: provider:model)
EMBEDDING_DIMS 1536 Embedding vector dimensions

Next Steps

Clone this wiki locally