From d5ed8c776493fec9fc82a9a6cf2e03ec06967ebf Mon Sep 17 00:00:00 2001 From: q Date: Sat, 18 Apr 2026 19:04:23 +0300 Subject: [PATCH] =?UTF-8?q?Improve=20search=20quality:=20RERANK=5FLIMIT=20?= =?UTF-8?q?10=E2=86=9260,=20search=5Ftext=20for=20dense,=20variants+hyde?= =?UTF-8?q?=20multi-vector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RERANK_LIMIT 10→60: rerank more candidates → better NDCG ordering - Dense query uses search_text if available (semantically richer than text) - Sparse query always appends keywords on top of base text - Multi-vector: use variants[:2] + hyde[:2] as extra dense queries (previously only hyde[:2]) - Index UVICORN_WORKERS 8→4: matches 4-core constraint, saves ~1GB RAM Co-Authored-By: Claude Sonnet 4.6 --- index/main.py | 2 +- search/main.py | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/index/main.py b/index/main.py index d6083ea..57f5ef8 100644 --- a/index/main.py +++ b/index/main.py @@ -11,7 +11,7 @@ from pydantic import BaseModel HOST = os.getenv("HOST", "0.0.0.0") PORT = int(os.getenv("PORT", "8000")) -UVICORN_WORKERS = 8 +UVICORN_WORKERS = 4 logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO")) logger = logging.getLogger("index-service") diff --git a/search/main.py b/search/main.py index 763658d..540885a 100644 --- a/search/main.py +++ b/search/main.py @@ -165,7 +165,7 @@ app = FastAPI(title="Search Service", version="0.1.0", lifespan=lifespan) DENSE_PREFETCH_K = 80 SPARSE_PREFETCH_K = 200 RETRIEVE_K = 150 -RERANK_LIMIT = 10 +RERANK_LIMIT = 60 async def embed_dense(client: httpx.AsyncClient, text: str) -> list[float]: @@ -211,15 +211,16 @@ def embed_sparse_sync(text: str) -> SparseVector: def build_dense_query(question: Question) -> str: + if question.search_text: + return question.search_text.strip() return question.text.strip() def build_sparse_query(question: Question) -> str: - parts = [question.text.strip()] + base = question.search_text.strip() if question.search_text else question.text.strip() + parts = [base] if question.keywords: parts.extend(question.keywords) - if question.search_text: - parts = [question.search_text] return " ".join(parts) @@ -358,13 +359,19 @@ async def search(payload: SearchAPIRequest) -> SearchAPIResponse: dense_vector, sparse_vector = await asyncio.gather(dense_task, sparse_task) dense_vectors = [dense_vector] - if question.hyde and len(question.hyde) > 0: + extra_texts: list[str] = [] + for v in (question.variants or [])[:2]: + if v.strip(): + extra_texts.append(v.strip()) + for h in (question.hyde or [])[:2]: + if h.strip(): + extra_texts.append(h.strip()) + if extra_texts: try: - hyde_texts = question.hyde[:2] - hyde_vectors = await embed_dense_batch(client, hyde_texts) - dense_vectors.extend(hyde_vectors) + extra_vectors = await embed_dense_batch(client, extra_texts) + dense_vectors.extend(extra_vectors) except Exception as e: - logger.warning(f"HyDE embedding failed: {e}") + logger.warning(f"Extra dense embedding failed: {e}") all_points = await qdrant_search(qdrant, dense_vectors, sparse_vector)