An autonomous agent that prepares a job application end-to-end: it fetches a job posting, parses your resume, runs a hybrid ATS compatibility check, and generates grounded tailoring suggestions - running 100% locally on an 8B model via Ollama. No API keys, no rate limits, and your resume never leaves your machine.
Next.js UI ─────┐ (streams the agent trace live over NDJSON)
Streamlit UI ───┼──▶ FastAPI / direct call
CLI ────────────┘ │
▼
┌───────────────────┐ ┌──────────────────────────┐
resume file ─▶│ Agent loop (LLM │────▶│ tools │
(PDF/DOCX) │ plans each step) │◀────│ fetch_job_posting │
job URL/text ▶│ or direct │ │ extract_job_info (LLM) │
│ pipeline (ATS- │ │ parse_resume │
│ only, rewrite) │ │ run_ats_check (hybrid)│
└─────────┬─────────┘ │ tailor_resume (LLM) │
│ │ rewrite_resume (LLM) │
decision trace │ save_report │
▼ └──────────────────────────┘
trace + report + rewritten resume (opt-in)
Three frontends share one core: a Next.js + FastAPI web app (streaming trace), a Streamlit app, and a CLI. Two modes everywhere: full agent run or a standalone ATS test (job posting optional, without one it's a general resume health check). After any run with a job, JobPilot asks whether you want your resume rewritten for that job - the draft never invents experience.
- Hand-rolled agent loop, no framework. The loop in
jobpilot/agent.pyis ~100 lines: model plans → tool executes → result feeds back → repeat. Includes a step cap, duplicate-call detection, and per-step error strings the model can recover from (a failed scrape tells the model to ask for pasted text instead of retrying). - Small tool args, shared context. An 8B model corrupts long JSON in tool
arguments, so tools exchange large state (resume text, job info, ATS report)
through an
AgentContextobject and the model only passes tiny args like a URL. - Hybrid ATS checker - deterministic where it matters. Keyword coverage
(with a synonym table so "JS" matches "JavaScript"), section/contact/format
checks, and quantified-bullet analysis are pure Python and unit-tested offline;
only semantic fit uses embeddings (
nomic-embed-textcosine similarity) and the score degrades gracefully if the embedding model is missing. - Grounded tailoring & rewriting. Tailoring and full-resume rewrites are fed the ATS gap analysis but hard-constrained to never invent experience - missing skills are never offered to the rewriter (early testing showed the model would "help" by adding them), and generic keywords are woven in only where the original resume shows evidence. Rewrites are opt-in: the app asks first.
- Streaming agent trace. The FastAPI endpoint streams each tool call as NDJSON; the Next.js UI renders the agent's decisions live.
- Swappable LLM provider. Everything talks to an abstract
LLMClient(chat + embeddings); the Ollama implementation is ~80 lines, so pointing the agent at Anthropic/OpenAI is a one-class change.
| Category | Weight | Method |
|---|---|---|
| Keyword coverage | 40% | required/preferred/other keywords, whole-word + synonym matching |
| Semantic fit | 15% | embedding cosine (resume vs posting), rescaled |
| Sections | 15% | experience / education / skills / projects detection |
| Contact info | 10% | email, phone, LinkedIn/GitHub |
| Content quality | 20% | length, quantified bullets, action verbs, dates, pronouns |
# 1. Ollama + models (one-time, ~5.5 GB)
ollama pull qwen3:8b && ollama pull nomic-embed-text
# 2. Python env
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# 3a. Web app (FastAPI + Next.js)
.venv/bin/uvicorn server.main:app --port 8000 # terminal 1
cd web && npm install && npm run dev # terminal 2 → http://localhost:3000
# 3b. or Streamlit
.venv/bin/streamlit run app.py
# 3c. or CLI (--ats-only for a standalone ATS test; --rewrite to skip the prompt)
.venv/bin/python cli.py --resume my_resume.pdf --job-file job.txt
.venv/bin/python cli.py --resume my_resume.pdf --ats-onlyOn a test run (ML-intern posting vs a student resume) the agent scored
74/100 (good): all 5 required skills matched, and it correctly flagged missing
evaluation-methodology keywords (cross-validation, AUC, precision/recall) plus
content issues (thin resume, weak action verbs) — see reports/ after a run.
- Detailed resume review with every ATS test - strengths, weaknesses, and prioritized fixes, quoting your actual lines.
- Opt-in rewrite & cover letter, grounded in your real experience (never invents anything), exportable as PDF / DOCX / MD / HTML.
- Compare mode ranks one resume against several postings, best match first.
- Run history tracks score changes per resume/job pair over time.
- Evaluation harness (
python -m evals.run): labeled cases with expected score bands - scoring changes can't silently regress.
- JS-rendered job boards (LinkedIn, Workday) block plain HTTP fetch, paste the posting text instead. (The Next Step: Playwright-based fetcher.)
- Keyword extraction quality depends on the local model; a cleanup pass strips company names / locations / generic terms it tends to emit.
- Next Step: DOCX in-place resume editing, cloud-provider backends
(Anthropic/OpenAI) behind the existing
LLMClientinterface.