Resume screening is two bad options stacked together: humans skimming PDFs in ten seconds, or black-box ATS keyword bingo. The open-source Hiring Agent project (also mirrored under interviewstreet/hiring-agent) takes a third path: structured extraction, GitHub enrichment, and explainable scoring you can run locally with Ollama or vMLX, or swap in Google Gemini.
I do not think AI should auto-reject humans. I do think engineers deserve a pipeline that reads their PDF and their commits before anyone asks them to whiteboard FizzBuzz again.
End-to-end pipeline
The orchestrator is score.py. It wires five stages:
- PDF to Markdown (
pymupdf_rag.py) with headings, links, and tables preserved enough for LLM parsing. - Sectioned JSON extraction (
pdf.py+ Jinja templates inprompts/templates/) into aJSONResumeobject (models.py). - GitHub enrichment (
github.py): profile fetch, repo classification, LLM picks seven meaningful projects with a minimum author-commit threshold. - Role-aware evaluation (
evaluator.py) with fairness constraints baked into system prompts. - Stdout summary + optional CSV when
DEVELOPMENT_MODE=Truecaches intermediates undercache/.

That is a clean separation of concerns. Swapping models does not require rewriting PDF parsing. Swapping rubrics means editing role YAML, not the GitHub client.
GitHub enrichment is the differentiator
Plenty of resume parsers stop at skills arrays. Hiring Agent tries to answer a harder question: what did this person actually ship?
github.py:
- Extracts a username from resume profiles
- Pulls profile metadata and repositories
- Classifies each project (open source vs toy vs production-ish signals)
- Asks the LLM to select exactly seven unique repos with meaningful author commits
That last step is important. A candidate with forty forks should not drown evaluators. The model must justify which repos represent real work.
| Stage | Output |
|---|---|
| PDF parse | JSON Resume sections (basics, work, education, skills, projects) |
| GitHub fetch | Repo list + commit heuristics |
| LLM repo pick | Top 7 evidence repos |
| Evaluator | Category scores + bonuses + deductions + narrative evidence |
Scoring rubric and explainability
evaluator.py scores against a role you pass on the CLI. Each role loads categories and weights from roles/<role>/criteria.yaml with Jinja templates for fairness rules.
Published categories include:
open_sourceself_projectsproductiontechnical_skills
Plus explicit bonus and deduction lines. The stdout summary is meant for humans: not just a number, but why the number moved.

That design is closer to how good hiring managers already think. The LLM is doing structured note-taking, not replacing judgment.
Local vs hosted LLM providers
The README supports:
- Ollama or vMLX for fully local runs (privacy-friendly for HR data)
- Google Gemini when you want a stronger extractor without operating GPUs
Templates are per-section (basics.jinja, work.jinja, skills.jinja, etc.), which reduces the classic "one-shot JSON hallucination" failure mode. Each call has a narrow job.
# Example shape: see repo README for env vars and role names python score.py path/to/resume.pdf --role backend_engineer
Set DEVELOPMENT_MODE=True during tuning to append rows to resume_evaluations.csv and inspect cached JSON in cache/.
Guardrails I would add before trusting scores
Open source does not mean "ship to production HR without edits." If I were hardening this for a client:
- Bias audits on synthetic resumes (names, schools, gendered language) similar to what projects like Candisift document for ATS ranking.
- Human-in-the-loop gate: AI produces evidence packets; humans make reject/advance calls.
- GitHub rate limits and private repos: not every strong candidate has public code. Missing GitHub should be neutral, not punitive.
- PDF OCR path for scanned resumes (Hiring Agent is PyMuPDF-first; scanned docs need a fallback extractor).
- Retention policy: cache/ CSV defaults are great for dev; prod needs encryption and deletion workflows.
Also compare with keyword matchers like Resume-Matcher if your problem is JD tailoring, not holistic evidence scoring. Different tools, different failure modes.
Where this fits my applied AI work
Teams hire me to automate ops, not to automate ethics away. Hiring Agent is interesting because it is inspectable: templates, criteria YAML, cached JSON, CSV exports. That is the same shape I want in voice agents and CRM automations: auditable steps, explicit rubrics, local inference options.
If you are building recruiting automation, start by measuring time-to-evidence (how fast a recruiter gets a justified shortlist), not time-to-reject.
Want help wiring local LLM pipelines with audit trails for HR or ops? Book a free discovery call.

