Real output: Oral's CV automatically tailored to the best-matching job (cosine score 0.45).
What it does
Job hunting means rewriting the same CV and cover letter for every posting. This project automates that loop end-to-end:
- Scrape — pulls live job listings from Adzuna and Remotive.
- Embed & store — encodes each posting with sentence-transformers and stores the vectors in PostgreSQL with the pgvector extension.
- Match — ranks postings against your CV by cosine similarity, surfacing the best-aligned roles.
- Tailor — an LLM rewrites your CV and drafts a cover letter for each top match, emphasising the skills that posting actually asks for.
- Fact-check — generated content is checked back against your original CV so nothing is invented.
- Export — outputs polished
.docxfiles, with on-demand PDF conversion.
What “tailoring” actually changes
The agent doesn’t just paste keywords — it rewrites the professional summary and highlights to match the target role while staying truthful to the source CV.
Before / after: the professional summary rewritten for the matched role.
Architecture
The system is a FastAPI backend with a modular pipeline, orchestrated with Docker Compose. Long-running work (ingestion, tailoring) runs as background tasks with polling-based status tracking, so the API stays responsive.
| Layer | Technology |
|---|---|
| Backend / API | Python 3.11+, FastAPI (Swagger UI at /docs) |
| Database | PostgreSQL + pgvector |
| Embeddings | Sentence-Transformers |
| LLM providers | Anthropic Claude (default), OpenAI, or local Ollama |
| Document rendering | docxtpl + headless LibreOffice (DOCX → PDF) |
| Deployment | Docker & Docker Compose |
Key features
- Semantic matching over raw keyword search — finds roles that fit the CV, not just ones that share words.
- Pluggable LLM backend — swap between Claude, OpenAI, or a self-hosted Ollama model via config.
- Configurable search — country, location, and remote-only filtering.
- Async pipeline — background ingestion/tailoring with status polling.
- RESTful API with interactive Swagger docs.
Installation & usage
Local setup (database in Docker, app on host):
docker compose up -d db
cp .env.example .env # add your API keys
pip install -r requirements.txt
uvicorn src.api.app:app --reload
Full Docker setup:
cp .env.example .env
docker compose up --build
One-shot CLI pipeline (ingest + tailor):
python -m src.main
The interactive API docs are then available at /docs.
License
MIT — see LICENSE in the repository.