-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Two installable packages:
-
recall/— the library. No server, no framework, no global state. -
recall_mcp/— an MCP server wrapping it, so an agent can call the library as a tool.
recall_mcp depends on recall. Nothing in recall knows the MCP server exists.
flowchart TB
Q([query]) --> TS["trust.trusted_search()"]
TS --> R["retriever.HybridRetriever"]
R --> E["embeddings.Embedder"]
R --> SD["store.query_dense · pgvector"]
R --> SS["store.query_sparse · full-text"]
R -. optional .-> LS["learned sparse · SPLADE"]
SD --> RRF["Reciprocal Rank Fusion"]
SS --> RRF
LS -. optional .-> RRF
RRF --> RK["rerank.Reranker (optional)"]
RK --> G["guards: gap_warning · staleness"]
G --> EV["trust.evaluate()"]
SUP["store.supersession()"] --> EV
CAL["calibration.Calibration or generation binding"] --> EV
EV --> EN["entailment.apply_entailment (opt-in)"]
EN --> EB["evidence bundle (optional)"]
EN --> O([TrustedResult])
The shape worth noticing: trust.evaluate() is pure. It reads no clock, touches no
database, and performs no I/O — now, the supersession map and the calibration are all passed
in. That is why the trust semantics can be tested exhaustively without a database, and why the
ordering rules are verifiable rather than merely observed.
| Module | Owns | Depends on |
|---|---|---|
types.py |
The data contract: Chunk, ScoredChunk, TrustedHit, TrustedResult, Verdict, Validity, Provenance, StalenessReport. All frozen dataclasses. |
nothing |
store.py |
PostgreSQL + pgvector. RLS, dense/sparse queries, upsert, prune, the supersession scan and its cache, connection mode, retry, timeouts, DSN security checks. The largest legacy store module, and the main one that talks to chunk tables. | types |
generation_store.py |
Generation-aware serving over immutable chunk tables, with schema checks and active generation routing for production search. |
store, control_plane
|
migration.py / schema.py
|
Ordered SQL migration runner, checksum ledger, grants generator, schema status and plan/apply operations. | store |
control_plane.py |
Enterprise generation registry, tenant routes, shadow cutover, migration event replay and readiness support. |
schema, generation_store
|
embeddings.py |
The Embedder protocol and its implementations (hashing, FastEmbed, sentence-transformers, Voyage), plus batching, retry-with-backoff and dimension checking. |
nothing |
retriever.py |
HybridRetriever: dense, sparse and optional learned sparse legs, RRF fusion, rerank hand-off, and annotation with gap/staleness. |
store, embeddings, rerank, guards, types
|
sparse.py |
Learned sparse SPLADE encoding and sparse-vector indexing support. | embeddings |
rerank.py |
The Reranker protocol, a no-op, and a cross-encoder implementation. |
types |
guards.py |
The two cheap honesty signals: gap_warning (are all candidates below threshold) and staleness (is the index too old). Holds the default gap threshold. |
types |
calibration.py |
Per-embedder abstention threshold and the cosine→confidence mapping; fitting from labelled samples; safe load/save. | observability |
frontmatter.py |
The dependency-free frontmatter parser, validity-date interpretation, and supersedes: reference normalisation. |
nothing |
trust.py |
Verdict assignment, successor resolution, successor promotion, valid-first ordering, the abstention decision and its reason. |
store, embeddings, retriever, rerank, calibration, frontmatter, guards, types
|
entailment.py |
Opt-in near-miss guard: demotes verdict-ok hits that do not entail an answer. Pure post-processing over a TrustedResult, mirroring trust. |
trust, types
|
evidence.py |
Builds citable evidence bundles from trusted hits, renders generator-neutral prompts, and validates that generated citations resolve to supplied chunk IDs. |
types, trust
|
index.py |
Indexer — file discovery, chunking (prose and code), NUL sanitisation, index-root confinement, content-hash skip, batched writes, pruning of vanished sources and the prune guard. |
store, embeddings, frontmatter, cache, lint, observability
|
cache.py |
Content-addressed embedding cache (SQLite), keyed by embedder name + dim + text. | embeddings |
lint.py |
Static supersession-graph lint: dangling edges, self-supersession, cycles, ambiguous targets, invalid dates. No database. | frontmatter |
fix.py |
Extracts prose closure markers and proposes supersedes: edges — under rules that refuse far more often than they act. |
frontmatter, lint
|
check.py |
Write-time gate for a single file being authored: surfaces candidate edges while the author is still present. |
frontmatter, lint
|
semantic_lint.py |
The missing-edge lint: queries the index with a new memo's text to surface closed decisions it should probably reference. |
trust, store, embeddings
|
observability.py |
get_logger (namespaced, never configures handlers), opt-in configure_logging with JSON output, and the METRICS counter/histogram registry. |
nothing |
timing.py |
Decorators that wrap an Embedder or Reranker to record per-call latency. Used by the eval harness. |
embeddings, rerank, types
|
cli.py |
Argument parsing and the command implementations. | most of the above |
_env.py |
Minimal .env loader for local development. Never overrides an already-set variable. |
nothing |
eval/ |
The evaluation harness: ablation matrix, trust eval, near-miss eval, calibration fitting, labelled-question runner, synthetic corpus generation, scale/latency runner, metrics. | most of the above |
| Module | Owns |
|---|---|
server.py |
Builds the FastMCP server, the five tools, the lifespan, generation-mode startup checks, auth wiring, scope enforcement per tool, and transport selection. |
service.py |
The tool bodies, independent of MCP: search, evidence, index, forget, stats. Pydantic response models. This is where the logic lives, so it is testable without a server. |
auth.py |
Bearer-token registry, principal→tenant mapping, scope definitions, token-file parsing (plaintext or SHA-256 digests), expiry, and the fail-closed configuration check. |
limits.py |
Per-tenant token buckets: call rates per tool class and an aggregate indexing byte budget. |
stores.py |
Per-tenant store construction and lifetime. |
The trust layer is a pure function over a retrieval result. It could have been folded into the retriever's SQL — one query returning already-judged rows would be faster. It is separate because the judgment is the part most likely to be wrong, and a pure function is the only shape in which "a superseded hit with a top cosine loses to its lower-scoring successor" is a test rather than an observation.
Entailment mirrors trust rather than extending it. apply_entailment takes a TrustedResult
and returns a TrustedResult. It judges only verdict-ok hits — judging an already-demoted hit
would waste a model call and, worse, could resurrect a superseded memory by ranking it back up.
The two stages stack; neither replaces the other, and the measurements say so explicitly.
A store is bound to one tenant for its lifetime. Tenancy is not an argument you pass to a
query — it is a property of which store object you hold, enforced in the database by a row-level
security policy comparing against a per-connection setting. That is what makes a forgotten
WHERE clause return nothing rather than another tenant's memories, and it is why the MCP
server's authentication has to resolve a tenant before any tool body runs. → Tenancy-and-Auth
Production serving is migration and generation driven. Normal data operations and MCP startup
do not run DDL. A migration job applies ordered SQL through RECALL_MIGRATION_DSN; runtime traffic
uses RECALL_SERVING_DSN. In production, searches read the tenant's active generation and strict
trust refuses when the generation, lineage or certified calibration is not ready. → Installation-and-Setup
- Retrieval mechanics → Retrieval-Pipeline
- Verdicts, supersession, calibration, abstention → The-Trust-Layer
- Calling it → Python-API-and-MCP
- Every configuration knob → Configuration-Reference
This wiki explains design and intent. Measured figures, deployment defaults and CLI flag values
live in the repository, versioned with the code that produced them:
FINDINGS ·
RESULTS ·
MIGRATIONS ·
.env.example · --help.
If a page here disagrees with the repo, the repo is right.
Concepts
Using it
- Installation-and-Setup
- Embedders-and-Rerankers
- Configuration-Reference
- CLI-Reference
- Python-API-and-MCP
- Tenancy-and-Auth
Evidence
Contributing
In the repo