India Runs Hackathon · Redrob AI Challenge 2025

SignalHire

100,000 candidate profilestop 100 best matches in under 3 seconds. Five orthogonal signals. Zero API calls. Full explainability.

100K
Candidates Ranked
~3s
Ranking Time
5
Scoring Signals
35
Adversarials Caught

Everything a modern
recruiter needs

Eight production-grade features — from adversarial detection to blind screening — in one CPU-native pipeline.

🚀

GPU-Accelerated Precompute

Embed 100K profiles with all-MiniLM-L6-v2 in ~4 min on GPU. Artifacts cached — ranking is instant on every subsequent run.

Phase A

Live Re-Ranking

Drag a weight slider → entire 100K pool re-scored in ~50 ms. One float32 matrix multiply. No pipeline re-run needed.

~50ms
🧠

Evidence-Cited Reasoning

Every score claim traces to a specific declared skill or career sentence. The reasoning string never invents evidence.

Explainable AI
🛡️

Adversarial Detection

Catches honeypots (impossible YoE), ghost profiles (unverified, empty), and pure-research candidates before scoring begins.

35 caught
📊

Fairness Audit

Shortlist vs full-pool distributions by education tier, country, and years of experience — detect encoded bias at a glance.

Bias detection
🎛️

Diversity Control (MMR)

Maximal Marginal Relevance slider penalizes near-duplicate profiles so the shortlist surfaces distinct candidate archetypes.

MMR
🕶️

Blind Screening Mode

Toggle to hide names, companies, and institutions. Reduce reviewer bias — evaluate candidates on skills and signals only.

DEI
📦

One-Command Export

Submission CSV + personalized outreach pack (top-10 draft messages) + reproducible ranking config JSON — all from the dashboard.

Export

Two-phase
offline pipeline

Expensive GPU work separated from the CPU-only ranking sandbox. Artifacts baked into Docker — zero network at inference time.

PHASE A Precompute — offline, before submission GPU ~4 min · CPU ~30 min
01

Ingest JSONL

Stream 100K candidates from 487 MB candidates.jsonl

02

Disqualify

Hard-remove honeypots, ghosts, pure-research profiles

03

Build Profile Blob

Headline + summary + career descriptions + top skills → 1024 char

04

Batch Embed

all-MiniLM-L6-v2 · batch=2048 · 384-dim normalized vectors

05

Compute Sub-Scores

Technical fit, career quality, availability, seniority per candidate

06

Serialize

embeddings.npy · subscores.pkl · disqualified.json · candidate_ids.npy

embeddings.npy (~147 MB) candidate_ids.npy jd_embedding.npy subscores.pkl (~7 MB) disqualified.json
PHASE B Ranking — CPU-only sandbox ~3 s · no network · 16 GB RAM
01

Load Artifacts

Memory-map numpy arrays — no full file parse

02

Cosine Similarity

embeddings @ jd_embedding — single dot product broadcast

03

Composite Score

penalty × (subscore_matrix @ weights + w_sem × sim)

04

Top-100 Select

np.argsort → top-k with deterministic tie-breaking

05

Evidence Reasoning

Byte-offset seek index → load 100 records, cite actual skills

06

Validate & Write

validate_submission.py → submission.csv

DASHBOARD Streamlit — interactive re-ranking workbench ~50 ms re-rank · 6 tabs
UI

Weight Sliders

Drag any weight → single matrix multiply → instant re-rank

UI

Custom JD

Paste any job description → embed on the fly → re-rank 100K

UI

MMR Diversity

λ slider → penalize near-duplicate profiles in shortlist

UI

Stability Badge

200 Monte Carlo trials → ±20% weight perturbation frequency

Five orthogonal
scoring signals

No single signal can game the system. Keyword stuffers score high on technical fit but low on career quality and availability.

S = penalty × (
  0.35 × technical_fit
+ 0.25 × career_quality
+ 0.20 × availability
+ 0.12 × seniority_fit
+ 0.08 × semantic_sim
)

All sub-scores ∈ [0, 1]
Penalty ∈ [0.12, 1.0]

Technical Fit35%

Embeddings/retrieval · vector DBs · Python · eval frameworks · LLM fine-tuning

