An job matcher and resume tailoring web
app built on real Techmap job-posting data (49,806 postings). Flask + Tailwind +
HTMX production UI; the original Streamlit prototype is retained in app.py.
The build pipeline streams a Techmap NDJSON dump, deduplicates, and writes
structured data + FAISS index to data/.
python build.py --techmap-json <path-to-techmap.ndjson> --n 25000To rebuild only embeddings/index from an existing data/jobs.csv (e.g. to
switch the embedding backend):
python reembed.py # Sentence-BERT (default, ~2 min for ~50k jobs)
python reembed.py --backend tfidf # legacy TF-IDF + SVD (CPU-only, no torch)| File | Size | Description |
|---|---|---|
data/jobs.csv |
~142 MB | Structured 18-field job table |
data/embeddings.npy |
~73 MB | 384-d Sentence-BERT vectors |
data/job_ids.npy |
~1.7 MB | Embedding-to-job-ID mapping |
data/jobs_hnsw.faiss |
~86 MB | FAISS HNSW ANN index |
data/encoder_meta.joblib |
<1 KB | Records which encoder built the index |
python server.pyOpen http://127.0.0.1:8501. The app loads all artifacts into memory at startup.
| Variable | Default | Purpose |
|---|---|---|
DEEPSEEK_API_KEY |
(none) | Enables LLM resume tailoring and cover letters. Without it the app uses deterministic templates. |
DEEPSEEK_MODEL |
deepseek-v4-flash |
Model override for LLM calls |
SBERT_MODEL |
all-MiniLM-L6-v2 |
Sentence-BERT model ID for embeddings |
SECRET_KEY |
dev default | Flask session encryption key |
FLASK_RELOAD |
0 |
Set to 1 to enable auto-reloader (disabled by default on Windows because torch imports cause restarts) |
You can place these in a .env file in code/; the app reads it on startup.
python evaluate.pyReports three things:
- Retrieval quality -- dense Sentence-BERT vs keyword baseline Precision@10 (Aisha persona: 1.00 vs 0.10)
- Adaptive learning -- simulated feedback over 10 rounds; large-company share in top-20 rises from 75% to 100%, company weight grows 0.35 to 0.60
- Persona constraint checks -- 4 test personas (Aisha, Marcus, Priya, Kenji) pass hard-filter validation on top-10 results
code/
server.py Flask app entry point
build.py Offline data pipeline (Techmap -> CSV + FAISS)
reembed.py Rebuild embeddings/index from existing CSV
evaluate.py Offline benchmarks
requirements.txt Python dependencies
src/
config.py Paths, skills ontology, default weights
data.py Job loading and streaming ingestion
embed.py Sentence-BERT encoding + FAISS index build
rank.py Multi-stage ranking (retrieve -> filter -> score -> rank)
profile.py Candidate profile assembly
feedback.py Pairwise adaptive weight learning
resume.py Resume tailoring + cover letter generation
resume_parse.py PDF/DOCX/TXT text extraction
resume_pdf.py Render tailored resume to PDF
resume_docx.py Render tailored resume to DOCX
analytics.py Pre-computed market analytics charts
skills.py Skill extraction from job descriptions
llm.py DeepSeek API wrapper (optional)
templates/
base.html Layout, nav bar, footer (all pages extend this)
index.html Search form + results page
_results.html HTMX partial: ranked job cards
resume.html Resume tailoring + cover letter page
_resume_result.html HTMX partial: tailored resume output
_cover_letter.html HTMX partial: generated cover letter
analytics.html Market analytics dashboard
data/ Pre-built data artifacts (jobs, embeddings, FAISS index)
If the data artifacts are already present in data/:
cd code
pip install -r requirements.txt
python server.py # app at http://127.0.0.1:8501
python evaluate.py # benchmarksNo API keys, external services, or Docker required.