Local LLM + RAG pipeline that automates intelligent job filtering across multiple job boards. Encodes your hiring judgment as semantic search β not keyword matching β and runs entirely on your machine.
Senior-level job searches on major boards are high-noise, low-precision. Mislabeled IC roles, vendor-chain postings, keyword-match junk, and irrelevant industries overwhelm every search page. Signal-based tools still produce misaligned results because they match keywords, not judgment.
This project encodes your judgment once β as semantic embeddings built from your resume, your target role archetypes, a global evaluation rubric, and your own growing decision history β then applies it continuously across every board you care about. Everything runs locally via Ollama and ChromaDB; no data leaves your machine.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLI Entry Point β
β python -m jobsearch_rag [command] β
ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββ΄ββββββββββββββββ
β β
βββββββββββΌββββββββββββ ββββββββββββΌβββββββββββββββββββ
β Adapter Layer β β RAG Pipeline β
β (IoC / Strategy) β β (Ollama + ChromaDB) β
β β β β
β AdapterRegistry β β - Resume embedding β
β JobBoardAdapter ABC β β - Role archetypes β
β β ZipRecruiter β β - Global positive signals β
β β Indeed β β - Negative signal penalty β
β β WeWorkRemotely β β - Decision history β
β β LinkedIn* β β - Compensation scoring β
β β β - LLM disqualifier β
βββββββββββ¬ββββββββββββ ββββββββββββ¬βββββββββββββββββββ
β β
ββββββββββββββββ¬ββββββββββββββββ
β
ββββββββββΌβββββββββββ
β Ranker & β
β Exporter β
β β
β - Score fusion β
β - Deduplication β
β - Markdown / CSV β
β - JD files β
β - Browser tabs β
βββββββββββββββββββββ
* LinkedIn: overnight mode, stealth, throttled
The scoring pipeline encodes two independent dimensions of judgment:
- What kind of role β
archetype_score(role type match) +fit_score(resume alignment) +comp_score(compensation vs. target) - What kind of environment β
culture_score(work model, ethics, autonomy preferences) +negative_score(red-flag penalty for adtech, chaos culture, seniority mismatch, etc.)
A role must score well on both axes to rank highly. The LLM disqualifier acts as a binary gate for structural problems keywords can't catch (IC roles disguised as architect titles, staffing-agency postings).
- Adapter layer β Playwright-based browser automation loads job board pages
exactly as a human would. Each board has its own adapter; all produce the
same
JobListingdata contract. - Scoring pipeline β Job descriptions are embedded locally via
nomic-embed-textand scored against six ChromaDB collections (resume, role archetypes, global positive signals, negative signals, decision history, plus inline compensation parsing). - LLM disqualifier β A
mistral:7bpass screens for structural mismatches, personalized with your past rejection reasons. Defense-in-depth prompt injection mitigation protects this stage. - Ranker β Weighted score fusion, cross-board deduplication (cosine > 0.95), and minimum-score threshold filtering.
- Export β Ranked results as Markdown table, CSV with compensation columns, individual JD files, and/or browser tabs.
Your decisions feed back into the system:
search β review β decide (yes/no/maybe + reason)
β
ββββΆ decisions collection (future history_score)
ββββΆ rejection reasons β personalize LLM disqualifier
ββββΆ rescore β eval (measure agreement, precision, recall)
Over time, the pipeline learns your preferences without retraining any models.
| Requirement | Install |
|---|---|
| Python 3.11+ | brew install python@3.13 or via uv |
| uv | curl -LsSf https://astral.sh/uv/install.sh | sh |
| Ollama | brew install ollama |
| direnv | brew install direnv (optional β auto-activates venv) |
# Clone and install
git clone https://github.com/grimlor/jobsearch-rag.git
cd jobsearch-rag
uv sync --extra dev
# Optional: auto-activate venv via direnv
direnv allow
# Install Playwright browsers
uv run playwright install chromium
# Start Ollama and pull models
ollama serve & # if not already running
ollama pull mistral:7b
ollama pull nomic-embed-text
# Configure your search (edit these files)
# config/settings.toml β board URLs, scoring weights, output prefs
# config/role_archetypes.toml β descriptions of your target roles
# config/global_rubric.toml β universal evaluation dimensions
# data/resume.md β your resume in plain Markdown
# Index your resume, archetypes, and rubric into ChromaDB
uv run python -m jobsearch_rag index
# Run a search across enabled boards
uv run python -m jobsearch_rag search
# Review results interactively (builds decision history)
uv run python -m jobsearch_rag review# Core workflow
jobsearch-rag index # Index resume + archetypes + rubric into ChromaDB
jobsearch-rag search # Search β score β rank β export (cumulative by default)
jobsearch-rag search --fresh # Fresh search (discard prior accumulated results)
jobsearch-rag search --board linkedin --overnight # Single board, overnight throttle
jobsearch-rag rescore # Re-score exported JDs without browser (tuning loop)
jobsearch-rag rescore --force-rescore # Re-score including previously decided listings
# Review & decisions
jobsearch-rag review # Interactive batch review of undecided listings
jobsearch-rag decide <job_id> --verdict yes|no|maybe
jobsearch-rag decide <job_id> --verdict no --reason "Requires on-call rotation"
jobsearch-rag decisions show <job_id> # Inspect a stored decision
jobsearch-rag decisions remove <job_id> # Remove from ChromaDB (JSONL audit log preserved)
jobsearch-rag decisions audit # List all decisions with reasons
# Evaluation
jobsearch-rag eval # Pipeline vs. human decisions: agreement, precision, recall
jobsearch-rag eval --compare-models mistral:7b llama3.1:8b # A/B test disqualifier models
# Utilities
jobsearch-rag boards # List registered adapters
jobsearch-rag login --board ziprecruiter --browser msedge # Interactive session login
jobsearch-rag reset # Clear ChromaDB collections
jobsearch-rag reset --clear-output # Also clear output directory
# Indexing variants
jobsearch-rag index --resume-only # Re-index resume only
jobsearch-rag index --archetypes-only # Re-index archetypes + rubric + negative signalsNote: Replace
jobsearch-ragwithuv run python -m jobsearch_ragif your venv is not activated.
All configuration lives in three TOML files under config/:
| File | Purpose |
|---|---|
settings.toml |
Enabled boards, search URLs, scoring weights, Ollama models, output paths, comp bands, security prompts |
role_archetypes.toml |
Natural language descriptions of your target roles with positive/negative signals |
global_rubric.toml |
Ten universal evaluation dimensions (culture, ethics, seniority, operational burden, etc.) |
Your resume goes in data/resume.md. Past decisions (yes/no/maybe on roles
you've reviewed) are auto-stored in data/decisions/ and build a personal
preference signal over time.
See CONFIG.md for the full schema, defaults, and validation rules.
# Individual checks
uv run task lint # ruff check --fix
uv run task format # ruff format
uv run task type # pyright type checking
uv run task test # pytest -v (unit tests only)
uv run task live # integration + live tests (requires Ollama)
# All checks at once
uv run task check # format β lint β type β testNote: The
uv runprefix is optional if your venv is already activated β either manually (source .venv/bin/activate) or automatically viadirenv allow.
CI runs on 3 OS Γ 3 Python versions (Ubuntu, macOS, Windows Γ 3.11, 3.12, 3.13) with lint, type checking, and test coverage on every push.
See CONTRIBUTING.md for the full development workflow, testing philosophy, and PR process.
Phases 1β8 complete. Active daily use for real job search filtering.
| Phase | Status | Highlights |
|---|---|---|
| 1 β Core Adapter Infrastructure | β | Adapter registry, session manager, ZipRecruiter adapter, error hierarchy |
| 2 β RAG Foundation | β | Ollama embeddings, ChromaDB vector store, resume/archetype indexing, semantic scoring |
| 3 β Scoring Pipeline | β | Score fusion, cross-board dedup, LLM disqualifier, decision history |
| 4 β Export & Polish | β | Markdown/CSV/JD file export, compensation scoring, context-length safety, operational resilience, interactive review, rejection reason learning, file identity refactor |
| 5 β Observability & Evaluation | β | Structured session tracing, inference metrics, retrieval quality metrics, eval harness with Spearman correlation, model A/B comparison |
| 6 β Security & Data Hygiene | β | Threat model, prompt injection defense (4 layers), input validation, decision audit, privacy verification test, ZipRecruiter Next.js rewrite |
| 7 β Cumulative Search | β | Accumulate results across runs, parallel scoring loop, live/integration test automation |
| 8 β Config Externalization | β | All persona-specific, operational, and tuning parameters moved from source to settings.toml |
| 9 β Portfolio Polish | π§ | Documentation updates (current phase) |
868+ tests across 35 test files. 100% statement coverage on all core modules. CI validated on 3 OS Γ 3 Python versions.
This tool performs browser automation β it loads pages exactly as a human would, navigates via visible links, and reads only data visible in your own authenticated session. It does not redistribute data, bypass access controls, or perform bulk harvesting.
That said, job boards have terms of service governing automated access. You are responsible for understanding and complying with the ToS of any board you configure. This project is provided as-is for personal, educational, and research purposes. The authors assume no liability for misuse.
Some practical guidelines:
- Use human-like request timing (the built-in throttle provides this)
- Run overnight passes for boards with stricter detection (e.g., LinkedIn)
- Don't redistribute scraped data
- Respect rate limits and session caps
| Document | Scope |
|---|---|
| ARCHITECTURE.md | System design, adapter pattern, scoring model overview |
| CONFIG.md | Full schema, defaults, and validation rules for all TOML files |
| SCORING_ENGINE.md | Fusion formula, compensation parsing, disqualification, dedup |
| RAG_PIPELINE.md | Embedding, indexing, retrieval, and ChromaDB collections |
| DATA_FLOW.md | End-to-end data lifecycle and persistence points |
| FEEDBACK_LOOP.md | Decide β rescore β eval cycle and tuning workflow |
| FAILURE_MODES.md | Error catalog, recovery, bot detection, prompt injection |
| EVOLUTION.md | How the system grew phase by phase |
| TEAM_SCALING.md | What would change with multiple users |
| CONTRIBUTING.md | Development setup, testing standards, PR process |
| SECURITY.md | Threat model, privacy guarantees, prompt injection defense |
PolyForm Noncommercial 1.0.0 β free for personal use, learning, and noncommercial purposes.