◇ AI Resume Screener Python · Claude · embeddings · Postgres · Streamlit Project Blueprint
Hiring

A shortlist you can defend line by line.

Every candidate is scored three ways against the same job description — skills overlap, experience fit, and soft fit — combined into one number whose weights you set. Each part keeps the evidence behind it, so "why is this person third?" has an actual answer rather than a shrug.

Below, the scoring run, the three scores, and how it's built.
01

How a Candidate Is Scored

Both sides are parsed into structure first, then compared three separate ways. Two of the three comparisons are deterministic; only the third involves judgement, and it works on a redacted copy.

Job description pasted in 📋 Requirements 📄 Resume PDF, in a batch Candidate profile Skills match embeddings · 40% Experience fit rules only · 30% Soft fit on stripped text · 30% weights you set Composite at or above threshold Shortlisted Ranked below still scored, still explained Evidence kept every sub-score, with the reason behind it
you provide runs automatically your shortlist to review

two things in

job description → structured requirements
resume → structured profile

scored three ways

🧩
Skills match · 40%
Embedding similarity, with a bonus for exact taxonomy hits.
📅
Experience fit · 30%
Plain rules over years, seniority, and trajectory.
🛡
Soft fit · 30%
One judgement call, made on PII-stripped text.
Composite
Three sub-scores, weighted into one number.
at or above threshold → shortlisted
below it → ranked, still explained
every sub-score keeps its evidence — exportable per candidate
02

Inside Each Step

📋

01 Read the job description

Paste the posting and it comes back as structure: required skills, nice-to-haves, minimum and preferred years, seniority, responsibilities, and the expectations that were implied rather than listed. That last category matters — "startup pace" and "comfortable with ambiguity" are real requirements that never appear in a checklist.

Claude API · structured job description
📄

02 Read the resumes

Upload a batch and each one is parsed into the same shape: canonical skills, work history with years attached role by role, education, total experience, highest seniority reached, and a one-line read on the trajectory. Skill variants are normalised against an editable taxonomy, so one candidate writing "Postgres" and another writing "PostgreSQL" stop being different people.

PyMuPDF · Claude API · editable skills taxonomy
🛡

03 Strip the identity

Before the judgement-based scorer reads anything, a deterministic pass removes name, email, phone, profile links, postal address, pronouns, and age signals. Deterministic matters here: this is regex, not a model deciding what counts as identifying. The number of redactions made is shown per candidate, so the step is visibly auditable rather than merely claimed.

regex redaction · per-candidate redaction counts
🧩

04 Three scorers, three methods

Skills are compared by embedding similarity, which catches equivalent skills written differently, with a bonus when a canonical taxonomy term matches exactly. Experience is pure Python: distance from the target years with a linear falloff, seniority gap, and a lift for an upward trajectory. Only soft fit involves a model, assessing achievement alignment and the implicit expectations — on the redacted copy.

embeddings · rules engine · one model call per candidate

05 Combine and cut

The three sub-scores are weighted into a single value — forty, thirty, thirty by default, adjustable per job when a role genuinely hinges on one dimension. Candidates at or above a configurable threshold are flagged as shortlisted. Everyone below it is still scored and still explained, because the second-tier candidate you dismissed is the one you'll want to revisit when the first hire falls through.

configurable weights and threshold, per job
📊

06 Show the working

Every component's evidence and sub-metrics are preserved rather than collapsed into the final number. The dashboard shows the ranked table and a per-candidate breakdown, the shortlist exports as a spreadsheet, and each candidate has a downloadable report carrying the composite score, the per-component blocks with their evidence, the redaction summary, and the career snapshot.

dashboard · CSV export · per-candidate PDF report
What the redaction does and doesn't do

The model never sees who the candidate is.

Name, contact details, links, address, pronouns, and age signals are gone before the fit reasoner reads a word, and the redaction count is surfaced as evidence the pass ran. This closes the most direct route for irrelevant signal to reach a score — it does not make screening neutral, and it isn't claimed to. Two of the three scores are deterministic and every score shows its evidence precisely so a person can audit the ranking rather than trust it. The system ranks; it doesn't decide.

03

The Three Scores

Three different questions, answered three different ways, because they aren't the same kind of question.

SKILLS · 40%
Does the toolkit overlap?

Embedding similarity between what the role requires and what the candidate lists, so related skills still register, plus a bonus when a canonical term matches exactly and a small lift for nice-to-have coverage.

EXPERIENCE · 30%
Is the depth right?

Plain Python over the numbers: distance from the target years with a linear falloff, the gap to the required seniority, and credit for a career that's been moving upward. No model, no variance, same input to same output every run.

SOFT FIT · 30%
Would this actually work?

The judgement call — do the achievements line up with the responsibilities, are the implicit expectations met, are there signals pointing the other way. One model call per candidate, reading a copy with the identity removed.

WEIGHTS
Yours to move

Forty/thirty/thirty is a starting point, not a claim. A role that genuinely lives or dies on one specific skill should be weighted to say so, and the weights are set per job.

THRESHOLD
Where the shortlist starts

A configurable cut-off decides which candidates get flagged. Everyone else stays ranked and explained in the same table — the line moves without rerunning anything.

04

Architecture

Parsing on both sides, three independent scorers in the middle, and a ranking layer that keeps everything it was given.

Parsing and redaction

PyMuPDF · Claude API · regex redaction · skills taxonomy

Turns a posting and a stack of PDFs into two comparable structures, normalising skill names against a taxonomy that ships as an editable file — a client swaps in their own without a code change. Identity is stripped on the copy bound for the model.

Similarity

Skills scorer

local embeddings · hosted opt-in

Cosine similarity between required and held skills, with exact-match bonuses. Runs locally and free by default; a hosted provider can be switched on.

Deterministic

Experience scorer

pure Python rules

Years, seniority distance, and trajectory. No model involved, which is what makes this component reproducible and arguable.

Judgement

Fit reasoner

one Claude call · redacted input

Assesses alignment and implicit expectations. Repeat documents are hashed and skipped, so re-running a batch doesn't re-pay for the same call.

Ranking and output

weighted composite · Postgres · Streamlit · CSV · PDF

Applies the weights, flags against the threshold, and preserves every component's evidence for the dashboard, the exported shortlist, and the per-candidate report.

05

How It's Built

Six modular blocks. The taxonomy and the weights change per client; the three-way split and the redaction pass don't.

01

JD parser

the ask

Turns a posting into requirements, stated and implied.

02

Resume parser

the offer

Turns a PDF into skills, years, and trajectory.

03

Redactor

identity out

Strips identifying detail before any judgement is made.

04

Scorers

three methods

Similarity, rules, and one judgement call.

05

Ranker

weights + cut

Combines the three, flags against the threshold.

06

Reporter

the working

Dashboard, export, and a report per candidate.

06

Stack

Python FastAPI Streamlit PyMuPDF Claude API sentence-transformers PostgreSQL reportlab