Career Quality25%

Non-consulting history · ML title at real companies · tenure · title progression

Availability Signal20%

Open to work · recency · response rate · notice period · interview rate

Seniority Fit12%

YoE 6–9 ideal · education tier bonus (IIT/IISc/NIT/BITS)

Semantic Similarity8%

MiniLM-L6-v2 cosine similarity — catches plain-language strong engineers

SignalWeightWhat It Checks
🔧 Technical Fit 0.35 JD must-haves scored by proficiency (expert=1.0, advanced=0.75, intermediate=0.4, beginner=0.1). Platform assessment scores override self-reported proficiency.
🏢 Career Quality 0.25 Non-consulting role presence (+0.30), ML/AI title at ≥50-person company (+0.15), median tenure ≥36mo (+0.25), upward title progression (+0.20).
📡 Availability 0.20 Open-to-work flag, last active ≤30d/90d/180d, recruiter response rate, interview completion rate, notice period, applications submitted.
📏 Seniority Fit 0.12 YoE bands: 6–9yr → 1.0, 4–5/10–12yr → 0.7, 3/13–15yr → 0.4, else → 0.1. Tier-1 education +0.05.
🔎 Semantic Match 0.08 Cosine similarity between 384-dim profile embedding and JD embedding. Safety net for strong candidates who describe skills in plain language.

Built to defeat
adversarial profiles

The dataset seeds ~80 fake profiles to catch naive rankers. SignalHire's disqualification layer hard-removes them before scoring begins.

🍯

Honeypot Detection

Claimed years of experience exceeds what the career timeline allows. A candidate claiming 20yr experience with earliest role starting in 2018 is impossible.

yoe > (CURRENT_YEAR - earliest_start)
+ HONEYPOT_YEAR_BUFFER (5)
→ HARD DISQUALIFIED
👻

Ghost Profile Detection

Near-empty profiles with no verified contact. Profile completeness below 5% with neither verified email nor verified phone — they're not real candidates.

completeness < 5%
AND not verified_email
AND not verified_phone
→ HARD DISQUALIFIED
🔬

Pure Research Filter

All career roles titled "researcher" with zero deployment or production evidence in descriptions. Academic-only profiles don't fit a Senior AI Engineer role.

ALL roles: researcher title
AND zero production signals
(deployed/shipped/serving...)
→ HARD DISQUALIFIED

Score multipliers for
borderline profiles

×0.15

All-Consulting Career

Every role at TCS, Infosys, Wipro, Capgemini, Cognizant, Accenture, HCL etc. These profiles rarely have the deep technical work required.

85% penalty
×0.80

No Code in 18 Months

Last active >548 days ago with no current role. Skills may be significantly stale in a fast-moving AI/ML field.

20% penalty
×0.85

CV / Speech / Robotics Only

Has computer vision, speech recognition, or robotics domain skills but zero retrieval/search signals. Domain misalignment with this JD.

15% penalty

Six tabs.
One workbench.

An interactive ranking environment — not just a results page. Every control re-ranks the full pool in real time.

🏆 Shortlist — Paginated candidate cards

  • Stability badge — frequency in top-100 across 200 Monte Carlo trials (±20% weight jitter). Stable ≥90% / Moderate ≥60% / Fragile <60%
  • Score bars — visual breakdown by signal (technical fit, career quality, availability, seniority, semantic match)
  • Evidence chips — every matched JD requirement tagged with the exact skill name or career sentence that triggered it
  • Penalty badges — flags consulting penalty, no-code penalty, or CV-only penalty where applied
  • One-liner reasoning — evidence-cited summary for each candidate, never fabricated
  • Load more — paginated at 10, expand as needed to see the full top-100

⚖️ Compare — Side-by-side radar chart

  • Select 2–4 candidates from the shortlist to compare head-to-head
  • Radar chart across all 5 score dimensions — visualize strengths and gaps at a glance
  • Per-candidate reasoning summaries shown side-by-side
  • Missing must-haves highlighted for each candidate

📊 Insights — Score landscape + fairness audit

  • Score histogram — distribution of top-5000 composite scores with top-100 cutoff line
  • Education tier — shortlist vs 100K pool distribution (Tier 1/2/3/unknown)
  • Country distribution — shortlist vs pool for top-8 countries
  • Years of experience — histogram overlay to detect seniority bias

