Skip to Content
ConceptsMemory

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:

BackendBest forTradeoff
sqlite (default)Single-agent, single-hostZero-config; not shared
lucidSame as SQLite, but with a human-readable workspace markdown view layered on topSlight write overhead for the markdown sync
markdownMemory you want to grep / version-control / hand-editSlower at scale; lexical search only
postgresMulti-agent fleets, shared memory poolRequires DB; needs --features memory-postgres
noneExplicit 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:

CategoryWhat goes here
CoreDurable facts about the user, the agent’s identity, persistent preferences. Backed up to MEMORY_SNAPSHOT.md for soul-hydration.
DailyDaily summaries; archived after archive_after_days (default 7) and purged after purge_after_days (default 30)
ConversationConversation 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:

WeightDefault
vector_weight0.7
keyword_weight0.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 = 1536

There 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 MiniMax embo-01 (v0.6.86); set embedding_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 = true

When 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 Conversation rows older than conversation_retention_days
  • Archives Daily entries older than archive_after_days to a daily/archive/ folder (markdown backend) or a status flag (sqlite)
  • Purges archived Daily entries older than purge_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>/:

BackendPath
sqlite / lucid<profile>/workspace/memory/brain.db
markdown<profile>/workspace/memory/<category>/...
postgresconfigured 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 = 5000

When 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.rsMemory trait + MemoryCategory + MemoryEntry
  • src/memory/sqlite.rs — SQLite + FTS5 + embedding storage
  • src/memory/lucid.rs — markdown overlay
  • src/memory/markdown.rs — pure markdown backend
  • src/memory/postgres.rs — Postgres backend (feature-gated)
  • src/memory/none.rs — explicit no-op
  • src/memory/hygiene.rs — retention pruner (12h loop)
  • src/memory/snapshot.rs — soul backup / hydration
  • src/memory/response_cache.rs — LRU response cache
  • src/agent/memory_loader.rs — agent-side recall + min_relevance_score filter
Last updated on