Redrob-hackathon / precompute.py
Mohit0708's picture
Initial commit
7b833a7
Raw
History Blame Contribute Delete
20.8 kB
#!/usr/bin/env python3
"""
precompute.py — V7 Winning-Level Intelligent Recruitment Engine
Offline preprocessing. Extracts ~55 JD-driven features:
- V5's ~40 base features
- V6's 8 interaction/narrative features
- V6.1's 6 winning-differentiator features (tier5_signature, behavioral_twin,
langchain_only, closed_source_isolation, pre_llm_x_ownership, salary_compatibility)
- V7's 4 new features (assessment_signal, endorsement_signal, education_tier,
cross_validation_signal)
Builds canonical candidate profiles, fits TF-IDF+SVD embeddings with query
expansion, computes adaptive RRF retrieval scores, and writes a Parquet artifact.
V7 pipeline:
JD -> Hiring Intent -> Domain Builder -> Query Expansion
Candidates -> Canonical Profile -> Feature Registry -> Interaction Features
-> V6.1 Features -> V7 Features -> All features + embeddings -> Parquet
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from pathlib import Path
import numpy as np
import pandas as pd
sys.path.insert(0, str(Path(__file__).resolve().parent))
from lib import schema, features, honeypot, embeddings as emb_mod
from lib.jd_parser import get_jd
from lib.hiring_intent import get_intent
from lib.query_expansion import get_expanded_text
def load_candidates(path: str):
"""Load candidates from JSONL (one JSON object per line)."""
import gzip as _gzip
if path.endswith(".gz"):
f = _gzip.open(path, "rt", encoding="utf-8")
else:
f = open(path, "r", encoding="utf-8")
try:
for line in f:
line = line.strip()
if not line:
continue
if line.startswith("candidate_id,") or line.startswith("#"):
continue
try:
yield json.loads(line)
except json.JSONDecodeError:
continue
finally:
f.close()
def extract_features_v7(c: dict) -> dict:
"""Extract all V7 features for one candidate (~55 features)."""
text = schema.unified_text_blob(c)
jd = get_jd()
# === V5 base features (all ~40) ===
skill_cov, _ = features.skill_coverage(c, text)
pref_cov, _ = features.preferred_coverage(c, text)
domain_spec, _ = features.domain_specialization(c, text)
skill_trust, _ = features.skill_trust_avg(c)
title_rel, _ = features.title_relevance(c)
seniority, _ = features.seniority_feature(c)
jd_skill_cnt, _ = features.jd_skill_count(c, text)
own_hier, _ = features.ownership_hierarchy(c, text)
imp_mag, _ = features.impact_magnitude(c, text)
imp_sig, _ = features.impact_signals(c, text)
ev_str, _ = features.evidence_strength(c)
prod_str, prod_hits = features.production_strength(c, text)
prod_div, _ = features.production_diversity(c, text)
scale_ev, _ = features.scale_evidence(c, text)
yoe_band, _ = features.yoe_band_score(c, jd)
depth_ratio, _ = features.career_depth_ratio(c, jd)
pre_llm, _ = features.pre_llm_months(c, jd)
career_traj, _ = features.career_trajectory(c)
comp_qual = features.company_quality_feature(c)
comp_qual_avg, _ = features.company_quality_avg(c)
career_stab, _ = features.career_stability(c)
promo_vel, _ = features.promotion_velocity(c)
ret_depth, _ = features.retrieval_depth(c, text)
eval_exp, _ = features.evaluation_experience(c, text)
sys_design, _ = features.system_design_evidence(c, text)
recency_sc, recency_ev = features.recency(c)
resp_sc, resp_ev = features.responsiveness(c)
market_sc, _ = features.market_demand(c)
github_sc, _ = features.github_activity(c)
avail_sc, avail_ev = features.availability_score(c)
interview_sc, _ = features.interview_completion(c)
trust_sc, _ = features.platform_trust(c)
quant_out, _ = features.quantified_outcomes(c, text)
truth_sc, _ = features.truthiness(c, text)
kw_risk, _ = features.keyword_stuffing_risk(c)
prof_comp, _ = features.profile_completeness(c)
disq_pen, disq_reasons = features.disqualifier_penalty(c, text)
is_hp, hp_reasons = honeypot.is_honeypot(c)
loc_sc, _ = features.location_score(c)
# === V6 Career narrative ===
from lib.career_narrative import analyze as analyze_narrative
narrative = analyze_narrative(c)
# === V6 Interaction features ===
ownership_x_prod = own_hier * prod_str
skill_x_yoe = skill_cov * (0.5 + 0.5 * yoe_band)
impact_x_domain = imp_mag * (0.3 + 0.7 * depth_ratio)
trajectory_x_company = career_traj * comp_qual_avg
# === V6.1 NEW FEATURES ===
# Build a features dict for the V6.1 feature functions that depend on other features
base_features = {
"title_relevance": title_rel,
"company_quality": comp_qual,
"career_depth_ratio": depth_ratio,
"ownership_hierarchy": own_hier,
"production_strength": prod_str,
"skill_coverage": skill_cov,
"yoe_band_score": yoe_band,
"pre_llm_months": pre_llm,
}
tier5_sig, tier5_ev = features.tier5_signature(c, text, base_features)
beh_twin_pen, beh_twin_ev = features.behavioral_twin(c, base_features)
lc_pen, lc_ev = features.langchain_only_recent(c, text, base_features)
cs_pen, cs_ev = features.closed_source_isolation(c, text, base_features)
pllm_own, pllm_own_ev = features.pre_llm_x_ownership(c, base_features)
sal_compat, sal_ev = features.salary_compatibility(c)
# === V7 NEW FEATURES ===
# V7-1: Assessment signal — platform skill assessment scores
assessment_signal, assessment_ev = _v7_assessment_signal(c)
# V7-2: Endorsement signal — volume and quality of endorsements
endorsement_signal, endorsement_ev = _v7_endorsement_signal(c)
# V7-3: Education tier — IIT/NIT/IIIT boost
edu_tier, edu_ev = _v7_education_tier(c)
# V7-4: Cross-validation signal — multiple evidence types agreeing
cross_val, cross_val_ev = _v7_cross_validation(c, text, base_features)
return {
"candidate_id": c["candidate_id"],
# G1: JD Fit
"skill_coverage": skill_cov,
"preferred_coverage": pref_cov,
"domain_specialization": domain_spec,
"skill_trust_avg": skill_trust,
"title_relevance": title_rel,
"seniority": seniority,
"jd_skill_count": jd_skill_cnt,
# G2: Impact & Ownership
"ownership_hierarchy": own_hier,
"impact_magnitude": imp_mag,
"impact_signals": imp_sig,
"evidence_strength": ev_str,
# G3: Production & Scale
"production_strength": prod_str,
"production_diversity": prod_div,
"scale_evidence": scale_ev,
# G4: Experience & Career
"yoe_band_score": yoe_band,
"career_depth_ratio": depth_ratio,
"pre_llm_months": pre_llm,
"career_trajectory": career_traj,
"company_quality": comp_qual,
"company_quality_avg": comp_qual_avg,
"career_stability": career_stab,
"promotion_velocity": promo_vel,
# G5: Retrieval & Evaluation
"retrieval_depth": ret_depth,
"evaluation_experience": eval_exp,
"system_design_evidence": sys_design,
# G6: Behavioural
"recency": recency_sc,
"responsiveness": resp_sc,
"market_demand": market_sc,
"github_activity": github_sc,
"availability_score": avail_sc,
"interview_completion": interview_sc,
"platform_trust": trust_sc,
# G7: Resume Quality
"quantified_outcomes": quant_out,
"truthiness": truth_sc,
"keyword_stuffing_risk": kw_risk,
"profile_completeness": prof_comp,
# G8: Safety
"disqualifier_penalty": disq_pen,
"is_honeypot": is_hp,
# G9: Location
"location_score": loc_sc,
# G10: Career Narrative (V6)
"career_coherence": narrative.coherence,
"narrative_type": narrative.trajectory_type,
"narrative_suspicious": json.dumps(narrative.suspicious_patterns),
# G11: Interaction Features (V6)
"ownership_x_production": ownership_x_prod,
"skill_x_yoe": skill_x_yoe,
"impact_x_domain": impact_x_domain,
"trajectory_x_company": trajectory_x_company,
# G12: V6.1 Winning Features
"tier5_signature": tier5_sig,
"behavioral_twin_penalty": beh_twin_pen,
"langchain_only_penalty": lc_pen,
"closed_source_penalty": cs_pen,
"pre_llm_x_ownership": pllm_own,
"salary_compatibility": sal_compat,
# G13: V7 New Features
"assessment_signal": assessment_signal,
"endorsement_signal": endorsement_signal,
"education_tier": edu_tier,
"cross_validation": cross_val,
# Metadata for reasoning
"current_title": schema.current_title(c),
"current_company": schema.current_company(c),
"years_of_experience": schema.years_of_experience(c),
# Evidence JSON for reasoning
"_candidate_json": json.dumps(c, default=str),
# Disq reasons for reasoning
"_disq_reasons": json.dumps(disq_reasons),
# Behavioural evidence for reasoning
"_behaviour_evidence": json.dumps({
**recency_ev, **resp_ev, **avail_ev,
"notice_period_days": avail_ev.get("notice_period_days", 45),
"days_since_active": recency_ev.get("days_since_active", 90),
"recruiter_response_rate": resp_ev.get("recruiter_response_rate", 0.3),
}),
# V6.1 evidence columns for reasoning
"_beh_twin_evidence": json.dumps(beh_twin_ev),
"_langchain_evidence": json.dumps(lc_ev),
"_closed_source_evidence": json.dumps(cs_ev),
"_salary_evidence": json.dumps(sal_ev),
"_tier5_evidence": json.dumps(tier5_ev),
"_assessment_evidence": json.dumps(assessment_ev),
"_endorsement_evidence": json.dumps(endorsement_ev),
"_education_evidence": json.dumps(edu_ev),
}
# ===========================================================================
# V7 NEW FEATURE FUNCTIONS
# ===========================================================================
def _v7_assessment_signal(c: dict) -> tuple[float, dict]:
"""V7-1: Platform skill assessment scores — validated skill proficiency.
The platform has skill_assessment_scores which are actual test results,
not self-reported. This is a much stronger signal than listed skills.
"""
s = schema.signals(c)
assessments = s.get("skill_assessment_scores") or {}
if not assessments:
return 0.50, {"reason": "no_assessments", "count": 0}
# Only count ML-relevant assessments
relevant_keys = {"python", "machine learning", "nlp", "natural language",
"deep learning", "statistics", "pytorch", "tensorflow",
"retrieval", "ranking", "recommendation", "data science",
"computer vision", "reinforcement learning"}
relevant_scores = []
for k, v in assessments.items():
if k.lower() in relevant_keys:
relevant_scores.append(float(v) if isinstance(v, (int, float)) else 0)
if not relevant_scores:
return 0.50, {"reason": "no_relevant_assessments", "count": len(assessments)}
avg_score = sum(relevant_scores) / len(relevant_scores)
# Normalize: >70 is strong, >50 is decent, <30 is concerning
if avg_score >= 70:
signal = 0.80 + 0.20 * min(1, (avg_score - 70) / 30)
elif avg_score >= 50:
signal = 0.60 + 0.20 * (avg_score - 50) / 20
elif avg_score >= 30:
signal = 0.40 + 0.20 * (avg_score - 30) / 20
else:
signal = 0.20 + 0.20 * avg_score / 30
# Bonus for taking multiple assessments (shows engagement)
count_bonus = min(0.10, len(relevant_scores) * 0.025)
signal = min(1.0, signal + count_bonus)
return signal, {"avg_score": avg_score, "count": len(relevant_scores),
"reason": "assessed"}
def _v7_endorsement_signal(c: dict) -> tuple[float, dict]:
"""V7-2: Endorsement volume and quality — peer-validated skills.
High endorsement counts on relevant skills indicate genuine expertise,
not just keyword stuffing. The JD values 'proven' skills.
"""
skills = schema.skills(c)
if not skills:
return 0.30, {"reason": "no_skills", "total_endorsements": 0}
# Get domain taxonomy for relevant skills
try:
taxonomy = __import__("lib.domain", fromlist=["get_taxonomy"]).get_taxonomy()
relevant_skill_names = set(taxonomy.skill_tier.keys())
except Exception:
relevant_skill_names = {
"python", "machine learning", "nlp", "deep learning", "pytorch",
"tensorflow", "elasticsearch", "faiss", "pinecone", "rag",
"embeddings", "retrieval", "ranking", "recommendation",
"ann", "hnsw", "vector database", "semantic search",
"bm25", "learning to rank", "numpy", "pandas", "scikit-learn",
}
total_endorsements = 0
relevant_endorsements = 0
relevant_count = 0
max_endorsement = 0
for sk in skills:
name = (sk.get("name") or "").lower()
endorsements = int(sk.get("endorsements") or 0)
proficiency = (sk.get("proficiency") or "").lower()
total_endorsements += endorsements
if name in relevant_skill_names:
relevant_endorsements += endorsements
relevant_count += 1
max_endorsement = max(max_endorsement, endorsements)
elif any(partial in name for partial in
["embed", "retriev", "rank", "search", "vector", "nlp",
"ml ", "machine learn", "deep learn", "pytorch", "tensor",
"faiss", "pinecone", "ann", "rag", "bm25", "elasticsearch"]):
relevant_endorsements += endorsements
relevant_count += 1
max_endorsement = max(max_endorsement, endorsements)
if relevant_count == 0:
# No relevant skills endorsed at all
return 0.30, {"reason": "no_relevant_skills", "total_endorsements": total_endorsements}
avg_rel = relevant_endorsements / relevant_count
# Signal based on endorsement volume
if avg_rel >= 30 and relevant_count >= 3:
signal = 0.85 + 0.15 * min(1, (avg_rel - 30) / 50)
elif avg_rel >= 15 and relevant_count >= 2:
signal = 0.65 + 0.20 * (avg_rel - 15) / 15
elif avg_rel >= 5:
signal = 0.45 + 0.20 * (avg_rel - 5) / 10
else:
signal = 0.25 + 0.20 * avg_rel / 5
return min(1.0, signal), {"avg_relevant": avg_rel, "relevant_count": relevant_count,
"max_endorsement": max_endorsement,
"total_endorsements": total_endorsements}
def _v7_education_tier(c: dict) -> tuple[float, dict]:
"""V7-3: Education tier — IIT/NIT/IIIT boost for Indian candidates.
Stage 4 reviewers may consider educational pedigree. This is a mild
signal (not a primary ranking factor) that breaks ties.
"""
education = schema.education(c)
if not education:
return 0.50, {"reason": "no_education"}
# Get highest degree
best_tier = 3 # default tier_3
best_degree = None
best_institution = None
tier_map = {"tier_1": 1, "tier_2": 2, "tier_3": 3, "tier_4": 4}
for edu in education:
tier_str = (edu.get("tier") or "tier_3").lower()
tier_num = tier_map.get(tier_str, 3)
if tier_num < best_tier:
best_tier = tier_num
best_degree = edu.get("degree", "")
best_institution = edu.get("institution", "")
# Tier-1 (IITs, top institutions): 0.80
# Tier-2 (NITs, IIITs, good colleges): 0.70
# Tier-3 (other): 0.50
# Tier-4 or unknown: 0.40
tier_scores = {1: 0.80, 2: 0.70, 3: 0.50, 4: 0.40}
score = tier_scores.get(best_tier, 0.50)
# Bonus for CS/AI/ML field of study
best_field = ""
for edu in education:
field = (edu.get("field_of_study") or "").lower()
if any(kw in field for kw in ["computer science", "artificial intelligence",
"machine learning", "data science", "information technology"]):
best_field = edu.get("field_of_study", "")
score = min(1.0, score + 0.10)
break
return score, {"tier": best_tier, "degree": best_degree,
"institution": best_institution, "field": best_field}
def _v7_cross_validation(c: dict, text: str, base_features: dict) -> tuple[float, dict]:
"""V7-4: Cross-validation — when multiple evidence types agree on quality.
A candidate who has high impact AND high ownership AND high production
AND high retrieval depth is much more likely to be genuinely strong
than someone who scores high on just one dimension.
"""
imp = base_features.get("impact_magnitude", 0)
own = base_features.get("ownership_hierarchy", 0)
prod = base_features.get("production_strength", 0)
depth = base_features.get("career_depth_ratio", 0)
eval_exp = base_features.get("evaluation_experience", 0) if "evaluation_experience" in base_features else 0
# Count how many dimensions are "strong" (>= 0.50)
strong_dims = sum(1 for v in [imp, own, prod, depth, eval_exp] if v >= 0.50)
# Score based on cross-validation
if strong_dims >= 5:
signal = 0.95
elif strong_dims >= 4:
signal = 0.80
elif strong_dims >= 3:
signal = 0.60
elif strong_dims >= 2:
signal = 0.40
else:
signal = 0.20
# Also check for "no weak dimensions" bonus
no_weak = all(v >= 0.20 for v in [imp, own, prod, depth])
if no_weak and strong_dims >= 3:
signal = min(1.0, signal + 0.10)
return signal, {"strong_dimensions": strong_dims,
"dimensions": {"impact": imp, "ownership": own,
"production": prod, "depth": depth,
"evaluation": eval_exp}}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--candidates", required=True)
ap.add_argument("--out", default="./artifacts/features.parquet")
args = ap.parse_args()
t0 = time.time()
candidates = list(load_candidates(args.candidates))
print(f"[precompute] loaded {len(candidates)} candidates in {time.time()-t0:.1f}s")
# Print hiring intent
intent = get_intent()
print(f"[precompute] Hiring Intent: philosophy={intent.philosophy}, "
f"need={intent.primary_need}, ownership={intent.ownership_expectation:.2f}, "
f"culture={intent.shipping_culture}, team={intent.team_context}")
# Feature extraction
rows = []
texts = []
t1 = time.time()
for i, c in enumerate(candidates):
row = extract_features_v7(c)
rows.append(row)
texts.append(schema.unified_text_blob(c))
if (i + 1) % 20000 == 0:
print(f"[precompute] processed {i+1}/{len(candidates)} candidates "
f"({time.time()-t1:.1f}s)")
print(f"[precompute] extracted V7 features for {len(rows)} candidates in {time.time()-t1:.1f}s")
# TF-IDF + SVD embeddings (with query expansion)
t2 = time.time()
jd = get_jd()
expanded_ideal = get_expanded_text(jd.ideal_text)
all_texts = texts + [expanded_ideal]
embedder = emb_mod.TfidfSvdEmbedder(n_components=min(100, len(all_texts) - 1))
embedder.fit(all_texts)
doc_emb = embedder.transform(all_texts)[:len(texts)]
sims = embedder.similarity_to_query(doc_emb, expanded_ideal)
for row, sim in zip(rows, sims):
row["embedding_sim"] = float(sim)
print(f"[precompute] TF-IDF+SVD embeddings (expanded query) in {time.time()-t2:.1f}s")
df = pd.DataFrame(rows)
out_path = Path(args.out)
out_path.parent.mkdir(parents=True, exist_ok=True)
df.to_parquet(out_path, index=False)
print(f"[precompute] wrote {len(df)} rows -> {out_path} ({out_path.stat().st_size/1e6:.1f} MB)")
print(f"[precompute] total time: {time.time()-t0:.1f}s")
print(f"[precompute] honeypots flagged: {int(df['is_honeypot'].sum())} / {len(df)}")
print(f"[precompute] V7 features: {len(df.columns)} columns")
# Feature statistics
for col in ["tier5_signature", "behavioral_twin_penalty", "langchain_only_penalty",
"closed_source_penalty", "pre_llm_x_ownership", "salary_compatibility",
"assessment_signal", "endorsement_signal", "education_tier", "cross_validation"]:
if col in df.columns:
vals = df[col].dropna()
print(f" {col}: mean={vals.mean():.3f} "
f"p50={vals.median():.3f} p90={vals.quantile(0.90):.3f}")
if __name__ == "__main__":
main()