The Problem With Manual CV Review
The modern hiring process is broken at the very top of the funnel. According to recent LinkedIn data, recruiters spend an average of 23 hours reviewing CVs for a single successful hire. This is not high-value, strategic work; this is the manual, repetitive process of pattern matching candidate resumes against a rigid list of job requirements.
Let's frame the actual financial cost of this inefficiency. If we assume a conservative fully-loaded cost of $40 per hour for a recruiter or hiring manager, that equates to $920 per hire spent strictly on manual screening labor. For a mid-sized firm or a healthcare staffing agency running 50 hires a year, that is $46,000 in pure screening cost. When you scale this across larger organizations or high-turnover roles, the financial leak becomes a massive liability.
Beyond the hard costs, manual screening introduces unacceptable delays. In competitive markets, top talent is off the market in 10 days. If your screening process takes a week just to surface the best candidates from a pile of 500 applications, you have already lost. The traditional approach is fundamentally unscalable, prone to human bias, susceptible to fatigue-induced errors, and represents one of the largest operational bottlenecks in any scaling enterprise.
Why Most ATS Tools Don't Actually Use AI
You might be thinking, "But my ATS already does this." The harsh reality is that most Applicant Tracking Systems (like Greenhouse, Lever, or Workable) do not actually evaluate candidates. What they do is primitive keyword matching.
We need to aggressively distinguish keyword matching from semantic evaluation.
Keyword matching operates by parsing the text of a CV and counting occurrences of specific strings ("Python", "HIPAA", "Project Management"). If the strings are present, the candidate passes the gate. This approach fails catastrophically in several common scenarios:
- Career Changers: A candidate might have extraordinary, transferable operational experience but doesn't use the exact industry jargon your keyword filter demands.
- Non-Standard Formatting: Complex multi-column PDFs, creative resumes, or non-standard international CV formats frequently break legacy ATS parsers, resulting in blank profiles and automatic rejection.
- Role-Function Mismatches: A candidate might list "Managed a team of 10 Python developers" on their resume. A keyword scanner sees "Python" and flags them as a software engineer, entirely missing that they are an engineering manager who hasn't written code in five years.
Semantic evaluation—what true AI provides—reads the resume the way a human would. It understands context, measures the depth of experience, infers transferable skills, and evaluates the candidate's trajectory against a highly specific scoring rubric. It doesn't look for the word "HIPAA"; it understands that a candidate who "managed patient data compliance for a 50-bed facility" inherently possesses HIPAA knowledge.
The Uplift ATS Architecture
To solve this, we built the Uplift ATS—a production-grade, multi-tenant evaluation pipeline. The architecture is designed for resilience, accuracy, and absolute scale.
The pipeline follows a strict, asynchronous state machine architecture:
- File Upload: The candidate submits their resume via the frontend. The file is uploaded directly to a secure Supabase storage bucket, generating a secure, time-limited signed URL.
- OCR Enforcement: Before any LLM touches the file, it passes through an OCR-first enforcement layer. We extract the text using robust OCR libraries. This is a critical architectural decision. Passing raw PDFs to an LLM vision model or relying on basic PDF text extraction often results in corrupted text, gibberish, or blank evaluations. By enforcing OCR first, we guarantee clean, standardized UTF-8 text. This prevents massive token waste on corrupt files and ensures the LLM has pristine data to evaluate.
- Async Queue: The normalized text is pushed into an asynchronous processing queue. The database record for this application is updated to a
ready_to_evaluatestate. - LLM Evaluation: A background FastAPI worker picks up the job, updating the state to
evaluating. It constructs a complex prompt containing the job's scoring rubric and the parsed CV text, and sends it to the Claude API (specifically, Claude 3.5 Haiku for speed and cost-efficiency). - State Machine Resolution: The Claude API returns a strictly formatted JSON object containing scores and reasoning for each dimension. The worker parses this, updates the database, and transitions the state to
complete,error, orineligible(if the CV is entirely irrelevant). - Frontend Polling: Throughout this process, the frontend polls the Supabase database, updating the UI in real-time as the candidate moves through the pipeline.
The Prompt Engineering Layer
The core intelligence of the system lives in the prompt engineering layer. You cannot simply ask an LLM, "Is this a good candidate for the job?" You will get generic, hallucinated, and useless responses.
Instead, we decompose the job description into weighted competency dimensions. For example, a Senior React Developer role might be broken down into:
- React/Next.js Architecture (Weight: 40%)
- State Management & Performance (Weight: 30%)
- CI/CD & Testing (Weight: 20%)
- Leadership/Mentorship (Weight: 10%)
The prompt explicitly instructs Claude to evaluate the candidate against only these dimensions. For each dimension, Claude must provide a score out of 10, a boolean pass/fail based on a strict threshold, and a 2-sentence justification quoting the resume directly.
Furthermore, we implemented a Kill Switch Design. Every job configuration in the database has an ai_evaluation_enabled boolean. This allows hiring managers to disable the AI evaluation pipeline on a per-job basis instantly, without requiring a code deployment. If an API outage occurs or a role requires highly subjective manual review, the system seamlessly falls back to standard file storage.
Multi-Tenancy and Row-Level Security
Because Uplift ATS handles sensitive HR data across multiple organizations, multi-tenancy and strict data isolation were non-negotiable.
We achieved this using Supabase Row-Level Security (RLS). Every table in the database (candidates, jobs, evaluations) has an organization_id column.
We wrote strict PostgreSQL policies that leverage the authenticated user's JWT to enforce isolation. A recruiter from Organization A literally cannot query, view, or modify a candidate from Organization B, even if they explicitly try to guess the UUID. The database engine itself rejects the query.
This matters immensely for GDPR, CCPA, and general HR privacy compliance. During the development process, we executed a critical migration from Neon Auth to Supabase Auth. This migration resolved a significant vulnerability where older auth implementations relied on RS256 JWTs that were cumbersome to verify across microservices. Supabase Auth provided a cleaner, HS256-backed standard that integrated natively with our RLS policies, radically simplifying our security posture.
Results: 124 Candidates, 9 Roles
We didn't just build this in a lab; we deployed it into production. Across a massive hiring sprint, the pipeline evaluated 124 candidates across 9 distinct technical and operational roles.
The results fundamentally changed our operations:
- 96% Top Match Score: When human hiring managers audited the AI's top 5 recommended candidates per role, they agreed with the AI's assessment 96% of the time.
- Evaluation Speed: The average time from a candidate clicking "Submit" to a full, multi-dimensional semantic evaluation being securely stored in the database was under 30 seconds.
- Time Saved: Compared to our previous manual review cycle, we eliminated over 60 hours of manual screening time in a single week.
How to Build This Yourself
If you want to implement this architecture, here is the blueprint.
1. Supabase Schema and State Machine
You need a table that tracks the exact state of the application.
CREATE TYPE evaluation_status AS ENUM (
'pending_data',
'ready_to_evaluate',
'evaluating',
'complete',
'error',
'ineligible'
);
CREATE TABLE applications (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
job_id UUID REFERENCES jobs(id),
candidate_name TEXT,
resume_text TEXT,
status evaluation_status DEFAULT 'pending_data',
evaluation_data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
2. FastAPI Trigger Endpoint
Your backend needs an endpoint to transition the state and trigger the background worker.
from fastapi import APIRouter, BackgroundTasks
from supabase import create_client
import os
router = APIRouter()
supabase = create_client(os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_SERVICE_KEY"))
@router.post("/trigger-evaluation/{application_id}")
async def trigger_eval(application_id: str, background_tasks: BackgroundTasks):
# Transition state
supabase.table("applications").update({"status": "evaluating"}).eq("id", application_id).execute()
# Trigger async evaluation
background_tasks.add_task(run_claude_evaluation, application_id)
return {"status": "processing initiated"}
3. The Claude Prompt Structure
Force structured JSON output to ensure your application doesn't crash trying to parse conversational text.
def run_claude_evaluation(application_id: str):
# ... fetch job rubric and resume text ...
prompt = f"""
You are an expert technical recruiter. Evaluate the following resume against the provided rubric.
RUBRIC: {rubric_json}
RESUME: {resume_text}
You MUST return ONLY valid JSON in the following format:
{{
"overall_score": <int 0-100>,
"dimensions": [
{{
"dimension_name": "<string>",
"score": <int 0-10>,
"pass": <boolean>,
"justification": "<2 sentences max quoting the resume>"
}}
],
"recommendation": "<Proceed | Reject | Hold>"
}}
"""
# ... call Claude API and update database ...
When NOT to Use AI CV Screening
Transparency is critical. This system is incredibly powerful, but it is not a silver bullet. You should explicitly avoid AI CV screening in the following scenarios:
- Low-Volume, High-Touch Roles: If you are hiring a C-level executive or a specialized partner, you will likely only receive a dozen highly qualified resumes. The human nuance required to evaluate these profiles outweighs the time saved by automation.
- Highly Subjective Creative Roles: Evaluating a portfolio of graphic design work, UI/UX case studies, or creative copywriting is notoriously difficult for text-based LLMs. Vision models are improving, but human aesthetic judgment is still required.
- Non-Standard Academic Formats: While our OCR-first approach handles 99% of commercial resumes, complex 30-page academic CVs with intricate citation formats and embedded charts can still cause parsing hallucinations.
Conclusion
By enforcing an OCR-first architecture, leveraging the semantic intelligence of the Claude API, and binding it all together with a rigid PostgreSQL state machine, we eliminated 70% of the manual labor involved in our recruitment funnel.
If you are spending thousands of dollars a month paying humans to read PDFs, you are burning capital that could be spent on actually interviewing and closing top candidates.
To see the system in action, you can view the live demonstration at cv-pipeline-frontend.onrender.com.
If you are a healthcare operator, DSO, or technical founder looking to implement custom automation pipelines, book a free consultation with me to discuss your specific bottlenecks.