Redrob-hackathon / lib /query_expansion.py
Mohit0708's picture
Initial commit
7b833a7
Raw
History Blame Contribute Delete
7.87 kB
"""
lib/query_expansion.py — V6 Query Expansion Engine
Expands JD skill terms with synonyms, related technologies, and
domain-specific variations. This improves retrieval coverage without
hardcoding every possible skill name.
Example:
"semantic search" -> ["semantic search", "dense retrieval", "vector search",
"ANN", "HNSW", "FAISS", "Pinecone", "Milvus", "Qdrant"]
The expansion happens ONCE at JD parse time, not per-candidate.
Expanded terms are stored in the JD understanding for use by all
downstream modules.
"""
from __future__ import annotations
# Expansion map: canonical term -> expanded synonyms
# These cover common synonyms and related technologies in ML/AI/search
EXPANSION_MAP = {
# Embeddings
"embedding": ["embedding", "dense vector", "vector representation",
"dense representation", "embedding model"],
"sentence-transformers": ["sentence-transformers", "sentence transformers",
"sbert", "sentence bert"],
"openai embeddings": ["openai embeddings", "text-embedding-ada",
"text-embedding-3", "openai embedding"],
"bge": ["bge", "bge embedding", "bge-m3", "bge-large", "bge-base"],
"e5": ["e5", "e5 embedding", "e5-large", "e5-base", "e5-mistral"],
# Vector DBs
"pinecone": ["pinecone", "pinecone vector", "pinecone db"],
"weaviate": ["weaviate", "weaviate vector db"],
"qdrant": ["qdrant", "qdrant vector"],
"milvus": ["milvus", "milvus vector db", "zilliz"],
"faiss": ["faiss", "facebook ai similarity search"],
"opensearch": ["opensearch", "aws opensearch", "elastic opensearch"],
"elasticsearch": ["elasticsearch", "elastic search", "es cluster"],
"vector database": ["vector database", "vector db", "vector store",
"vector index", "embedding store", "ann index"],
# Search/Retrieval
"semantic search": ["semantic search", "dense retrieval", "vector search",
"neural search", "meaning-based search"],
"hybrid search": ["hybrid search", "hybrid retrieval", "sparse+dense",
"bm25+vector", "lexical+semantic"],
"bm25": ["bm25", "okapi bm25", "tf-idf search", "lexical search",
"keyword search", "sparse retrieval"],
"reranking": ["reranking", "re-ranking", "cross-encoder rerank",
"reranker", "second-stage ranking"],
"rag": ["rag", "retrieval-augmented generation", "rag pipeline",
"rag system", "retrieval augmented"],
# Ranking
"ranking": ["ranking", "learning to rank", "ltr", "ranker",
"ranking model", "rank ordering"],
"learning to rank": ["learning to rank", "ltr", "lambdarank",
"lambdamart", "listwise ranking", "pairwise ranking"],
"ndcg": ["ndcg", "normalized discounted cumulative gain",
"ndcg@k", "ndcg evaluation"],
# LLM/Fine-tuning
"fine-tuning": ["fine-tuning", "finetuning", "fine tuning", "fine-tune",
"supervised fine-tuning", "sft"],
"lora": ["lora", "low-rank adaptation", "low rank adaptation"],
"qlora": ["qlora", "quantized lora", "quantized low-rank"],
"peft": ["peft", "parameter efficient fine-tuning",
"parameter-efficient fine-tuning"],
"rlhf": ["rlhf", "reinforcement learning from human feedback"],
"dpo": ["dpo", "direct preference optimization"],
# Production
"production": ["production", "prod", "deployed", "live", "shipped"],
"at scale": ["at scale", "large scale", "production scale",
"internet scale", "high scale"],
# Evaluation
"evaluation framework": ["evaluation framework", "eval framework",
"evaluation pipeline", "eval pipeline",
"ranking evaluation", "quality evaluation"],
"a/b test": ["a/b test", "ab test", "ab testing", "a/b testing",
"online experiment", "online evaluation"],
"mrr": ["mrr", "mean reciprocal rank"],
# NLP
"nlp": ["nlp", "natural language processing", "natural language",
"text understanding", "language model"],
"llm": ["llm", "large language model", "large language models",
"foundation model", "generative ai"],
# Infrastructure
"distributed system": ["distributed system", "distributed systems",
"distributed computing", "distributed architecture"],
"low latency": ["low latency", "sub-millisecond", "real-time",
"low latency inference", "fast inference"],
}
# Domain-level expansions (applied to domain names from JD parser)
DOMAIN_EXPANSIONS = {
"search": ["information retrieval", "search engine", "search system",
"search relevance", "query understanding", "search ranking"],
"ranking": ["rank ordering", "relevance ranking", "learning to rank",
"ranking algorithm", "position ranking", "rank model"],
"embeddings": ["vector representations", "dense vectors", "embedding models",
"sentence embeddings", "passage embeddings"],
"vector_db": ["vector index", "approximate nearest neighbor", "ann",
"similarity search", "vector search engine"],
"llm": ["large language model", "foundation model", "generative model",
"language model", "transformer model", "gpt", "llama", "mistral"],
"engineering": ["software engineering", "systems engineering",
"backend engineering", "machine learning engineering"],
"evaluation": ["ranking evaluation", "quality assessment",
"relevance evaluation", "search quality"],
"infrastructure": ["cloud infrastructure", "ml infrastructure",
"data infrastructure", "platform engineering"],
"nlp": ["natural language processing", "text processing",
"language understanding", "computational linguistics"],
}
def expand_skill(skill: str) -> list[str]:
"""Expand a single skill into a set of synonyms and related terms."""
s = skill.lower().strip()
# Check direct match
if s in EXPANSION_MAP:
return EXPANSION_MAP[s]
# Check partial match (e.g., "fine-tun" matches "fine-tuning")
for key, expansions in EXPANSION_MAP.items():
if key in s or s in key:
return expansions
return [s] # no expansion, return original
def expand_domain(domain: str) -> list[str]:
"""Expand a domain name into related terms."""
return DOMAIN_EXPANSIONS.get(domain.lower(), [domain])
def expand_jd_skills(required: dict[str, list[str]],
preferred: dict[str, list[str]]) -> dict[str, list[str]]:
"""
Expand all JD skills with synonyms. Returns a merged expansion dict.
Each original skill maps to its expanded set. This is used to improve
retrieval and skill matching without changing the core JD understanding.
"""
expanded = {}
for domain, skills in {**required, **preferred}.items():
for skill in skills:
expansions = expand_skill(skill)
# Also add domain-level expansions
domain_exps = expand_domain(domain)
all_terms = list(set(expansions + domain_exps))
expanded[skill] = all_terms
return expanded
def get_expanded_text(jd_text: str) -> str:
"""
Create an expanded version of JD text with synonyms appended.
Used as the query for TF-IDF/SVD similarity computation.
"""
extra_terms = []
for skill, expansions in EXPANSION_MAP.items():
if skill in jd_text.lower():
extra_terms.extend(expansions)
# Deduplicate and add to original
seen = set(jd_text.lower().split())
new_terms = [t for t in extra_terms if t not in seen]
return jd_text + " " + " ".join(new_terms)