| """ |
| text_builder.py — Build rich text representations of candidates for embedding. |
| |
| The quality of your FAISS retrieval depends directly on the quality of the |
| text you feed to the encoder. Naive approach: just dump all fields as a string. |
| Better approach: weight important fields, structure the text semantically, |
| and help the model understand what matters for this JD. |
| |
| Key insight from the JD: |
| - Career descriptions are more important than skill lists |
| - Product company experience > consulting company experience |
| - Title matters (ML Engineer >> Marketing Manager, even with identical skills) |
| - Skills with duration and proficiency > bare skill names |
| """ |
|
|
| from typing import Dict, Any, List |
|
|
|
|
| |
| CORE_AI_SKILLS = { |
| "embeddings", "vector search", "faiss", "pinecone", "weaviate", "qdrant", |
| "milvus", "opensearch", "elasticsearch", "sentence-transformers", "sentence transformers", |
| "dense retrieval", "hybrid search", "bm25", "retrieval", "ranking", |
| "learning to rank", "reranking", "cross-encoder", "bi-encoder", |
| "pytorch", "transformers", "hugging face", "huggingface", |
| "llm", "llms", "large language model", "rag", "retrieval augmented generation", |
| "fine-tuning", "fine tuning", "lora", "qlora", "peft", |
| "nlp", "natural language processing", "text embeddings", |
| "ndcg", "mrr", "map", "evaluation", "a/b testing", |
| "python", "machine learning", "deep learning", |
| "recommendation system", "search system", "information retrieval", |
| "xgboost", "lightgbm", "neural ranking" |
| } |
|
|
| |
| CONSULTING_COMPANIES = { |
| "tcs", "tata consultancy", "infosys", "wipro", "accenture", |
| "cognizant", "capgemini", "hcl technologies", "hcl", "tech mahindra", |
| "hexaware", "mphasis", "l&t infotech", "ltimindtree", "mindtree", |
| "niit technologies", "syntel", "patni", "mastech", "zensar", |
| "igate", "merilytics", "firstsource", "wns global" |
| } |
|
|
| |
| TARGET_LOCATIONS = { |
| "pune", "noida", "hyderabad", "mumbai", "delhi", "bangalore", |
| "bengaluru", "gurugram", "gurgaon", "ncr", "delhi ncr" |
| } |
|
|
| PROFICIENCY_WEIGHT = { |
| "expert": 4, |
| "advanced": 3, |
| "intermediate": 2, |
| "beginner": 1 |
| } |
|
|
|
|
| def build_candidate_text(candidate: Dict[str, Any]) -> str: |
| """ |
| Build a rich, structured text string for a candidate. |
| This text is what gets embedded by SentenceTransformer. |
| |
| Strategy: |
| 1. Lead with title and headline (high signal) |
| 2. Include career descriptions (most semantically rich) |
| 3. Add skills with proficiency (not just names) |
| 4. Note company types (product vs consulting) |
| 5. Include certifications and education field |
| """ |
| parts = [] |
|
|
| profile = candidate.get("profile", {}) |
| career = candidate.get("career_history", []) |
| skills = candidate.get("skills", []) |
| education = candidate.get("education", []) |
| certifications = candidate.get("certifications", []) |
|
|
| |
| title = profile.get("current_title", "") |
| headline = profile.get("headline", "") |
| yoe = profile.get("years_of_experience", 0) |
| location = profile.get("location", "") |
|
|
| parts.append(f"Title: {title}. Experience: {yoe:.1f} years. Location: {location}.") |
| if headline: |
| parts.append(f"Headline: {headline}.") |
|
|
| |
| summary = profile.get("summary", "") |
| if summary: |
| parts.append(f"Summary: {summary}") |
|
|
| |
| career_parts = [] |
| for job in career: |
| job_title = job.get("title", "") |
| company = job.get("company", "") |
| industry = job.get("industry", "") |
| duration = job.get("duration_months", 0) |
| description = job.get("description", "") |
| company_size = job.get("company_size", "") |
| is_current = job.get("is_current", False) |
|
|
| |
| is_consulting = any( |
| cf in company.lower() for cf in CONSULTING_COMPANIES |
| ) |
| company_type = "consulting company" if is_consulting else "product company" |
|
|
| job_text = ( |
| f"{'Current role' if is_current else 'Previous role'}: " |
| f"{job_title} at {company} ({company_type}, {industry}, " |
| f"{company_size} employees, {duration} months). {description}" |
| ) |
| career_parts.append(job_text) |
|
|
| if career_parts: |
| parts.append("Career history: " + " | ".join(career_parts)) |
|
|
| |
| skill_strs = [] |
| core_skills_found = [] |
|
|
| for skill in skills: |
| name = skill.get("name", "") |
| proficiency = skill.get("proficiency", "beginner") |
| duration = skill.get("duration_months", 0) |
| endorsements = skill.get("endorsements", 0) |
|
|
| |
| is_core = any( |
| kw in name.lower() for kw in CORE_AI_SKILLS |
| ) |
|
|
| |
| skill_desc = f"{name} ({proficiency}" |
| if duration > 0: |
| years = duration / 12 |
| skill_desc += f", {years:.1f} years" |
| if endorsements > 10: |
| skill_desc += f", {endorsements} endorsements" |
| skill_desc += ")" |
|
|
| if is_core: |
| core_skills_found.append(skill_desc) |
| else: |
| skill_strs.append(skill_desc) |
|
|
| |
| all_skills = core_skills_found + skill_strs |
| if all_skills: |
| if core_skills_found: |
| parts.append( |
| f"Key AI/ML skills: {', '.join(core_skills_found)}." |
| ) |
| if skill_strs: |
| parts.append(f"Other skills: {', '.join(skill_strs[:10])}.") |
|
|
| |
| for edu in education: |
| field = edu.get("field_of_study", "") |
| degree = edu.get("degree", "") |
| institution = edu.get("institution", "") |
| tier = edu.get("tier", "") |
| if field or degree: |
| parts.append( |
| f"Education: {degree} in {field} from {institution} ({tier})." |
| ) |
|
|
| |
| if certifications: |
| cert_names = [c.get("name", "") for c in certifications[:5]] |
| parts.append(f"Certifications: {', '.join(cert_names)}.") |
|
|
| return " ".join(parts) |
|
|
|
|
| def build_jd_query_text() -> str: |
| """ |
| Build the query text from the Job Description. |
| |
| This is what we embed to search against the candidate index. |
| Critical: this should describe the IDEAL candidate, not just the JD text. |
| We expand with synonyms and related concepts. |
| """ |
| return ( |
| "Senior AI Engineer with 5 to 9 years of experience at product companies. " |
| "Expert in embeddings-based retrieval systems using sentence-transformers, " |
| "FAISS, Pinecone, Weaviate, Qdrant, Milvus, OpenSearch, Elasticsearch. " |
| "Production experience with vector databases and hybrid search infrastructure. " |
| "Strong Python skills. Experience designing evaluation frameworks for ranking " |
| "systems using NDCG, MRR, MAP, A/B testing. " |
| "Shipped end-to-end search, ranking, or recommendation systems to real users. " |
| "Experience with LLM fine-tuning using LoRA, QLoRA, PEFT. " |
| "Learning-to-rank models with XGBoost or LightGBM. " |
| "Located in Pune, Noida, Hyderabad, Mumbai, Delhi NCR, Bangalore. " |
| "Startup or product company background. " |
| "Not from pure research or pure consulting background. " |
| "Has worked on information retrieval, semantic search, dense retrieval. " |
| "NLP and natural language processing background. " |
| "RAG retrieval augmented generation experience. " |
| "Hands-on coder who ships production ML systems." |
| ) |
|
|