Two searches, one ranking
Every query runs two searches over the same chunk store:
- Vector search — embeddings in Postgres via pgvector, HNSW-indexed with cosine similarity. Catches semantic matches (“toggle” finds Switch).
- Full-text search — Postgres FTS. Catches exact terms, prop names, and identifiers that embeddings blur.
Reciprocal-rank fusion (RRF) merges the two rankings, so a chunk that scores well on either signal surfaces. Results carry the fused rrf_score and an fts_mode marker alongside chunk metadata (source_path, component_name, doc_type, chunk_index, text).
Embedders
The embedder is selected by the EMBEDDER environment variable:
| Embedder | What it is | When it’s used |
|---|---|---|
fake | Deterministic, free, no network calls | Quickstart default, CI, and the eval baseline |
voyage | Voyage voyage-3 (needs VOYAGE_API_KEY) | Real semantic retrieval |
The fake embedder isn’t a mock to be deleted later — it’s what keeps evals honest. Retrieval improvements are measured against a baseline that can’t silently drift, and CI never needs an API key.
Usage-weighted boost
With telemetry enabled, retrieval can factor in what agents actually use. USAGE_BOOST turns it on and USAGE_BOOST_WEIGHT (default 0.002) controls how strongly usage counts nudge the ranking.
Measured on the retrieval eval suite, the boost moved hit@5 from 0.7903 to 0.8065 and MRR from 0.7032 to 0.7040 at the default weight — a modest, deliberate gain that was shipped because it was measured, not assumed. See Evals & benchmarks.
Where retrieval is served
The same retrieval stack answers both surfaces:
- MCP — the three tools documented in MCP tools.
- HTTP — a plain API (
POST /search) used by scripts, the benchmark harness, and anything that isn’t an MCP client.