🛡️ Integrity — Adversarial audit log

  • Counts by category: total disqualified, honeypots, ghosts, pure research
  • Concrete examples — actual honeypot candidates with their claimed YoE vs career timeline evidence
  • Full disqualification log table (expandable) with reason strings

📤 Export — Three export formats

  • Submission CSV — challenge-format: candidate_id, rank, score (x/10), evidence reasoning (≤300 chars)
  • Outreach pack — top-10 personalized first-touch draft messages citing each candidate's actual skills and notice period
  • Ranking config JSON — current weights, JD label, and shortlist IDs as a reproducible snapshot

📖 Methodology — Live formula display

  • Composite scoring formula rendered dynamically from current weight values
  • Signal explanations with what each sub-score measures
  • Integrity rules documentation with exact thresholds

Clean dependency
layer graph

Zero circular dependencies. Every module has a single responsibility. Leaf modules have no project imports.

Layer 0 — Leaf modules (no project imports) textmatch.py ── re, functools # word-boundary keyword matching, lru_cache config.py ── os, re, pathlib # all constants, weights, keyword lists Layer 1 disqualify.py ──► config, textmatch # honeypot / ghost / pure-research detection Layer 2 signals.py ──► config, disqualify, textmatch # 4 sub-score functions Layer 3 evidence.py ──► config, signals, textmatch # JD requirement tracing + reasoning engine.py ──► config # vectorized scoring, MMR, stability Layer 4 precompute.py──► config, disqualify, signals + sentence_transformers Layer 5 rank.py ──► config, engine, evidence Layer 6 app.py ──► config, engine, evidence, rank + streamlit, plotly
output/submission.csv — validated output format
candidate_id,rank,score,reasoning
CAND_0081846,1,8.70,"6.7yr Lead AI Engineer at Razorpay; strong match on embeddings, vector search,
python, information retrieval; production evidence (serving, ndcg);
actively looking, 73% response rate, 30d notice."
CAND_0055905,2,8.69,"8.1yr Senior ML Engineer at Flipkart; strong match on embeddings, vector search,
python, information retrieval; production evidence (deployed, serving);
actively looking, 87% response rate."

Up and running
in four commands

01

Install

# Create venv & install deps python -m venv venv source venv/bin/activate pip install -r requirements.txt
02

Precompute (once)

# CPU default; GPU ~10x faster python precompute.py # or with GPU: EMBEDDING_DEVICE=cuda python precompute.py
03

Rank

# Generates output/submission.csv python rank.py # Output: validated, 100 rows, ~3s
04

Dashboard

# Interactive workbench streamlit run app.py # Opens: http://localhost:8501 # Or: huggingface.co/spaces/DevanshSrajput/SignalHire

🐳 Docker (submission-ready)

# Artifacts baked in — only candidates.jsonl needs mounting docker build -t signalhire . docker run --rm \ -v $(pwd)/data:/app/data \ -v $(pwd)/output:/app/output \ signalhire

Measured on
RTX 3050 + 12-core CPU

4.2m
Precompute (GPU)
100K → 99,965 candidates · ~400 cand/s · all-MiniLM-L6-v2
~3s
Ranking + Validation
CPU-only · full composite scores · validate_submission passes
50ms
Dashboard Re-rank
Single float32 matrix multiply · 2M candidates/s throughput
162MB
Artifact Footprint
147MB embeddings · 7MB subscores · well within 16GB RAM limit

Built by
this team

Contributors who made SignalHire happen. Every commit, review, and idea counted.

Devansh Singh Rajput
Devansh Singh Rajput
Lead Author · 52 commits
Research Intern at IIT (ISM) Dhanbad · AI/ML Developer · Open Source Contributor · Building efficient AI systems and real-world ML applications
Aditya
Aditya
Contributor
i-used-arch-for-1098-days-btw!
1 contribution
Rachit Mittal
Rachit Mittal
Contributor
GitHub contributor to SignalHire
1 contribution
Vaishnavi Singh
Vaishnavi Singh
Contributor
Just a dehydrated penguin 🐧
1 contribution