Aleph
Architecture

Memory Architecture

Deep dive into Aleph's memory storage, retrieval, and background processing implementation.

This document provides a detailed look at the implementation of Aleph's memory system. For a high-level overview, see Memory System Concepts.


Storage Architecture

Four-Trait Backend Design

The storage layer uses four separate traits rather than a monolithic store. Each caller depends only on the capabilities it needs:

// src/memory/store/mod.rs
pub trait NoteStore: Send + Sync { /* ... */ }
pub trait RawMemoryStore: Send + Sync { /* ... */ }
pub trait DreamStore: Send + Sync { /* ... */ }
pub trait CompressionStore: Send + Sync { /* ... */ }

All four are implemented by SqliteMemoryBackend:

pub struct SqliteMemoryBackend {
    db: Arc<Mutex<Connection>>,
}

pub type MemoryBackend = Arc<SqliteMemoryBackend>;

Schema (SQLite + sqlite-vec)

Core Tables

TablePurpose
raw_memoriesSession transcripts, attachment text, is_processed flag
notes_indexKnowledge note metadata (title, category, tags, hash)
notes_linksBidirectional wikilink graph
notes_ftsFTS5 full-text index over note content
notes_vec_mapLinks (path, agent_id) to numeric rowids
notes_vec_{dim}sqlite-vec virtual tables (768/1024/1536 dims)
memory_eventsImmutable event log (event-sourced mutations)
context_anchorsFact-to-session linkage for provenance
dream_statusDaily/weekly dream run tracking
daily_insightsDream daemon output cache

sqlite-vec Virtual Tables

-- Created per embedding dimension
CREATE VIRTUAL TABLE notes_vec_768 USING vec0(
    embedding float[768]
);
CREATE VIRTUAL TABLE notes_vec_1024 USING vec0(
    embedding float[1024]
);
CREATE VIRTUAL TABLE notes_vec_1536 USING vec0(
    embedding float[1536]
);

The notes_vec_map table bridges human-readable (path, agent_id) pairs to numeric rowids required by vec0 tables.


Retrieval Pipeline

NoteFactRetrieval

The primary retrieval interface for knowledge notes:

// src/memory/note_retrieval/mod.rs
#[async_trait]
pub trait NoteFactRetrieval: Send + Sync {
    async fn retrieve(
        &self,
        query: &str,
        agent_id: &str,
        limit: usize,
    ) -> Result<Vec<ScoredFact<MemoryFact>>, AlephError>;
}

Hybrid Search (Vector + FTS)

Query
  ├── Vector Search (sqlite-vec) → candidates with distance scores
  ├── FTS Search (FTS5) → candidates with match scores
  └── RRF Fusion → unified ranked list

Reciprocal Rank Fusion (RRF) formula:

RRF_score = Σ 1 / (k + rank_i)

where k = 60 (constant), rank_i = rank from source i.

Scoring Pipeline (6 Stages)

StageDescriptionDefault
cosine_rerankBlend vector-search score with fresh cosine similarityenabled
recency_boostAdditive boost for recently created facts+0.1
length_normalizationPenalize very long contentenabled
time_decayExponential decay by age, floor 0.5half-life 30 days
hard_min_scoreDrop candidates below threshold0.35
mmr_diversityMaximal Marginal Relevance — defer near-duplicates to tailλ = 0.5

Deleted stages: importance_weight (removed in Sovereignty Cleanup).


Background Processing

Compression Service

Runs in real-time during idle moments:

Raw Memories (is_processed = false)
  → SessionCompactor: per-session compaction
  → NoteIndexer: create/update knowledge notes
  → mark is_processed = true

Dream Daemon

Runs during system idle, with two cadences:

Daily Pipeline (5 stages):

[Consolidate] → [Drift] → [Lint] → [Decay] → [DailyDigest]

Weekly Pipeline (6 stages):

[Consolidate] → [Drift] → [Synthesis] → [Lint] → [Decay] → [DailyDigest]
// src/memory/dreaming/mod.rs
pub fn ensure_dream_daemon();
pub fn record_activity();  // Call on user activity to reset idle timer

Working Memory Assembly

HybridAssembler (Spec 2)

Replaces the legacy ContextComptroller::arbitrate path. Produces a MemoryEnvelope with explicit slots:

// src/memory/assembler/mod.rs
pub struct MemoryEnvelope {
    pub system_slots: Vec<MemorySlot>,      // Always-injected facts
    pub user_slots: Vec<MemorySlot>,        // User-query relevant facts
    pub scratchpad: Option<String>,         // Working notes
}

pub struct MemorySlot {
    pub content: String,
    pub source: String,
    pub score: f32,
}
#[async_trait]
pub trait WorkingMemoryAssembler: Send + Sync {
    async fn assemble(
        &self,
        query: &str,
        agent_id: &str,
        session_id: Option<&str>,
        budget: AssemblyBudget,
    ) -> Result<MemoryEnvelope, AlephError>;
}

Assembly Budget

pub struct AssemblyBudget {
    pub max_tokens: usize,      // Target token budget for assembled memory
    pub min_score: f32,         // Minimum fact score threshold
    pub max_facts: usize,       // Maximum number of facts to include
}

Curated Hot Memory (Spec A)

A manually-curated, frozen snapshot of critical facts that bypasses normal retrieval:

// src/memory/curated/mod.rs
pub struct CuratedHotMemory {
    pub facts: Vec<CuratedFact>,
    pub frozen_at: i64,
}

