Retrieval Overview
-
Vector Search
pgvector similarity over chunk embeddings with configurable Top-K.
-
Sparse Search
PostgreSQL FTS/BM25 for exact tokens, identifiers, and literals.
-
Graph Search
Neo4j traversal to expand neighborhoods and follow relationships across files.
-
Fusion
Weighted or RRF; per-retriever contributions tracked for analysis.
-
Optional Reranker
Cross-encoder rescoring of fused candidates for precision.
Balance Recall and Precision
Increase per-leg Top-K to maximize recall; use fusion weights and reranking to restore precision.
Isolation by Corpus
Each corpus has isolated storage and graph. Queries always require corpus_id.
Latency Budget
Graph traversal increases latency with larger hop counts. Use max_hops conservatively.
Control Surface (Selected)
| Retriever | Key Fields | Defaults |
|---|---|---|
| Vector | vector_search.enabled, vector_search.top_k | 50 |
| Sparse | sparse_search.enabled, sparse_search.top_k, sparse_search.bm25_k1, sparse_search.bm25_b | 50 / 1.2 / 0.4 |
| Graph | graph_search.enabled, graph_search.max_hops, graph_search.top_k | true / 2 / 30 |
| Fusion | fusion.method, fusion.rrf_k, fusion.*_weight | rrf / 60 |
| Reranker | reranking.reranker_mode, tribrid_reranker_topn | local / 50 |
flowchart LR
Q["Query"] --> V["Vector"]
Q --> S["Sparse"]
Q --> G["Graph"]
V --> F["Fusion"]
S --> F
G --> F
F --> R["Reranker (opt)"]
R --> OUT["Results"]
F --> OUT Programmatic Search
import httpx
BASE = "http://localhost:8000"
body = {"corpus_id": "tribrid", "query": "How are pgvector indexes created?", "top_k": 10}
res = httpx.post(f"{BASE}/search", json=body).json() # (1)
for r in res.get("matches", []):
print(r["file_path"], r["score"]) # fused score (2)
curl -sS -X POST http://localhost:8000/search \
-H 'Content-Type: application/json' \
-d '{"corpus_id":"tribrid","query":"pgvector index","top_k":10}' | jq '.matches[0]'
import type { SearchRequest, SearchResponse } from "../../web/src/types/generated";
async function run(req: SearchRequest): Promise<SearchResponse> {
const r = await fetch("/search", { method: "POST", headers: {"Content-Type":"application/json"}, body: JSON.stringify(req) });
return await r.json();
}
- Search executes all retrievers concurrently
scoreis fused; provenance retained in eachChunkMatch.source
Auditable Fusion
SearchResponse.debug can include per-leg diagnostics when enabled.
Caching
Retrieval cache keys include corpus_id, query, and a hash of relevant retrieval config. Invalidate on config change or reindex.