Back to all projects
Local App·Runs Locally, Not Deployed by Design

Recruit Assistant: Local-First AI Recruiting Copilot

Scans resumes from local folders and email, merges duplicate candidates into one profile, and ranks them against a job with LLM matching plus an LLM-as-judge review pass.

LLM-as-JudgeEmail IngestionLocal-FirstFastAPI + React

This one is deliberately not deployed anywhere: it's built to run locally, one instance per recruiter's own laptop, scanning that recruiter's own files and mailbox. There's no public demo to link to by design; screenshots above show it running.

Disclaimer: All candidates, jobs, and emails shown are synthetic mock data, not real people.

Technical Deep Dive
on failure
Browser
React + Vite
FastAPI Backend
runs locally
SQLite + Local Mirror
candidates, jobs, matches
OS Keychain
OAuth tokens
LLM Client
embeddings + scoring + judge
Gmail / Graph API
email ingestion
OpenRouter
primary
OpenAI
fallback
Core serviceThird-party APIData storeClient / UI
For the full detail beyond what fits here, these are standalone interactive diagrams (they open in a new tab, sized for a wide screen):

Architecture

  • Folder scanning and email scanning feed one shared pipeline, so both sources produce the same candidate profile
  • Every scanned resume is mirrored to local disk (file, summary, metadata) so results are browsable offline
  • Candidates are matched by email, or name and phone as a fallback, so the same person seen twice merges into one profile instead of duplicating
  • Matching runs in stages: a fast similarity pre-filter, then LLM scoring, then a judge pass that reviews borderline results, keeping cost and speed reasonable across a full candidate pool
  • Runs fully offline in mock mode by default; real LLM scoring is opt-in

Design Decisions

  • Single local account, bound to localhost, with login attempts rate-limited
  • OAuth tokens for connected mailboxes are stored in the OS keychain, never in the database
  • Deleting a candidate is a real, permanent delete, not a soft-delete
  • Requires a one-time consent step before switching from mock data to a real LLM, since that sends resume text to a third-party API
  • Pipeline stage (sourced through placed) is tracked separately from match quality

Problems & Challenges

  • A dashboard KPI disagreed with its own detail page until the counting logic was unified
  • Missing route handling meant a mistyped URL showed a blank page instead of a proper 404
  • A "last scanned" timestamp silently never got written, always showing as blank
  • Bulk delete had a confirmation step that single-item delete was missing

Limitations & Improvements

  • Single account, single machine for now; storage is built behind an interface so a shared or cloud version wouldn't need a rewrite
  • Pipeline stage moves are free-form today, with no enforced workflow
Stack
Python 3.11FastAPISQLAlchemy + SQLiteReact 18 + TypeScript + ViteZustandTailwind CSS v4OpenRouterOpenAI (fallback)Gmail / Microsoft Graph OAuthOS Keychain