Improve search quality: RERANK_LIMIT 10→60, search_text for dense, variants+hyde multi-vector
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
84eec2321e
commit
4822bbb24b
2 changed files with 17 additions and 10 deletions
|
|
@ -11,7 +11,7 @@ from pydantic import BaseModel
|
||||||
|
|
||||||
HOST = os.getenv("HOST", "0.0.0.0")
|
HOST = os.getenv("HOST", "0.0.0.0")
|
||||||
PORT = int(os.getenv("PORT", "8000"))
|
PORT = int(os.getenv("PORT", "8000"))
|
||||||
UVICORN_WORKERS = 8
|
UVICORN_WORKERS = 4
|
||||||
|
|
||||||
logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO"))
|
logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO"))
|
||||||
logger = logging.getLogger("index-service")
|
logger = logging.getLogger("index-service")
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ app = FastAPI(title="Search Service", version="0.1.0", lifespan=lifespan)
|
||||||
DENSE_PREFETCH_K = 80
|
DENSE_PREFETCH_K = 80
|
||||||
SPARSE_PREFETCH_K = 200
|
SPARSE_PREFETCH_K = 200
|
||||||
RETRIEVE_K = 150
|
RETRIEVE_K = 150
|
||||||
RERANK_LIMIT = 10
|
RERANK_LIMIT = 60
|
||||||
|
|
||||||
|
|
||||||
async def embed_dense(client: httpx.AsyncClient, text: str) -> list[float]:
|
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:
|
def build_dense_query(question: Question) -> str:
|
||||||
|
if question.search_text:
|
||||||
|
return question.search_text.strip()
|
||||||
return question.text.strip()
|
return question.text.strip()
|
||||||
|
|
||||||
|
|
||||||
def build_sparse_query(question: Question) -> str:
|
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:
|
if question.keywords:
|
||||||
parts.extend(question.keywords)
|
parts.extend(question.keywords)
|
||||||
if question.search_text:
|
|
||||||
parts = [question.search_text]
|
|
||||||
return " ".join(parts)
|
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_vector, sparse_vector = await asyncio.gather(dense_task, sparse_task)
|
||||||
|
|
||||||
dense_vectors = [dense_vector]
|
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:
|
try:
|
||||||
hyde_texts = question.hyde[:2]
|
extra_vectors = await embed_dense_batch(client, extra_texts)
|
||||||
hyde_vectors = await embed_dense_batch(client, hyde_texts)
|
dense_vectors.extend(extra_vectors)
|
||||||
dense_vectors.extend(hyde_vectors)
|
|
||||||
except Exception as e:
|
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)
|
all_points = await qdrant_search(qdrant, dense_vectors, sparse_vector)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue