Back to Portfolio
Case N°00 · Featured Jul — Sep 2024 3D Smart Factory · PFA Internship

RucRut.
AI Recruitment reimagined.

An end-to-end platform that automates the hiring pipeline — CV parsing, semantic candidate-to-job matching, and LLM-generated interview questions. Built from NLP models to a full-stack RESTful app with analytics dashboard.

87%
Match Accuracy
60%
Screening Time Saved
400+
CVs Processed
3 mo
From Zero to Demo
01 / Problem

Hiring teams drown in CVs while
good candidates slip through keyword filters.

3D Smart Factory's HR pipeline relied on keyword-based ATS filtering — fast but blunt. Strong candidates were getting rejected because their resumes phrased experience differently than the job description. Meanwhile recruiters spent ~80% of their first-pass time on manual triage.

The goal: a system that understands semantic intent, not just term frequency. One that ranks candidates by genuine fit, summarizes their profile, and prepares the interviewer with tailored questions before the first call.

"Most ATS reject 70% of qualified candidates before a human ever sees the resume. We wanted to flip that ratio."
02 / Architecture

A 4-stage pipeline from raw PDF
to interview-ready brief.

01 · Ingest
CV Parsing
PDF/DOCX → structured JSON (skills, experience, education) via Transformer NER.
02 · Embed
Vector Encoding
Sentence-Transformers encode CV + job description into a shared 768-dim space.
03 · Match
Semantic Ranking
Cosine similarity + reranker scores each CV against the role. Top-K returned.
04 · Generate
LLM Brief
LLaMA generates per-candidate interview questions and a recruiter summary.
ML Stack
Hugging FaceSentence-TransformersLLaMAPyTorchSpaCy NER
Backend
FlaskJWT AuthREST APIMongoDB
Frontend
AngularTypeScriptChart.js
Ops
DockerGitPostman
03 / Key Decisions

Three calls that defined the result.

01
Why semantic embeddings over a fine-tuned classifier?

A classifier would have needed labeled CV/job pairs — we had none. Embeddings let us bootstrap from a frozen Sentence-Transformers model and tune the similarity threshold empirically. Six weeks of dev time saved, no labeled-data bottleneck.

Pragmatism over perfection
02
Why LLaMA for interview questions instead of GPT?

HR data couldn't leave the company VPC for compliance reasons. LLaMA running locally on a GPU instance gave us full data sovereignty with acceptable latency (~4s per generation). GPT-3.5 was marginally better but a non-starter on legal review.

Compliance shaped the model choice
03
Why ship an Angular dashboard and not just a JSON API?

Recruiters don't read JSON. The dashboard turned the matching score into a ranked, filterable, exportable list with one-click "summarize" and "send to interviewer" actions. The UI was what made adoption happen — the model was just an input.

UX is part of the model
04 / Code Excerpt

The matching loop, simplified.

# core/matching.py — the heart of the ranking pipeline
from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("sentence-transformers/all-mpnet-base-v2")

def rank_candidates(job_desc: str, cvs: list[dict]) -> list[dict]:
    # 1. embed the job description once
    job_vec = model.encode(job_desc, convert_to_tensor=True)

    # 2. embed each parsed CV (cached if seen before)
    cv_vecs = model.encode(
        [cv["summary"] for cv in cvs],
        convert_to_tensor=True,
        show_progress_bar=False,
    )

    # 3. cosine similarity gives a [0,1] fit score per candidate
    scores = util.cos_sim(job_vec, cv_vecs)[0]

    # 4. zip + sort, return top matches with diagnostic context
    return sorted(
        [{**cv, "score": float(s)} for cv, s in zip(cvs, scores)],
        key=lambda c: c["score"], reverse=True,
    )
05 / Outcomes & Lessons

What shipped, what worked, what we'd do differently.

RucRut went from a blank repo to a functioning internal tool screening 400+ CVs in three months. The 87% match accuracy benchmark was set by hand-labeling 100 known-good pairs.
Lesson 01
Embeddings ≠ understanding
Cosine similarity catches semantic overlap but misses negations and seniority signals. We added a lightweight rule layer on top — biggest single accuracy bump.
Lesson 02
Latency is a UX feature
LLM generation took 4s per CV. We batched in the background and surfaced results progressively in the UI — perceived speed went up without touching the model.
Lesson 03
Recruiters don't trust black boxes
The first version returned a score and nothing else. Adoption was zero. Once we added why each candidate ranked where they did, usage took off.

Like the way I work?

Always open to new AI & data engineering challenges. If you have a project that needs this kind of attention, let's talk.

Get in touch View on GitHub