Build Your Own Resume Grader
Last week I launched a VC job board: around 10,000 live openings from Utah and national venture portfolios, refreshed twice a day, live at byu-aifoundry.com/jobs.
Here is the honest shape of it. AI built the scraper. AI built the site. But there is exactly one piece of AI actually running inside the product: the resume grader. Everything else is deterministic code an agent happened to write.
So I open-sourced the one part that matters. The full repo is jddavenportOpen/resume-grader (MIT). This page is how it works and how you build your own.
What you’ll build
Section titled “What you’ll build”A function that takes a resume and a job posting and returns a 0-100 fit score across six dimensions, plus specific, actionable feedback written in a hiring manager’s voice. One API call. About two cents. Cached to zero on repeat.
How it works
Section titled “How it works”The whole path is three steps:
- Ingest. Pull the job description from a posting’s public ATS API (Greenhouse, Lever, Ashby). Each is one branch; a new source is roughly 30 lines.
- Normalize. Every ATS returns different broken HTML. Decode it, strip inline styles and attributes, flatten to plain text.
- Grade. One call to Claude. The system prompt casts the model as the hiring manager for this role. The user prompt is the job block, the resume, and the rubric. Claude returns JSON; deterministic code applies fixed weights and clamps.
The prompt is the whole trick. The system message:
You are the hiring manager for this specific role. You have read hundreds ofresumes and you know exactly what you want. Be honest and direct — never inflatescores. A 70+ means you would likely call them. Below 50 means you would pass.Ground every judgment in evidence from the resume and the job posting. Do notinvent requirements the job posting does not state. Output ONLY valid JSON.Then you ask for six subscores, 3-6 specific gap suggestions, and a one-line verdict, and you demand a strict JSON shape back.
The rubric
Section titled “The rubric”| Dimension | Weight | Asks |
|---|---|---|
keyword | 25 | Do they have the hard skills and tools the role requires? |
experience | 20 | Is their domain and functional experience genuinely relevant? |
impact | 15 | Concrete results with numbers, not just responsibilities? |
structure | 15 | Clear and fast to evaluate? |
semantic | 13 | Does the overall trajectory fit this role and company? |
seniority | 12 | Does level, scope, and years match? |
Weights sum to 100. Retune them per role family. The composite is just the weighted sum, clamped 0-100.
Why it scales to any job
Section titled “Why it scales to any job”This is the part people miss. You do not write a grader per role. The rubric is universal, and the job posting is the only variable. The same grader reads a barista listing and a Principal PM role.
- ATS-agnostic ingestion. A new source is one adapter. The grader never changes.
- Graceful degradation. A posting under 120 characters (a link-out with no body) is graded conservatively on title, company, and level instead of failing.
- Stateless. No training, no fine-tuning, no labeled data.
Is it labor intensive? No.
Section titled “Is it labor intensive? No.”About 160 lines of grader plus 60 of ingestion. One API call per resume-and-job. No human in the loop. Cache by a hash of the resume and the job and repeat views cost nothing. A fresh grade is one Sonnet call, roughly one to two cents.
Try it
Section titled “Try it”git clone https://github.com/jddavenportOpen/resume-gradercd resume-graderexport ANTHROPIC_API_KEY=sk-ant-...
node examples/grade-cli.mjs --resume ./resume.txt \ --title "Forward Deployed Engineer" --company "Anthropic" \ --url https://job-boards.greenhouse.io/anthropic/jobs/5065835008The dynamic-RAG evolution
Section titled “The dynamic-RAG evolution”The grader above is Tier-3 of a three-tier design. Add the lower tiers when one prompt stops being enough: when the corpus is too big to inject, when you want calibration instead of an opinion, or when you want to cut cost and variance.
- Tier 1, deterministic (no LLM, ~free). Parse the required skills from the JD, hard-match them against the resume, compute a coverage percentage. Explainable, zero variance. Gate the obvious misses before you spend a token.
- Tier 2, embeddings (this is the RAG step). Embed the resume bullets and the JD’s requirement sentences. For each requirement, retrieve the nearest resume evidence. Now you have a per-requirement “is this backed, and where?” map. “Dynamic” because the retrieval query set is generated per job from that job’s requirements.
- Tier 3, the judge (this repo). Give Claude the resume, the JD, and the retrieved evidence map. It grades with evidence instead of reading cold. Same rubric, but the numbers stop being vibes.
- Live product: byu-aifoundry.com/jobs
- Code (MIT): github.com/jddavenportOpen/resume-grader