Memory
An agent without memory forgets every conversation when it starts a new one. RantAIClaw’s memory system gives agents three things: short-term conversation history, durable facts, and hybrid (lexical + vector) recall.
Memory vs the Knowledge Base. This page is about memory — the agent’s short-term and long-term conversation state. The Knowledge Base is a separate document store (PDFs, markdown, office files, images) with its own schema, lifecycle, and access paths. The two are deliberately not the same store; see the Knowledge Base section for ingestion, hybrid + GraphRAG retrieval, and its HTTP API.
There are five backends, each with a different sweet spot:
| Backend | Best for | Tradeoff |
|---|---|---|
sqlite (default) | Single-agent, single-host | Zero-config; not shared |
lucid | Same as SQLite, but with a human-readable workspace markdown view layered on top | Slight write overhead for the markdown sync |
markdown | Memory you want to grep / version-control / hand-edit | Slower at scale; lexical search only |
postgres | Multi-agent fleets, shared memory pool | Requires DB; needs --features memory-postgres |
none | Explicit no-op (testing, ephemeral runs) | No persistence at all |
Set the backend in [memory] backend = "...". Unknown backend names fall back to markdown with a warning.
What memory stores
Every record carries a MemoryCategory:
| Category | What goes here |
|---|---|
Core | Durable facts about the user, the agent’s identity, persistent preferences. Backed up to MEMORY_SNAPSHOT.md for soul-hydration. |
Daily | Daily summaries; archived after archive_after_days (default 7) and purged after purge_after_days (default 30) |
Conversation | Conversation turns; pruned by conversation_retention_days (default 30) |
Custom("name") | Arbitrary application-defined buckets |
Records have: id, key, content, category, timestamp, session_id, score. Optional scoping is via session_id: Option<&str> — None is global. There is no “sender identity” or “namespace” primitive beyond session IDs and categories.
The Memory trait
// src/memory/traits.rs (simplified)
#[async_trait]
pub trait Memory: Send + Sync {
fn name(&self) -> &str;
async fn store(&self, key: &str, content: &str, category: MemoryCategory, session_id: Option<&str>) -> Result<()>;
async fn recall(&self, query: &str, limit: usize, session_id: Option<&str>) -> Result<Vec<MemoryEntry>>;
async fn get(&self, key: &str) -> Result<Option<MemoryEntry>>;
async fn list(&self, category: Option<&MemoryCategory>, session_id: Option<&str>) -> Result<Vec<MemoryEntry>>;
async fn forget(&self, key: &str) -> Result<bool>;
async fn count(&self) -> Result<usize>;
async fn health_check(&self) -> bool;
}Hybrid recall — BM25 + cosine
Recall is not pure cosine similarity. The SQLite and Lucid backends combine:
- FTS5 BM25 — full-text lexical match
- Cosine similarity over stored embeddings
Then merges with weights:
| Weight | Default |
|---|---|
vector_weight | 0.7 |
keyword_weight | 0.3 |
The merged top-K is what gets fed to the agent. The threshold filter (min_relevance_score, default 0.4) is applied at the agent loop layer (src/agent/memory_loader.rs), not inside the backend.
For Markdown and Postgres backends, the recall path differs (no FTS5 — vector-only or keyword-only depending on configuration), but the same min_relevance_score filter applies.
Embedding provider
Defaults that may surprise you:
[memory]
backend = "sqlite"
conversation_retention_days = 30
min_relevance_score = 0.4
vector_weight = 0.7
keyword_weight = 0.3
embedding_provider = "none" # ← NoopEmbedding by default!
embedding_model = "text-embedding-3-small"
embedding_dimensions = 1536There is no [memory.embedding] nested table — these are flat keys under [memory] (MemoryConfig in src/config/schema.rs: embedding_provider, embedding_model, embedding_dimensions).
The default embedding provider is none (NoopEmbedding) — until you configure a real one, recall is effectively lexical-only. create_embedding_provider (src/memory/embeddings.rs) recognizes exactly:
none— NoopEmbedding (default; lexical-only recall)openai— OpenAI embeddings (e.g.text-embedding-3-small)minimax— native MiniMaxembo-01(v0.6.86); setembedding_model = "embo-01",embedding_dimensions = 1536. The embedding key reuses the main MiniMax provider key — no second credential to enter.custom:<url>— any OpenAI-compatible embeddings endpoint at the given base URL
Anything else falls back to none. Supply the matching provider credentials to enable real semantic recall.
You can also declare alternative routes per memory model name:
[[embedding_routes]]
hint = "core-"
provider = "openai"
model = "text-embedding-3-large"Snapshot and hydration (“soul” backup)
Core memories — durable user-identity facts — can be exported to a single human-readable markdown file:
[memory]
snapshot_enabled = true
auto_hydrate = trueWhen enabled, the agent periodically writes Core entries to <workspace>/memory/MEMORY_SNAPSHOT.md. On cold boot, if brain.db is missing but the snapshot exists, the agent re-hydrates Core memories from the markdown.
This is RantAIClaw-specific — it’s how an agent’s “soul” survives a cold boot, OS reinstall, or container redeployment that wipes the SQLite file.
Defaults: snapshot_enabled = false, auto_hydrate = true.
Hygiene job
A background hygiene loop runs every 12 hours (src/memory/hygiene.rs):
- Prunes
Conversationrows older thanconversation_retention_days - Archives
Dailyentries older thanarchive_after_daysto adaily/archive/folder (markdown backend) or a status flag (sqlite) - Purges archived
Dailyentries older thanpurge_after_days
Pruning is automatic. There’s no rantaiclaw memory prune command.
CLI
rantaiclaw memory list [--category core|daily|conversation|<custom>] [--session <id>] [--limit N] [--offset N]
rantaiclaw memory get <key>
rantaiclaw memory stats
rantaiclaw memory clear [--category <c>] [--session <id>]That’s the full surface. There is no prune, show, or forget CLI subcommand — forget is a trait method but not exposed via CLI.
Lucid backend
Unique to RantaiClaw. Layers a workspace markdown view on top of SQLite — every memory write produces both a brain.db row and a markdown file under <workspace>/memory/<category>/<date>/<key>.md.
The markdown view is for humans (grep, edit, version control). The SQLite view is for the runtime (fast queries, embeddings). They stay in sync automatically.
Postgres backend
Requires building with --features memory-postgres.
[memory]
backend = "postgres"
[storage.provider]
config = { db_url = "${DATABASE_URL}", schema = "rantaiclaw" }Multi-agent shared state, real concurrency, native pgvector index. Standard Postgres backup tooling applies.
Default paths
Per-profile under ~/.rantaiclaw/profiles/<active>/:
| Backend | Path |
|---|---|
sqlite / lucid | <profile>/workspace/memory/brain.db |
markdown | <profile>/workspace/memory/<category>/... |
postgres | configured via db_url |
Channel history persistence
Per-sender conversation history for chat channels (Telegram, Discord, …) is persisted so threads survive a daemon restart (v0.6.88). It’s held write-through in a channel_history table in the same brain.db, keyed by channel_sender, and reloaded at startup — so a restarted bot resumes each thread exactly where it left off instead of “forgetting” the conversation.
This is sqlite-only: enabled automatically when the memory backend is sqlite; markdown / none backends keep the prior in-memory behavior. Persistence failures degrade gracefully (log + in-memory only) and never block message handling (src/channels/history_store.rs).
Response cache (separate)
Adjacent to memory but distinct: an LRU cache of LLM responses keyed by prompt hash. Its keys are flat under [memory] (there is no [memory.response_cache] nested table), and the TTL is in minutes:
[memory]
response_cache_enabled = false # default
response_cache_ttl_minutes = 60 # minutes, not seconds
response_cache_max_entries = 5000When enabled, repeated identical prompts skip the provider and return cached answers within the TTL window. Useful for deterministic workloads; a footgun for anything personalized.
Reading the code
src/memory/traits.rs—Memorytrait +MemoryCategory+MemoryEntrysrc/memory/sqlite.rs— SQLite + FTS5 + embedding storagesrc/memory/lucid.rs— markdown overlaysrc/memory/markdown.rs— pure markdown backendsrc/memory/postgres.rs— Postgres backend (feature-gated)src/memory/none.rs— explicit no-opsrc/memory/hygiene.rs— retention pruner (12h loop)src/memory/snapshot.rs— soul backup / hydrationsrc/memory/response_cache.rs— LRU response cachesrc/agent/memory_loader.rs— agent-side recall +min_relevance_scorefilter