Job Application Autopilot 🤖
Inspiration
Job hunting is a grind. As a recent BSc Information Systems graduate entering a competitive market, I found myself repeating the same exhausting cycle hundreds of times — scroll through job boards, read a listing, decide if it's worth applying, rewrite my CV summary, write a cover letter from scratch, send an email, wait, follow up, repeat.
The worst part wasn't the rejection. It was the invisible labour — the hours spent on applications that were never even going to be a good fit, the copy-pasting, the subtle rewording, the mental overhead of tracking what was sent, to whom, and when.
I kept thinking: every single step of this process is something an agent could do better than me at 2am.
That thought became this project.
What It Does
Job Application Autopilot is a multi-agent Python system that autonomously handles the entire job application pipeline — from discovery to follow-up — with zero human intervention once configured.
The pipeline runs in sequence across 7 specialised agents:
CV Parser → Job Discovery → Relevance Filter → CV Tailor → Cover Letter → Submission → Tracker → Follow-up
- Scraper — Crawls vacancymail.co.zw for new ICT job listings, fetching full descriptions from each job page
- CV Parser — Extracts structured data from the candidate's CV (
.docxor.pdf) usingpdfplumber/python-docx, then uses Qwen to produce a typed JSON profile - Relevance Filter — Scores each listing against the CV profile (0–100) using either a fast heuristic engine or Qwen for semantic scoring. Jobs scoring $\geq 70$ are auto-applied; scores in $[50, 70)$ are flagged for review
- CV Tailor — Calls Qwen to rewrite the professional summary and reorder skills to mirror the language of the specific job description — without fabricating experience
- Cover Letter Generator — Calls Qwen to produce a personalised, 4-paragraph cover letter grounded in the candidate's real experience
- Submission Agent — Sends the application email via the Gmail API with
.docxattachments - Tracker + Follow-up — Logs every application to SQLite and automatically sends polite follow-up emails 7 days after submission
A FastAPI dashboard provides a real-time view of the pipeline — application statuses, relevance scores, expiry dates, and a one-click pipeline trigger.
How I Built It
The stack is intentionally lean:
| Layer | Technology |
|---|---|
| Language | Python 3.13 |
| LLM | Qwen Cloud (qwen-plus) via OpenAI-compatible SDK |
| Scraping | httpx + BeautifulSoup4 |
| CV parsing | pdfplumber, python-docx |
| Gmail API (OAuth2) | |
| Database | SQLite via sqlite3 |
| Dashboard | FastAPI + Jinja2 |
| Deployment | Alibaba Cloud |
The architecture follows a dual-mode design for every LLM-dependent agent: a heuristic fallback that runs without any API credits, and a full LLM mode that activates when QWEN_API_KEY is present. This meant I could build, test, and iterate the entire pipeline before receiving Qwen credits — which was critical given the tight deadline.
The scoring function is worth noting. Each job is scored as:
$$S = S_{title} + S_{skills} + S_{domain} + S_{location} - S_{redflags}$$
Where:
- $S_{title} \in {0, 15, 40}$ — strong/weak/no title pattern match
- $S_{skills} = \min(30,\ 6 \times |skills_matched|)$ — skill keyword hits, capped at 30
- $S_{domain} = \min(20,\ 10 \times |domain_matched|)$ — domain relevance, capped at 20
- $S_{location} \in {0, 10}$ — Harare/remote bonus
- $S_{redflags} = 25 \times |red_flags_matched|$ — penalise mismatches like "10+ years required"
In LLM mode, Qwen replaces this formula with semantic reasoning, producing a score alongside matched keywords, red flags, and a 2-sentence justification.
Challenges
1. Job board reliability My original target, zimbajob.com, turned out to host scam listings — inflated salaries, 50 open positions at once, copy-pasted descriptions. I switched to vacancymail.co.zw mid-build, which required inspecting live HTML to fix CSS selectors twice before getting clean data.
2. Building without credits I had no Qwen API credits during most of development. The dual-mode architecture (heuristic + LLM) wasn't just a nice-to-have — it was a survival strategy. Every agent had to be fully testable without touching the API.
3. Selector brittleness Web scrapers are fragile by nature. The vacancymail scraper went through two complete selector rewrites after the actual HTML turned out to differ from what was inferred. The fix was simple but the lesson was important: always dump and inspect raw HTML before writing a single selector.
4. Gmail OAuth on a dev machine Setting up Gmail API OAuth2 for a desktop app hit the classic "app not verified" wall. Navigating Google Cloud Console's consent screen, test user approval, and the OAuth flow added unexpected friction. Documented clearly in the README for anyone reproducing the project.
5. The real problem this solves The deeper challenge was philosophical: how do you build an agent that applies selectively rather than spamming every listing? The relevance filter and the two-tier threshold ($\geq 70$ auto-apply, $50$–$70$ review) was the answer — the agent needed judgment, not just automation.
What I Learned
- Agent orchestration is about failure handling, not just happy paths. Every agent needed a fallback, a retry, and a dry-run mode before it was production-ready.
- Qwen's OpenAI-compatible API made switching from prototyping on other models to production on Qwen completely seamless — one line change (
base_url). - Structured prompting is an engineering discipline. Getting Qwen to return clean JSON reliably required iterating on system prompts, setting
temperature=0.1for structured tasks and0.4–0.6for creative writing tasks, and always stripping markdown fences defensively. - Sometimes the most important architectural decision is what the agent should refuse to do — in this case, never fabricating experience, never applying to obvious mismatches, never sending without a contact email.
What's Next
- Telegram channel monitoring as an additional job source (more open than WhatsApp)
- LLM-powered follow-up personalisation — referencing specific job details in follow-up emails rather than a generic template
- Interview scheduler — detecting positive responses and suggesting available slots
- Multi-candidate support — a single deployment serving multiple job seekers with isolated profiles
Built with Python, Qwen Cloud, FastAPI, and a lot of personal frustration with job hunting.

Log in or sign up for Devpost to join the conversation.