Work / 01 / Placewise
Résumés in, evidence-backed shortlists out.
Recruiters at architecture, engineering and construction staffing firms spend hours reading résumés against vague job notes. Placewise sits on top of their existing ATS and does the first pass: it reads every résumé, finds the right people for a role, and backs each recommendation with the résumé line that proves it.
What it does
- Intake. PDF, DOCX and text résumés arrive from an upload page, a watched folder or an n8n webhook, and wait in a Postgres job queue.
- Understanding. A LangGraph pipeline per résumé dedupes, parses and screens the file, has Claude extract a structured profile, then validates it in code.
- Job orders. Messy recruiter notes become a structured job order, and the system asks clarifying questions when something is missing.
- Matching. Hard filters (license, state, years) plus hybrid search and rerank find candidates, and each one is scored with quoted evidence.
- Ask. A conversational assistant answers questions about candidates and roles, citing résumé lines.
- Human in control. Shortlists go through approve or reject. Nothing is sent automatically.
SKIP LOCKED, so there is no separate broker to run.Ingest: treat every file as untrusted
Real résumé folders are full of scans, two-column layouts, duplicates, corrupt files and occasionally hidden text written to manipulate an AI screener. The ingest graph screens each file before any model sees it, and never takes the model's word for a fact.
- Hidden and injected text is detected and stripped in a plain-code step that takes about 30 ms.
- Claude returns a structured profile with evidence quotes, then code validates the facts: each quote must exist in the résumé, and years of experience are computed from the dates.
- Extraction is cached on the cleaned text, model and prompt, so reprocessing a file costs nothing.
- Every model call is logged with tokens, latency and cost. A daily spend cap pauses work instead of failing it.
On the evaluation set, unusable and adversarial files were caught with 100% precision and recall. Moving extraction from a small model to Claude Sonnet raised precision on flawed résumés from 57% to 94%, at about two cents per résumé.
Retrieval: from 48% to 89% recall
Pure vector search missed most qualified candidates, because requirements like “PE licensed in Texas, 10+ years” are hard constraints, not semantic similarity. Each layer stayed only because the evaluation showed it helped.
| Retrieval setup | recall@10 | precision@10 | MRR |
|---|---|---|---|
| Vector search only | 48% | – | – |
| + hard filters | 84% | – | – |
| + hybrid keyword search + rerank | 89% | 98% | 1.00 |
Measured against an answer key the API never sees: 597 résumés and 15 job orders, including scanned, corrupt, duplicate and prompt-injection files.
Matching you can audit
For each candidate, the model must quote the résumé line that supports each requirement. Code checks that every quote really exists, and code, not the model, computes the final score. Scoring is blind: names, addresses and graduation years are removed first.
97.6% submit precision, and a recruiter can see exactly why each person made the list.
Production engineering
Deployment
- Google Cloud VM running the Docker Compose stack behind Caddy and Cloudflare.
- GitHub Actions: CI on every pull request, auto-deploy from
main, health check, automatic rollback. - Uptime and budget alerts, and a hard spend cap on model calls.
Cost and quality controls
- Prompt caching plus a Postgres response cache, so repeat questions cost nothing.
- LangSmith tracing and versioned prompts.
- 21 architecture decision records, one per trade-off.
SKIP LOCKED queue instead of a broker (ADR 010). Auto-deploy with rollback (ADR 020). Claude cost caching (ADR 021).Stack
Fits more than recruiting
The same pattern (untrusted documents in, validated structured data, filtered hybrid retrieval, evidence-backed answers) fits contracts, support tickets, claims, research papers and more.