Concepts

How retrieval works

The hybrid pipeline, the memory model, and the trust machinery: one page, no hand-waving.

This is the mental model the API and the MCP tools (generated catalog) sit on top of. Everything here describes what the engine actually does; the full mechanical contract lives in the generated reference pages (REST and MCP schemas).

The retrieval pipeline

Hybrid retrieval: BM25 (Portuguese and English) and dense vectors (LFM2.5-Embedding-350M, 1024 dimensions) fused by reciprocal-rank fusion, then a cross-encoder rerank (BGE-Reranker-v2-m3) and MMR for diversity. An LLM only expands the query; it never ranks. Personalized PageRank exists behind a flag and is off in production.

                         ┌─ BM25 (pt + en, pgturbohybrid) ─────────────────┐
query ─▶ LLM expansion ──┤                                                 ├─ RRF ─▶ cross-encoder (BGE-Reranker-v2-m3) ─▶ MMR ─▶ top-K
                         └─ dense vectors (LFM2.5-Embedding-350M, 1024d) ──┘
  1. Query expansion (LLM) — an LLM rewrites/expands the query into search variants. It is the only generative model in the path; it never orders results.
  2. Lexical (BM25, Portuguese and English) — pgturbohybrid keeps BM25 and dense in a single index; tenant_hash rides in the index INCLUDE, so the tenant filter happens inside the index scan, not as a post-filter. On a multi-tenant index, that is the difference between an ANN that works and one that silently starves small tenants.
  3. Dense — 1024-dimension LFM2.5-Embedding-350M embeddings in content_vectors (deduplicated by content hash), HNSW with ef_search=1000.
  4. RRF fusion — reciprocal-rank fusion merges the two legs without needing comparable scores.
  5. Cross-encoder rerank — BGE-Reranker-v2-m3 scores query-document pairs directly.
  6. MMR — the diversity filter (diverse: true, default) keeps the top-K from being ten paraphrases of one document.

mode on /search and memory_retrieve (auto, keyword, semantic, causal, hybrid) biases how the query is routed inside this same pipeline; auto decides for you.

Ranking is the last suspect, not the first. When retrieval misses, the honest order of investigation is: candidate pool (was the document indexed at all?) → hydration (was the body loaded?) → snippet/window rendering (was the answer inside the delivered excerpt?) → ranking. The engine's own debugging history is a chain of nine ranking "fixes" that changed nothing while the document was being cut at render time.

The memory model

LayerWhat it isKey behavior
DocumentsThe corpus. collection + path identity, content deduplicated by hash (cross-collection too)Async embedding (embed_state); visibility tenant/private
Consolidated observationsHigher-order synthesis of documentsConsolidation worker, confidence gate ≥ 0.65
Keyed facts(fact_key, as_of) snapshots with an authority levelSupersede prose. A document contradicting a keyed fact loses
Entity triples (KG)SPO facts, temporal, per (tenant_id, entity_id)SHACL pre-check; rejects go to quarantine, never silently in
EpisodesBounded dialogue summariesEpisodic retrieval; scoped memory_prepare
DecisionsFirst-class records: category, scenario, reasoning, outcome, confidenceHash-chained audit trail; causal links (caused/influenced/precedent_for)
ProvenanceW3C PROV-O lineage, SHA-256 hash chainEvery triple write is tracked; tamper-evident by construction
Fact conflictsExplicit value/temporal/relationship contradictionsDetected on consolidation ticks and on demand; resolved by strategy

The write path mirrors this: raw documents in, entity extraction (GLiNER zero-shot) enriches, consolidation synthesizes observations, decision-extractor hooks promote decisions, and every triple write lands with provenance.

Tenancy and identity

  • Every tenant-scoped table is RLS-enforced with a tenant_id policy; the runtime connects as an app role without BYPASSRLS.
  • Identity comes from the token — never guessed, never hardcoded. Zero tenant/user/company specifics in source.
  • Entity identity is (tenant_id, entity_id); the vault isolates nothing by itself — the tenant does.

Consolidation and decay

Memory that never decays is a landfill. Three mechanisms keep it honest:

  • Lifecycle policies archive stale documents (pinned ones exempt), lifecycle_sweep runs them (dry-run by default).
  • Curation — pin (+0.3 permanent boost), snooze (hide N days) — is the manual lever, driven by the memory_used feedback loop.
  • The usage ledger records every operation by channel; value_daily pre-aggregates it. Ranking learns from declared usage, not from guesses.

The one-call contract

POST /api/v1/memory/prepare (REST) and memory_prepare (MCP) assemble a full context bundle — recall + vault facts + foresights + funnel (episodes, lessons, keyed facts, top documents) — in one call with a token budget. This is the intended integration point for prompt hooks; the read tools are for drilling, not for building your own bundle.

On this page