Most "AI Resume Scanners" on the market have two fatal flaws:
-
Random Score Generation: Because LLMs are probabilistic (
temperature > 0), submitting the exact same resume twice often yields62%first and84%a minute later. - Skill Hallucinations: When asked to tailor a resume or cover letter, standard LLM wrappers cheerfully invent experience with technologies the candidate has never touched—leading to instant rejection during the first 5 minutes of a technical screen.
While building LaunchPad AI PRO (a job aggregator powered by the Adzuna API with a built-in ATS scanner and cover letter generator), I replaced "AI score guessing" with a deterministic verification pipeline.
The Architecture: Quote-Verified Scoring
Instead of asking an LLM to guess a number from 0 to 100, our engine calculates the score across 4 weighted vectors:
-
55% — Quote-Verified Hard Skills (
resumeQuoteEvidence): A skill from the job description is only counted as matched if the parser extracts the exact sentence from the resume as proof. -
15% — Measurable Impact: Regex & NLP detection of quantifiable metrics (
%,$,xgrowth, latencyms). - 15% — Soft Skills & Role Markers.
- 15% — Structural Completeness: Contact info, GitHub/LinkedIn links, standard section headers, and optimal word count.
Core Implementation (TypeScript)
Here is the core deterministic scoring and quote-extraction logic from our engine:
export interface SkillMatchEvidence {
skill: string;
foundInResume: boolean;
resumeQuoteEvidence: string | null;
category: 'hard' | 'soft';
}
function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// 1. Verify Hard Skills only if an exact sentence quote exists in the resume
export function verifyHardSkillsWithQuotes(
requiredSkills: string[],
resumeSentences: string[]
): { matched: SkillMatchEvidence[]; missing: string[] } {
const matched: SkillMatchEvidence[] = [];
const missing: string[] = [];
for (const skill of requiredSkills) {
const regex = new RegExp(
`(^|[^a-zA-Z0-9])${escapeRegExp(skill.toLowerCase())}([^a-zA-Z0-9]|$)`,
'i'
);
const evidenceSentence = resumeSentences.find((sentence) =>
regex.test(sentence.toLowerCase())
);
if (evidenceSentence) {
matched.push({
skill,
foundInResume: true,
resumeQuoteEvidence: evidenceSentence.slice(0, 180),
category: 'hard',
});
} else {
missing.push(skill);
}
}
return { matched, missing };
}
// 2. Extract quantifiable engineering/business impact (%, $, x, ms, users)
export function extractMeasurableMetrics(resumeText: string): string[] {
const metricRegex =
/\b(\d+(?:[.,]\d+)?\s*(?:%|x|k|m|\$|€|users|clients|ms|sec))\b/gi;
return Array.from(
new Set((resumeText.match(metricRegex) || []).map((m) => m.trim()))
).slice(0, 10);
}
// 3. Deterministic weighted ATS score formula
export function calculateATSScore(params: {
matchedHardCount: number;
totalHardCount: number;
matchedSoftCount: number;
totalSoftCount: number;
metricsCount: number;
structureChecksPassed: number;
totalStructureChecks: number;
}) {
const hardSkillsScore =
params.totalHardCount > 0
? Math.round((params.matchedHardCount / params.totalHardCount) * 100)
: 75;
const softSkillsScore =
params.totalSoftCount > 0
? Math.round((params.matchedSoftCount / params.totalSoftCount) * 100)
: 80;
const impactScore = Math.min(100, Math.round((params.metricsCount / 4) * 100));
const structureScore = Math.round(
(params.structureChecksPassed / params.totalStructureChecks) * 100
);
return Math.round(
hardSkillsScore * 0.55 +
softSkillsScore * 0.15 +
impactScore * 0.15 +
structureScore * 0.15
);
}
Zero-Hallucination Cover Letters & XYZ Rewrites
Once the deterministic pass finishes, we have two verified arrays: matchedHardSkills and missingHardSkills.
Only then do we invoke the LLM (temperature: 0.15, strict JSON schema) with an explicit constraint: never claim experience with any skill in missingHardSkills.
- Actionable XYZ Rewrites: It flags weak sentences without metrics and suggests concrete improvements.
-
One-Click Workflow: You can search live jobs (via Adzuna), click "⚡ ATS Scan & Cover Letter" on any job card, inspect your quote-verified score, and save the application to a built-in Kanban board (
Saved -> Applied -> Interview -> Offer).
You can test it live with your own resume here (no signup wall for core scanning):
👉 LaunchPad AI PRO
I'd love feedback from fellow devs: what other deterministic heuristics would you add to a resume parser?



Top comments (0)