OpenClaw Memory Layers: Complete Setup Guide
Master OpenClaw's three memory layers: ephemeral, semantic, and persistent. Step-by-step guide to prevent context loss and build AI systems that truly lear
Originally published:
What You'll Learn
This tutorial explains OpenClaw's three-layer memory architecture and how to configure each layer to prevent information loss in your AI systems. You'll learn when to use each memory type, implement them in practice, and troubleshoot common retention issues that degrade performance over time.
Introduction: Why Memory Matters in AI Systems
AI systems without persistent memory degrade rapidly. Each conversation resets to zero context, forcing the model to re-learn patterns, waste tokens on redundant information, and fail at nuanced tasks requiring historical awareness. OpenClaw solves this with a three-layer memory stack: ephemeral (session), semantic (vector), and persistent (database). Each layer serves a distinct purpose, and configuring all three correctly determines whether your system learns or forgets.
Prerequisites
- OpenClaw installed (v0.8+) and running locally or in cloud environment
- Basic Python knowledge (no advanced expertise required)
- Access to a vector database (Pinecone, Weaviate, or local Qdrant instance recommended)
- Understanding of embeddings and semantic search concepts
- PostgreSQL or SQLite for persistent storage layer
- ~30 minutes for full setup; 10 minutes for verification
Learning Objectives
- Understand the purpose and constraints of each memory layer
- Configure ephemeral memory for single-session coherence
- Set up semantic memory with vector embeddings for cross-session relevance
- Implement persistent memory for long-term knowledge retention
- Debug memory leaks and validate retention across layer boundaries
- Optimize memory costs without sacrificing context quality
Part 1: Understanding the Three Memory Layers
Layer 1: Ephemeral Memory (Session Context)
Ephemeral memory holds the current conversation in a rolling buffer, typically the last 10–50 messages. It's the fastest layer—zero latency—but disappears when the session ends. This layer answers "What did the user say 3 turns ago?" without querying external systems.
Key characteristics: Lives in application RAM, token-limited (usually 2,000–8,000 tokens), cleared on session timeout. Use this for immediate context and coherence within a single conversation thread.
OpenClaw stores ephemeral memory in a deque (double-ended queue), automatically evicting oldest messages when capacity is reached. This prevents unbounded context growth while maintaining recent interaction history.
Layer 2: Semantic Memory (Vector Embeddings)
Semantic memory transforms past interactions into embeddings and stores them in a vector database. Instead of exact text matching, semantic memory retrieves conceptually similar information across all historical sessions. When a user asks about "payment issues," semantic search finds previous discussions about billing, invoices, and transaction problems—even if exact keywords don't match.
Key characteristics: Persistent across sessions, searchable by meaning (not keywords), ~10–100ms retrieval latency, enables cross-conversation learning. This layer answers "Have we discussed similar topics before?"
OpenClaw generates embeddings using OpenAI's `text-embedding-3-small` (or your configured provider) and queries the vector store with cosine similarity. Typical setup retrieves 5–10 most relevant past interactions per query.
Layer 3: Persistent Memory (Structured Knowledge)
Persistent memory stores facts, user profiles, system rules, and structured data in a traditional database. Unlike semantic memory's vector search, this layer is ideal for exact lookups: user IDs, account status, configuration settings, and categorical metadata. This layer answers "What is the user's account tier?" or "What rules apply here?"
Key characteristics: Queryable with SQL, supports ACID transactions, no semantic understanding required, millisecond retrieval. Use for structured facts that must remain consistent and auditable.
Part 2: Setting Up Ephemeral Memory
How does ephemeral memory initialization work?
OpenClaw initializes ephemeral memory when a session starts. You configure buffer size, message format, and eviction policy in the config file.
Step 1: Define session configuration
Create or edit your OpenClaw config file (typically `config.yaml` or environment variables):
ephemeral_memory: max_messages: 20 max_tokens: 4096 eviction_policy: "fifo" # First-in-first-out include_metadata: true
max_messages limits conversation turns; max_tokens caps context size. Set max_messages: 20 for balanced performance (older systems may need 10). The FIFO eviction policy removes oldest messages first when capacity is reached.
Step 2: Initialize the session manager
In your application startup code, instantiate OpenClaw's session handler:
from openclaw.memory import SessionManager
session_mgr = SessionManager(
max_messages=20,
max_tokens=4096,
enable_metadata=True
)
# Start a new session
session_id = session_mgr.create_session(user_id="user_123")
The `create_session()` call generates a unique session ID and initializes an empty ephemeral buffer for that user. Store this ID to maintain conversation continuity.
Step 3: Add messages to ephemeral memory
After each user input and model response, append to the ephemeral buffer:
session_mgr.add_message(
session_id=session_id,
role="user",
content="What's my account balance?",
metadata={"timestamp": 1704067200, "source": "web_ui"}
)
session_mgr.add_message(
session_id=session_id,
role="assistant",
content="Your balance is $1,250.",
metadata={"tokens_used": 15, "latency_ms": 340}
)
Metadata (timestamps, source, token counts) helps debug and monitor memory behavior. It's optional but recommended for production systems.
Step 4: Retrieve ephemeral context for the model
Before sending a prompt to the model, fetch the current ephemeral buffer:
context = session_mgr.get_context(session_id=session_id)
print(f"Current context: {len(context['messages'])} messages, {context['token_count']} tokens")
# Use in prompt
system_prompt = "You are a helpful assistant." + context['formatted_messages']
get_context() returns a structured object with messages (list of dicts), token_count (int), and formatted_messages (concatenated string ready for the model).
Troubleshooting ephemeral memory
Issue: Context growing too large, token budget exceeded
Solution: Lower max_messages to 10–15, or reduce max_tokens to 2048. Monitor token usage per session with session_mgr.get_stats(session_id).
Issue: Session data lost after application restart
Solution: This is expected—ephemeral memory is in-RAM only. To persist across restarts, use Layer 3 (persistent memory) or export session history before shutdown.
Part 3: Configuring Semantic Memory with Vector Embeddings
What does semantic memory setup involve?
You'll connect OpenClaw to a vector database, configure embedding generation, and define retrieval strategies. This enables the system to find relevant past interactions by meaning, not keywords.
Step 1: Choose and set up a vector database
OpenClaw supports Pinecone (hosted), Weaviate, Qdrant (local), and Milvus. For this tutorial, we'll use Qdrant (local, no external dependencies):
pip install qdrant-client openclaw[semantic]
If using Pinecone, install: pip install pinecone-client
Step 2: Configure semantic memory in OpenClaw
Update your config file:
semantic_memory:
enabled: true
vector_db:
provider: "qdrant"
host: "localhost"
port: 6333
collection_name: "conversations"
embeddings:
model: "text-embedding-3-small"
provider: "openai"
dimension: 1536
retrieval:
top_k: 5
min_similarity: 0.72
query_expansion: true
top_k: 5 retrieves the 5 most similar past interactions. min_similarity: 0.72 filters out weak matches (cosine similarity scale is 0–1). Set higher thresholds (0.80+) for stricter matching; lower (0.65) for broader retrieval.
Step 3: Initialize the semantic memory manager
In your application code:
from openclaw.memory import SemanticMemory
import os
semantic_mem = SemanticMemory(
vector_db_provider="qdrant",
vector_db_url="http://localhost:6333",
embedding_model="text-embedding-3-small",
embedding_api_key=os.getenv("OPENAI_API_KEY"),
collection_name="conversations",
top_k=5
)
Step 4: Index past interactions
Each user message and assistant response should be embedded and stored in the vector database:
semantic_mem.index_interaction(
interaction_id="msg_12345",
text="How do I reset my password?",
user_id="user_123",
timestamp=1704067200,
metadata={"session_id": session_id, "category": "account"}
)
semantic_mem.index_interaction(
interaction_id="msg_12346",
text="You can reset your password in account settings, or email support@example.com",
user_id="user_123",
timestamp=1704067215,
metadata={"session_id": session_id, "category": "account", "is_response": true}
)
Index both user queries and assistant responses. This allows retrieval of relevant responses to similar past queries.
Step 5: Retrieve semantic context for a new query
When a user asks a question, search for semantically similar interactions:
query = "I forgot my login password, what should I do?"
results = semantic_mem.retrieve(
query=query,
user_id="user_123",
top_k=5,
min_similarity=0.72
)
for result in results:
print(f"Similarity: {result['score']:.2f}")
print(f"Past interaction: {result['text']}")
print(f"Date: {result['timestamp']}\n")
Results are ranked by similarity score. Use the top 1–3 results as additional context in your prompt to the model.
Best practice: Query expansion
Single queries sometimes miss relevant results. Enable query expansion to generate semantically similar queries automatically:
results = semantic_mem.retrieve(
query="reset password",
user_id="user_123",
expand_query=True, # Generates: "forgot password", "login issues", etc.
top_k=10
)
OpenClaw uses a small language model to generate 2–3 query variants, retrieves from each, and deduplicates results. This improves recall (finding more relevant interactions) at a small latency cost (~50–100ms extra).
Troubleshooting semantic memory
Issue: Vector database connection fails
Solution: Verify Qdrant is running (docker ps | grep qdrant), or check Pinecone API credentials. Test connection with: semantic_mem.health_check()
Issue: Retrieving irrelevant results (low precision)
Solution: Raise min_similarity threshold from 0.72 to 0.78–0.80. Add metadata filters (e.g., retrieve only results from the last 90 days) to narrow scope.
Issue: No results retrieved (low recall)
Solution: Lower min_similarity to 0.65. Enable query_expansion: true. Check that interactions are being indexed correctly: semantic_mem.count_documents(user_id="user_123")
Part 4: Implementing Persistent Memory with Databases
How do you structure persistent memory?
Persistent memory stores user profiles, account data, system rules, and facts that must be consistent and queryable via SQL. Set up a relational database and OpenClaw's persistence layer.
Step 1: Choose a database and initialize schema
For this tutorial, we'll use PostgreSQL (production-grade) or SQLite (local development). Install the adapter:
pip install openclaw[postgres] # For PostgreSQL # or pip install openclaw[sqlite] # For SQLite
Step 2: Define your schema
Create tables for user profiles, rules, and facts:
CREATE TABLE users (
user_id VARCHAR(255) PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
account_tier VARCHAR(50) DEFAULT 'free',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata JSONB
);
CREATE TABLE rules (
rule_id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
condition VARCHAR(500),
action VARCHAR(500),
priority INT DEFAULT 100,
enabled BOOLEAN DEFAULT true
);
CREATE TABLE facts (
fact_id SERIAL PRIMARY KEY,
user_id VARCHAR(255) REFERENCES users(user_id),
key VARCHAR(255) NOT NULL,
value TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP
);
Step 3: Configure OpenClaw's persistent layer
Update config file:
persistent_memory:
enabled: true
database:
provider: "postgresql"
host: "localhost"
port: 5432
database: "openclaw_prod"
user: "openclaw_user"
password: "${DB_PASSWORD}" # Use environment variable
connection_pool:
min_size: 5
max_size: 20
cache:
enable: true
ttl_seconds: 300
Step 4: Initialize the persistent memory manager
from openclaw.memory import PersistentMemory import ospersistent_mem = PersistentMemory(
provider="postgresql",
connection_string=os.getenv("DATABASE_URL"),
enable_cache=True,
cache_ttl=300
)
Step 5: Store and retrieve user data
Create or update a user profile:
persistent_mem.upsert_user(
user_id="user_123",
email="alice@example.com",
account_tier="premium",
metadata={"company": "Acme Corp", "role": "admin"}
)
# Retrieve user data
user = persistent_mem.get_user(user_id="user_123")
print(f"Account tier: {user['account_tier']}")
Step 6: Store facts and rules
Add facts (time-limited or permanent):
persistent_mem.set_fact(
user_id="user_123",
key="preferred_language",
value="Spanish",
ttl_seconds=None # Permanent
)
persistent_mem.set_fact(
user_id="user_123",
key="api_quota_remaining",
value="9500",
ttl_seconds=86400 # Expires in 24 hours
)
# Retrieve
preferred_lang = persistent_mem.get_fact(user_id="user_123", key="preferred_language")
quota = persistent_mem.get_fact(user_id="user_123", key="api_quota_remaining")
Facts with ttl_seconds auto-expire and are cleaned up by background jobs.
Step 7: Query rules for conditional logic
Retrieve active rules to apply business logic:
active_rules = persistent_mem.get_rules(
enabled=True,
min_priority=50 # Only high-priority rules
)
for rule in active_rules:
if evaluate_condition(rule['condition'], context):
apply_action(rule['action'])
Troubleshooting persistent memory
Issue: Database connection timeout
Solution: Increase max_size in connection pool. Check database server is running and accessible. Test with: persistent_mem.health_check()
Issue: Cached data is stale
Solution: Reduce cache_ttl from 300 to 60 seconds, or disable cache for critical data: persistent_mem.get_user(user_id, use_cache=False)
Issue: Slow queries for large fact/rule tables
Solution: Add database indexes on frequently queried columns: CREATE INDEX idx_facts_user_key ON facts(user_id, key);
Part 5: Integrating All Three Layers
How do you coordinate ephemeral, semantic, and persistent memory?
Build a unified memory context function that pulls from all three layers and merges them into a coherent prompt:
def build_memory_context(user_id, session_id, query):
# Layer 1: Ephemeral
ephemeral_ctx = session_mgr.get_context(session_id)
# Layer 2: Semantic
semantic_results = semantic_mem.retrieve(query, user_id=user_id, top_k=3)
semantic_ctx = "\n".join([f"Past: {r['text']} (similarity: {r['score']:.2f})" for r in semantic_results])
# Layer 3: Persistent
user = persistent_mem.get_user(user_id)
facts = persistent_mem.get_facts(user_id, limit=10)
persistent_ctx = f"Account: {user['account_tier']}\nFacts: {facts}"
# Combine into memory prompt
memory_prompt = f"""
## Recent Conversation (Ephemeral):
{ephemeral_ctx['formatted_messages']}
## Relevant Past Interactions (Semantic):
{semantic_ctx}
## User Profile (Persistent):
{persistent_ctx}
"""
return memory_prompt
Use this function before every model call to maintain complete memory context across all layers.
Part 6: Best Practices
1. Monitor memory costs
Track token usage, vector DB queries, and database calls. Set budget alerts. For typical conversational AI, expect 500–2,000 extra tokens per query due to memory retrieval. Use token counting to validate:
from openclaw.utils import count_tokens
memory_tokens = count_tokens(memory_prompt)
query_tokens = count_tokens(user_query)
print(f"Memory overhead: {memory_tokens / (memory_tokens + query_tokens) * 100:.1f}%")
2. Implement memory expiration
Old interactions become noise. Archive or delete interactions older than 90–180 days:
from datetime import datetime, timedelta age_threshold = datetime.now() - timedelta(days=90) semantic_mem.delete_interactions_before(age_threshold) persistent_mem.delete_facts_older_than(age_threshold)
3. Validate memory integrity
Run periodic checks to ensure data consistency across layers:
def validate_memory(user_id):
# Check: Persistent user exists
user = persistent_mem.get_user(user_id)
assert user, f"User {user_id} not in persistent memory"
# Check: Semantic interactions reference valid user
semantic_count = semantic_mem.count_documents(user_id)
assert semantic_count > 0, f"No semantic data for {user_id}"
print(f"Memory integrity OK for {user_id}")
validate_memory("user_123")
4. Optimize similarity thresholds per use case
Customer support chatbots need broad retrieval (min_similarity: 0.65) to find loosely related solutions. Data lookup systems need strict matching (min_similarity: 0.85) to avoid false positives. Test and adjust based on your precision/recall tradeoff:
for threshold in [0.60, 0.70, 0.80, 0.90]:
results = semantic_mem.retrieve(query, min_similarity=threshold)
evaluate_quality(results) # Manual assessment
# Choose threshold that maximizes usefulness for your domain
5. Use metadata filters to reduce noise
Filter semantic retrieval by date range, category, or user segment:
recent_results = semantic_mem.retrieve(
query="billing issue",
user_id="user_123",
metadata_filters={
"category": "billing",
"timestamp_after": time.time() - 30*86400 # Last 30 days
}
)
Part 7: Troubleshooting & Debugging
Memory is missing across restarts
Ephemeral memory is designed to be lost. If you need persistence, store it in semantic or persistent layers before shutdown. Add a handler to save session summary on exit.
Model responses contradict previous statements
Cause: Low min_similarity retrieving irrelevant interactions, or ephemeral buffer too small to maintain recent context.
Solution: Increase ephemeral buffer (max_messages: 30), raise similarity threshold to 0.75+, manually inspect retrieval results with: semantic_mem.debug_retrieve(query, user_id)
Embedding/vector database is slow
Cause: Vector DB not indexed, or query expansion adding latency.
Solution: Check database performance with semantic_mem.benchmark_retrieval(iterations=100). Disable query expansion if latency > 500ms. Index frequently searched fields.
Database grows too large
Cause: Storing redundant facts, or not cleaning up expired records.
Solution: Enable automatic cleanup: persistent_mem.enable_auto_cleanup(interval_days=7). Archive old semantic interactions quarterly. Monitor database size: persistent_mem.get_stats()['storage_bytes']
Conclusion: Putting It All Together
OpenClaw's three-layer memory architecture prevents information loss at every scale. Ephemeral memory keeps conversations coherent within sessions. Semantic memory connects you to relevant patterns across your entire interaction history. Persistent memory stores the facts and rules that define your system's behavior.
Start by implementing ephemeral memory (10 minutes), then add semantic retrieval (30 minutes for vector DB setup), then persistent storage (20 minutes for database schema). Each layer multiplies the intelligence of your system, but also adds complexity—monitor costs and performance as you grow.
Summary
- Ephemeral memory maintains conversation coherence within a session via a fixed-size buffer (10–50 messages). Initialize with
SessionManager, add messages after each turn, retrieve context before model calls. - Semantic memory enables cross-session learning by embedding interactions and retrieving by meaning. Use a vector database (Qdrant, Pinecone), configure similarity thresholds (0.65–0.85), and enable query expansion for better recall.
- Persistent memory stores user profiles, facts, and rules in a traditional database. Query with SQL, implement caching to avoid bottlenecks, and set fact TTLs for automatic expiration.
- Integrate all three into a unified memory context function that combines ephemeral (recent), semantic (relevant), and persistent (factual) information into a single prompt.
- Monitor and optimize: Track memory costs, validate integrity, adjust similarity thresholds per use case, and clean up old data quarterly. Test in staging before production rollout.
Original Source
https://www.youtube.com/watch?v=f8LJBh1AtKg
Last updated: