Design enterprise hybrid retrieval with access-aware ranking
Expected question
"Design hybrid retrieval for enterprise RAG: BM25 + dense, fused with RRF, then cross-encoder rerank. Where does authorization happen relative to scoring? How do you avoid 'semantic' theater (Jaccard pretending to be embeddings)?"
Variant forms
- "Our 'hybrid' RAG is lexical only — add dense retrieval without breaking ACLs."
- "Explain RRF vs weighted score fusion when BM25 and cosine live on different scales."
- "Design Qdrant (or OpenSearch) so tenant filters run before ANN results are trusted."
- "When do you use a cross-encoder vs a cheap score-boost reranker?"
- "Calibrate decline thresholds across BM25/RRF scores vs cross-encoder logits."
- "How do you eval paraphrase recall vs exact-term recall?"
Where this actually gets asked
Staff+/Principal AI infra and search platform loops. Reviewers who have shipped RAG will probe whether "hybrid" means real dual-signal retrieval or a README badge over token overlap.
Executive summary
30-second thesis
I'd push access filters before any ranking signal — BM25 and dense both run under ACL predicates. Fuse with RRF because the score scales don't match, then cross-encode a bounded N. If "hybrid" is Jaccard dressed up as embeddings, I'll say so.
2-minute answer
Lexical catches exact terms; dense catches paraphrase. Their raw scores aren't commensurate, so I'd fuse by rank (RRF) unless we've earned calibrated weighted fusion offline. Cross-encoder reranks a widened candidate set — larger N helps until P99 dies; I'd reverse when the latency budget fails.
The NFR that kills you is authZ: filter-after-ANN still fetched unauthorized neighbors into memory and can leak via traces. Push tenant predicates into the store query; degrade to lexical-only under load rather than dropping authz. Cache keys include principal, policy, and index versions.
Demo can use local/hash embeddings with honesty; Strict uses real vectors. Zero-vector + scroll-as-search is theater — I'd refuse it. Limitation: fixed 0.65/0.30 fusion weights without slice evidence are a smell I'd call out in the room.
What I'd ask them: Index size and P99 budget? Filter pushdown support in the vector store? Decline threshold ownership? Demo vs Strict embedding posture?
Quantitative trade-offs
| Decision | Trade-off and reversal evidence | Evidence class |
|---|---|---|
| RRF vs tuned weighted fusion | RRF works without score calibration; reverse to learned fusion only with offline proof and owned features. | H |
| Cross-encoder depth vs latency | Larger rerank N improves quality; reverse when P99 budget fails and eval allows smaller N. | H |
| Filter pushdown vs app-side filter | Pushdown prevents leakage windows; app-side filter is a last resort with audited proofs. | O/H |
Numbers and thresholds in interview delivery should be labeled H unless the candidate can defend a measured baseline. Open repositories are O; researched public patterns are R. Do not upgrade O/R into employer P adoption.
ML fundamentals
Calibrate retrieval/rerank quality on slices; treat fixed 0.65/0.30 weights without evidence as a smell.
Migration and rollout
Hybrid retrieval migrations break when lexical and dense indexes diverge.
- Dual-write BM25 + dense with shared doc ids and ACL predicates.
- Shadow RRF + rerank; compare slice metrics vs dense-only baseline.
- Prove access filter still runs before fusion — fusion must not reintroduce denied docs.
- Canary query classes that need sparse (IDs, exact phrases) separately from semantic queries.
- Rollback = previous retriever config. Kill switch = lexical-only degraded mode.
Org ownership and operating model
- Search / RAG platform owns hybrid fusion, rerankers, and access-aware retrieval invariants.
- Corpus owners own metadata quality that sparse retrieval depends on.
- Security owns ACL predicate pushdown requirements.
- Eval owns sparse-vs-dense slice suites.
- SRE owns index lag and query p99.
Requirements
Functional
- Lexical retriever (BM25 with k1/b) and dense retriever (real embeddings).
- Fuse candidate lists with RRF (or documented alternative).
- Optional cross-encoder second stage on widened
top_k. - Access filter before any ranking signal is computed for unauthorized chunks.
Non-functional
- Dense path must work without paid APIs in Demo (local/hash) and with gateway/OpenAI in Strict/prod.
- Vector upserts use real dimensions; no zero-vector + scroll-as-search.
- Telemetry exposes fusion/reranker profile on
/health.
Core entities
- Lexical hit list / Dense hit list → RRF fused ranking.
- RetrievalHit: chunk + score + reasons (
bm25,dense,rrf,cross_encoder). - AccessPolicy: tenant, clearance, groups.
API / interface
POST /v1/retrieve
{"query":"termination clause","top_k":10,"principal":{"tenant_id":"acme","groups":["legal"]}}
→ 200 {"hits":[{"chunk_id":"c42","score":0.91,"reasons":["bm25","dense","cross_encoder"]}],"profile":"strict"}
→ 403 {"code":"principal_not_authorized"}
GET /health
→ 200 {"retrieval":{"lexical":"bm25","dense":"qdrant","fusion":"rrf","reranker":"cross_encoder"}}
Data Flow
Rendering architecture diagram…
High-level design
Rendering architecture diagram…
Deep dive 1: access-before-ranking
Filtering after vector search still fetched unauthorized neighbors into memory and can leak via bugs/traces. Push tenant predicates into the store query when possible; always enforce policy before returning hits.
Deep dive 2: RRF vs weighted sums
BM25 magnitudes and cosine similarities are incommensurable. Fixed weights (0.65/0.30) without calibration are a smell. RRF ranks are scale-free.
Deep dive 3: cross-encoder ops
Load once at process start; install weights in the image; do not call build_reranker() per request if that reloads the model. ScoreBoost is Demo-only honesty when CE is absent.
Staff+/Principal signal rubric
- Mid-level: lexical or vector retrieval with basic top-k ranking.
- Senior: hybrid two-stage + rerank vocabulary.
- Staff+: RRF rationale; ACL ordering; real embeddings.
- Principal: dual Demo/Strict embedding posture; decline calibration; Qdrant filter + no scroll theater.
Follow-up questions to expect
- "What breaks first at 1B chunks?" — Usually ANN fan-out, filter selectivity, or rerank N under P99 — not "BM25 can't scale" in the abstract. I'd ask for their QPS and corpus shape.
- "How do you A/B fusion strategies safely?" — Shadow retrieve, offline slice eval, then canary with holdback. Don't flip global fusion on a vibe.
- "Show live
/health.retrievalfor the profile under test." — Lexical, dense, fusion, reranker names on health — so "hybrid" is observable, not a README claim.
Related
- enterprise_rag_platform —
InMemoryHybridRetriever, Qdrant adapter, ADR-0008 - ADR-0001 Hybrid retrieval
- 02 RAG platform at scale · 22 PDF Q&A citations