pub struct CuratedFact {
    pub content: String,
    pub priority: u8,        // 1-10, higher = more important
    pub source: String,
}
  • Created via the remember builtin tool
  • Stored in ~/.aleph/memory/curated/{agent_id}.json
  • Injected into every prompt unconditionally (up to token budget)
  • Frozen snapshot — changes require explicit re-curation

Memory Extensions (Spec 4)

Pluggable extensions for custom memory behaviors:

// src/memory/extensions/mod.rs
#[async_trait]
pub trait MemoryExtension: Send + Sync {
    fn name(&self) -> &str;
    
    async fn on_memory_created(
        &self,
        event: &MemoryEvent,
        backend: &MemoryBackend,
    ) -> Result<(), AlephError>;
    
    async fn on_memory_retrieved(
        &self,
        query: &str,
        facts: &[MemoryFact],
        backend: &MemoryBackend,
    ) -> Result<Vec<MemoryFact>, AlephError>;
    
    async fn on_session_end(
        &self,
        session_id: &str,
        backend: &MemoryBackend,
    ) -> Result<(), AlephError>;
}

pub struct ExtensionRegistry {
    extensions: Vec<Box<dyn MemoryExtension>>,
}

Lifecycle hooks:

  • on_memory_created — Triggered after note creation/update
  • on_memory_retrieved — Can modify/filter retrieved facts
  • on_session_end — Cleanup or summarization

Memory Reflector (Spec 2)

A synthesis layer on top of HybridAssembler:

// src/memory/reflector/mod.rs
pub struct MemoryReflector {
    assembler: Arc<dyn WorkingMemoryAssembler>,
    llm: Arc<dyn LlmBackend>,
}

impl MemoryReflector {
    pub async fn reflect(
        &self,
        query: &str,
        agent_id: &str,
    ) -> Result<ReflectionResult, AlephError>;
}

pub struct ReflectionResult {
    pub answer: String,
    pub sources: Vec<MemorySource>,
    pub confidence: f32,
}

Exposed via the memory_reflect builtin tool. Returns a coherent LLM-synthesised answer with cited sources.


Event Sourcing

Every note mutation is captured as an immutable MemoryEvent:

// src/memory/events/mod.rs
pub struct MemoryEvent {
    pub id: i64,
    pub event_type: MemoryEventType,
    pub note_path: String,
    pub timestamp: i64,
    pub payload: String,  // JSON
}

pub enum MemoryEventType {
    NoteCreated,
    NoteUpdated,
    NoteDeleted,
    LinkAdded,
    LinkRemoved,
    TagChanged,
}

Events enable:

  • Audit trail for all memory changes
  • Time-travel queries
  • Conflict resolution
  • Extension hook triggers

Namespace Isolation

Memory namespaces provide scoped storage:

// src/memory/namespace/mod.rs
pub struct MemoryNamespace {
    pub id: String,
    pub parent: Option<String>,
    pub isolation_level: IsolationLevel,
}

pub enum IsolationLevel {
    Full,       // Completely isolated
    Inherited,  // Can read parent, writes isolated
    Shared,     // Read-write shared with parent
}

Safety Properties

ConcernMitigation
UTF-8 truncationchars().take(n) (never mid-character)
Lock poisoningunwrap_or_else(|e| e.into_inner())
SQL injectionParameterized queries via rusqlite
Vector boundsCosine clamped to [-1.0, 1.0]
Token overflowAssemblyBudget enforces limits

Deleted Components (Sovereignty Cleanup)

The following were removed on 2026-04-12:

ComponentReplacement
MemoryTier enumStructural distinction (raw vs notes)
MemoryFact.strengthRemoved (LLM decides relevance)
MemoryFact.confidenceRemoved (LLM decides relevance)
ValueEstimatorDeleted entirely
importance_weight scoringDeleted

Rationale: LLM sovereignty (R8) — deterministic heuristics for "importance" cannot outperform the LLM's own judgment about what matters in context.


Module File Map

PathContents
src/memory/mod.rsModule entry, re-exports
src/memory/assembler/mod.rsWorkingMemoryAssembler trait, HybridAssembler impl
src/memory/curated/mod.rsCuratedHotMemory, remember tool integration
src/memory/extensions/mod.rsMemoryExtension trait, ExtensionRegistry
src/memory/reflector/mod.rsMemoryReflector, ReflectionResult
src/memory/note_retrieval/mod.rsNoteFactRetrieval trait, hybrid search impl
src/memory/notes/note.rsKnowledgeNote struct
src/memory/notes/store.rsNoteStore trait
src/memory/notes/indexer.rsNoteIndexer (Markdown → SQLite)
src/memory/store/mod.rsRawMemoryStore, DreamStore, CompressionStore traits
src/memory/store/sqlite/mod.rsSqliteMemoryBackend impl
src/memory/store/sqlite/vec.rssqlite-vec integration
src/memory/retrieval/mod.rsGeneric retrieval interfaces
src/memory/scoring_pipeline/mod.rs6-stage scoring
src/memory/dreaming/mod.rsDreamDaemon, pipeline stages
src/memory/events/mod.rsMemoryEvent, event sourcing
src/memory/transcript_indexer/mod.rsTranscript chunking and indexing
src/memory/namespace/mod.rsMemoryNamespace, isolation levels
src/memory/context_comptroller/mod.rsLegacy budget manager (deprecated)

On this page