Spaces:
Sleeping
Sleeping
Talentry AI - System Architecture
This document is the engineer's guide to Talentry AI. If you are reviewing the codebase at Stage 3 (reproduction) or Stage 5 (defend-your-work) of the Redrob × Hack2Skill - India Runs challenge, start here.
1. Goals and constraints
The challenge (submission_spec.md §3)
imposes a hard envelope on the ranking step:
| Constraint | Limit |
|---|---|
| Wall-clock runtime | ≤ 5 minutes |
| RAM | ≤ 16 GB |
| Compute | CPU only |
| Network | Off (no LLM API calls) |
| Disk (intermediate state) | ≤ 5 GB |
We additionally treat these product requirements as first-class:
- Explainability - every score must be defensible at Stage 4 manual review.
- Reproducibility - bit-for-bit reproducibility in a sandboxed Docker.
- Anti-trap robustness - the dataset deliberately contains keyword stuffers, plain-language Tier-5s, behavioural twins, and ~80 honeypots.
- No hallucination in reasoning - every reasoning string must reference only facts present in the candidate's own profile.
2. High-level architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ Talentry AI │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌────────────────────────────┐ │
│ │ JD text │───▶│ JD Parser │───▶│ JobRequirements │ │
│ └──────────┘ │ (rules+lex) │ │ • role family, seniority │ │
│ └──────────────┘ │ • must / nice / disqual. │ │
│ │ • locations, behaviour │ │
│ └──────────────┬─────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ candidates.jsonl │──▶│ Loader + models │──▶│ Feature builder │ │
│ └──────────────────┘ └──────────────────┘ │ (text_blob, │ │
│ │ role signals) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────┴───────┐ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌────────────────────┐│
│ │ BM25 + TF-IDF │ hybrid semantic score │ Skill Evidence ││
│ │ hybrid index │─────────────────┐ │ (cluster, stuffer ││
│ └─────────────────┘ │ │ detection) ││
│ ▼ └────────┬───────────┘│
│ ┌────────────────────────┐ │ │
│ │ Scorer (5 weighted) │◀───────┘ │
│ │ + Behavioural × mul. │ │
│ │ − Honeypot penalty │ │
│ └──────────┬─────────────┘ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Sort + Reasoning + CSV │ │
│ └──────────┬─────────────┘ │
│ ▼ │
│ submission.csv │
└──────────────────────────────────────────────────────────────────────────┘
Module → file mapping
| Module | File |
|---|---|
| Domain models | src/talentry/core/models.py |
| Tokeniser + synonyms | src/talentry/nlp/tokenize.py |
| Domain lexicons | src/talentry/nlp/lexicons.py |
| Candidate I/O | src/talentry/io/candidates.py |
| Submission CSV writer | src/talentry/io/submission.py |
| Per-candidate features | src/talentry/features/builder.py |
| Skill evidence scoring | src/talentry/features/skill_match.py |
| Behavioural multiplier | src/talentry/signals/behavioural.py |
| Honeypot penalty | src/talentry/signals/honeypot.py |
| Hybrid BM25+TF-IDF index | src/talentry/ranker/semantic.py |
| JD parser | src/talentry/ranker/jd_parser.py |
| Scoring formulae | src/talentry/ranker/scorer.py |
| Reasoning composer | src/talentry/ranker/reasoning.py |
| End-to-end pipeline | src/talentry/ranker/engine.py |
| CLI | src/talentry/cli/rank.py |
| HTTP API (FastAPI) | src/talentry/api/server.py |
| React UI | ui/talentry-space/ |
3. Why not dense embeddings?
Reviewers will rightly ask: "this is a retrieval challenge - why not a sentence-transformer?" Three reasons:
- Budget. Loading a 90 MB MiniLM and encoding 100K text blobs is right at the edge of the 5-minute CPU budget; once you add the per-row scoring, feature building, sort, and CSV write, you have no headroom for slow I/O on the Stage 3 sandbox.
- Dependency surface. A serialised PyTorch model artifact is one more
thing that can break reproduction; pure BM25 + TF-IDF reproduces from
pip install-able libraries alone. - Signal saturation. BM25 saturates on rare-term overlap and TF-IDF smooths over phrasing - and that is exactly what this dataset rewards. Adding a dense model marginally improves recall on prose-only profiles but adds noise on the keyword surface where stuffers live.
If we had two hours and a GPU per ranking call we would absolutely add a domain-fine-tuned reranker on the top 1000. We do not.
4. Composition formula
linear = 0.32·title_alignment
+ 0.22·semantic_fit
+ 0.28·skill_evidence
+ 0.12·experience_band
+ 0.06·location
final = linear × behavioural_multiplier ∈ [0.55, 1.20]
− honeypot_penalty ∈ [0, 0.50]
(clipped to [-0.5, 1.5])
Every constant is grounded in a specific line of the JD; see
methodology.md for the rationale.
5. Determinism
- Hot path uses no random state.
reference_datefor behavioural recency is overridable for tests.- Sort tie-break:
candidate_idascending - matches the validator. - CSV writer enforces the exact validator invariants at write-time so any drift fails loudly in the CLI, never silently at upload.
6. Threat model - the four traps
| Trap | Defence |
|---|---|
| Keyword stuffer | Skill evidence (endorsements × duration × proficiency) |
| Plain-language Tier 5 | BM25 over career-description text + role-family trajectory |
| Behavioural twin | Behavioural multiplier in [0.55, 1.20] |
| Honeypot | Honeypot penalty subtracts up to 0.50 |
7. Operational notes
- CLI:
python -m talentry.cli.rank ...(seemake submission). - API:
talentry-serve(ormake serve) →http://localhost:7860. - UI:
make ui-dev(http://localhost:5173with/apiproxied). - Container:
make docker-build && make docker-run- same image is pushed to the HuggingFace Space.