Default search_docs to dense; make rerank opt-in (#17)
Co-authored-by: claude <[email protected]>
This commit was merged in pull request #17.
This commit is contained in:
+21
-14
@@ -61,6 +61,9 @@ API_LESSONS_MD = Path(__file__).resolve().parent / "api_lessons.md"
|
||||
RERANK_URL = os.environ.get("RERANK_URL", "").rstrip("/") or None
|
||||
RERANK_POOL = int(os.environ.get("RERANK_POOL", "50"))
|
||||
RERANK_TIMEOUT = float(os.environ.get("RERANK_TIMEOUT", "30"))
|
||||
# Opt-in. Watchtower keeps the old container env (RERANK_URL is set in
|
||||
# live compose); default off so a code-only ship actually stops reranking.
|
||||
RERANK_ENABLED = os.environ.get("RERANK_ENABLED", "").lower() in ("true", "1", "yes", "on")
|
||||
|
||||
HYBRID_SEARCH = os.environ.get("HYBRID_SEARCH", "").lower() in ("true", "1", "yes", "on")
|
||||
RRF_K = int(os.environ.get("RRF_K", "60"))
|
||||
@@ -295,11 +298,10 @@ def search_docs(
|
||||
bm25_where = _where_for_bm25(version, platform, bundle_id)
|
||||
pool = max(k * 5, 50)
|
||||
|
||||
# Retrieval mode selection. Eval on this corpus (2026-05-22, 22 golden
|
||||
# queries) showed BM25 MRR=0.88 vs dense MRR=0.54 vs hybrid MRR=0.69 —
|
||||
# HPE structured docs use controlled vocabulary, so lexical match wins.
|
||||
# Dense is kept as fallback when BM25 has no tokens to chew on (e.g.
|
||||
# purely stopword queries). HYBRID_SEARCH=true forces RRF fusion.
|
||||
# Retrieval mode. Eval 2026-09-30 (22 queries, post nomic prefixes +
|
||||
# heading-recursive chunking): dense MRR=0.966 P@1=0.955 vs
|
||||
# bm25+rerank MRR=0.827 P@1=0.773. Default is dense. HYBRID_SEARCH
|
||||
# still forces RRF. Rerank is opt-in (RERANK_ENABLED) — it now hurts.
|
||||
bm = _bm25()
|
||||
docs: list[str] = []
|
||||
metas: list[dict] = []
|
||||
@@ -322,7 +324,18 @@ def search_docs(
|
||||
else "dense_only")
|
||||
retrieval_mode = "hybrid"
|
||||
except Exception as e:
|
||||
log.warning("hybrid failed, falling back to BM25→dense: %s", e)
|
||||
log.warning("hybrid failed, falling back to dense: %s", e)
|
||||
|
||||
if not docs:
|
||||
try:
|
||||
res = _query_dense(col, query, k, where)
|
||||
docs = (res.get("documents") or [[]])[0]
|
||||
metas = (res.get("metadatas") or [[]])[0]
|
||||
dists = (res.get("distances") or [[]])[0]
|
||||
retrieval_mode = "dense"
|
||||
top1_source = "dense_only"
|
||||
except Exception as e:
|
||||
log.warning("dense retrieval failed, falling back to BM25: %s", e)
|
||||
|
||||
if not docs and bm is not None:
|
||||
try:
|
||||
@@ -336,16 +349,10 @@ def search_docs(
|
||||
retrieval_mode = "bm25"
|
||||
top1_source = "bm25_only"
|
||||
except Exception as e:
|
||||
log.warning("BM25 retrieval failed, falling back to dense: %s", e)
|
||||
|
||||
if not docs:
|
||||
res = _query_dense(col, query, k, where)
|
||||
docs = (res.get("documents") or [[]])[0]
|
||||
metas = (res.get("metadatas") or [[]])[0]
|
||||
dists = (res.get("distances") or [[]])[0]
|
||||
log.warning("BM25 retrieval failed: %s", e)
|
||||
|
||||
reranker_fired = False
|
||||
if RERANK_URL and docs:
|
||||
if RERANK_URL and RERANK_ENABLED and docs:
|
||||
# Pull a deeper pool to give the reranker something to chew on.
|
||||
# We over-fetch up to RERANK_POOL chunks from whichever retriever
|
||||
# already won, then ask the reranker to pick the final top-k.
|
||||
|
||||
Reference in New Issue
Block a user