Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dallay/corvus/llms.txt

Use this file to discover all available pages before exploring further.

Memory System Overview

Corvus features a full-stack search engine for long-term memory, built entirely from scratch with zero external dependencies.

Architecture

All memory backends implement the Memory trait from src/memory/traits.rs:58-111:
#[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>,
    ) -> anyhow::Result<()>;
    
    async fn recall(
        &self,
        query: &str,
        limit: usize,
        session_id: Option<&str>,
    ) -> anyhow::Result<Vec<MemoryEntry>>;
    
    async fn get(&self, key: &str) -> anyhow::Result<Option<MemoryEntry>>;
    async fn list(&self, category: Option<&MemoryCategory>, session_id: Option<&str>) -> anyhow::Result<Vec<MemoryEntry>>;
    async fn forget(&self, key: &str) -> anyhow::Result<bool>;
    async fn count(&self) -> anyhow::Result<usize>;
    async fn health_check(&self) -> bool;
}

Hybrid Search Stack

From README.md:
LayerImplementation
Vector DBEmbeddings stored as BLOB in SQLite, cosine similarity search
Keyword SearchFTS5 virtual tables with BM25 scoring
Hybrid MergeCustom weighted merge function (vector.rs)
EmbeddingsEmbeddingProvider trait — OpenAI, custom URL, or noop
ChunkingLine-based markdown chunker with heading preservation
CachingSQLite embedding_cache table with LRU eviction
Safe ReindexRebuild FTS5 + re-embed missing vectors atomically

Configuration

[memory]
backend = "sqlite"              # "sqlite", "lucid", "surreal", "markdown", "none"
auto_save = true
embedding_provider = "openai"
vector_weight = 0.7
keyword_weight = 0.3

Backend Comparison

BackendVector SearchKeyword SearchGraph QueriesBest For
SQLite✅ (FTS5)Local, fast, battle-tested
SurrealDBGraphs, relationships, multi-node
Markdown✅ (grep)Human-readable, git-friendly
NoneStateless agents

Memory Entry Structure

From src/memory/traits.rs:5-14:
pub struct MemoryEntry {
    pub id: String,
    pub key: String,
    pub content: String,
    pub category: MemoryCategory,
    pub timestamp: String,
    pub session_id: Option<String>,
    pub score: Option<f64>,  // relevance score from search
}

Memory Categories

pub enum MemoryCategory {
    Core,          // Long-term facts, preferences, decisions
    Daily,         // Daily session logs
    Conversation,  // Conversation context
    Custom(String),// User-defined category
}

Usage Guidelines

Core

User preferences, long-term facts, architecture decisions. Never auto-deleted.

Daily

Session logs, daily notes. Auto-pruned after 30 days.

Conversation

Current conversation context. Cleared per session.

Custom

Project-specific: project:webapp, bugs:critical, etc.

Hybrid Search Algorithm

From src/memory/vector.rs:
pub fn merge_search_results(
    vector_results: Vec<(String, f32)>,
    keyword_results: Vec<(String, f32)>,
    vector_weight: f32,
    keyword_weight: f32,
    limit: usize,
) -> Vec<(String, f64)> {
    // Normalize scores to [0, 1]
    // Weighted sum: score = (vector * w_v) + (keyword * w_k)
    // Sort by final score descending
    // Return top N
}
Default weights: 70% vector, 30% keyword (configurable)

Embedding Cache

To avoid redundant API calls, embeddings are cached:
CREATE TABLE embedding_cache (
    content_hash TEXT PRIMARY KEY,
    embedding    BLOB NOT NULL,
    accessed_at  TEXT NOT NULL
);
LRU eviction: Oldest accessed entries are pruned when cache exceeds cache_max (default: 10,000).

Performance Characteristics

SQLite Backend

  • Store: <1ms (indexed write)
  • Recall (vector): 10-50ms for 1000 entries
  • Recall (keyword): <5ms (FTS5 index)
  • Hybrid merge: 15-60ms total

SurrealDB Backend

  • Store: 1-5ms (local), 10-50ms (remote)
  • Recall: 5-20ms (native vector search)
  • Graph queries: 10-100ms depending on depth

SurrealDB Configuration

[memory.surreal]
url = "http://127.0.0.1:8000"
namespace = "corvus"
database = "memory"
allow_http_loopback = true
Environment overrides:
export CORVUS_MEMORY_BACKEND=surreal
export CORVUS_SURREALDB_URL=http://127.0.0.1:8000
export CORVUS_SURREALDB_NAMESPACE=corvus
export CORVUS_SURREALDB_DATABASE=memory

Migration from OpenClaw

Corvus can import memory from OpenClaw:
corvus migrate openclaw --dry-run
corvus migrate openclaw
See Migration Guide.

Best Practices

Use descriptive keys with prefixes: user_pref_theme, project_goal_2026_Q1
Enable auto_save for production agents that need to learn over time
Never store secrets (API keys, passwords) in memory. Use config or secure storage.

API Usage

Storing a Memory

memory.store(
    "user_prefers_rust",
    "User strongly prefers Rust for system programming",
    MemoryCategory::Core,
    None,  // no session scope
).await?;

Recalling Memories

let results = memory.recall(
    "programming language preferences",
    5,  // limit
    None,  // all sessions
).await?;

for entry in results {
    println!("{}: {} (score: {})", 
        entry.key, 
        entry.content, 
        entry.score.unwrap_or(0.0)
    );
}

Session-Scoped Recall

let results = memory.recall(
    "current task status",
    10,
    Some("session-abc-123"),  // filter by session
).await?;

Next Steps

SQLite Backend

Battle-tested local storage

SurrealDB Backend

Graph queries and multi-node

Embeddings

Vector search configuration

Memory Tools

Agent memory operations

Build docs developers (and LLMs) love