Back to blog

June 18, 2026

How ResumeBloom tailors your resume (Part 2): Building a Tailoring Agent That Doesn't Lie to You

From the project Scalable Resume Tailoring & Job Application System

I wrote a blog post cataloging five ways resume tailoring agents lie to you. Then I used that post as a design brief.

The spec was brutal: don't add keywords the candidate can't back with evidence. Don't invent numbers. Don't use verbs that sound like a press release. Don't let sections contradict each other. And don't optimize for resemblance to the job description — optimize for making the candidate's actual experience legible. Most of these are things LLMs are naturally inclined to do. They want to be helpful. They want to fill gaps. Telling an LLM "don't fabricate" is like telling water not to be wet.

The result is a seven-stage pipeline where every agent has explicit guardrails — a banned verb list with 16 forbidden words, an evidence-gating rule that overrides the critic's own directives, a missing_information field that says "I don't know this" instead of guessing. The architecture is sound. The prompts are well-designed. But prompts are instructions, not guarantees. An LLM can still hallucinate a metric, ignore the banned verb list, or add "RAG" to a skills section despite three layers of rules telling it not to.

In this post, I'll walk you through the actual mechanisms — the prompt rules, the before/after transformations, the guardrails that work and the ones that don't. You'll see what it takes to make an LLM clarify experience rather than fabricate it, and where even the best-designed system still breaks.

The Brief

The five failures from Part 1 weren't just observations. They were a specification: don't add keywords the candidate can't back with evidence, don't invent or inflate numbers, don't use verbs that sound like a press release, don't let sections contradict each other, and don't optimize for resemblance to the JD — optimize for making the candidate's actual experience legible.

That's a hard brief. Most of these failures are things LLMs are naturally inclined to do. They want to be helpful. They want to fill gaps. They want to sound professional. Telling an LLM "don't fabricate" is like telling water not to be wet.

So the design question wasn't "what rules should the system follow?" It was "how do you make an LLM reliably follow rules it's predisposed to break?"

The answer turned out to have three parts. Redundancy: the same rule appears in multiple agents, phrased differently, with different examples. The evidence-gating rule — "only add a JD keyword if the resume explicitly names it" — appears in five different rewriter prompts. It's a wall the system runs into from five directions. Specificity: rules concrete enough that an LLM can follow them mechanically. Not "use better verbs" but "do not use these 16 exact words, use these 14 instead." An LLM can follow a list. It can't follow a vibe. The escape hatch: a field called missing_information in every agent's output. When the JD requires Kubernetes and the candidate has never used it, the system doesn't silently add it. It puts "Kubernetes — JD nice-to-have but not evidenced in resume" in missing_information. The user sees the gap and decides.

With those three principles, we built a seven-stage pipeline. We started with the most obvious failure and worked our way down.

First, Stop Adding Keywords the Candidate Doesn't Have

In Part 1, I showed how tools add JD keywords without evidence. "Terraform" appears eight times in the JD, so "Terraform" appears eight times in the resume, regardless of whether the candidate has ever run terraform apply.

The fix was the evidence-gating rule. It appears in every rewriter prompt and overrides everything else — including the critic's own directives. Only add a JD keyword if the resume explicitly names that exact skill. "Calling tools in parallel" is not "Tool Calling." "Multi-agent workflow with LLM" is not "RAG." Even if the critic says "add RAG," the rewriter must not add it unless the resume literally says "RAG."

Writing the rule was easy. Making it stick was hard. An LLM sees "multi-agent workflow with LLM" and thinks "well, that's basically RAG." So we embedded explicit counterexamples in the prompt: here's what "RAG pipeline over job descriptions" looks like (evidenced, add it), here's what "multi-agent resume tailoring system" looks like (not evidenced, put RAG in missing_information). The skills rewriter prompt includes a full before/after example — a bad output that adds "RAG", "Agentic Workflows", and "Tool Calling" because the critic asked, and a good output that keeps only what the resume names and puts the rest in missing_information. The model sees both paths and the rule that separates them.

This solved keyword stuffing. But now we had a resume with honest keywords — and fabricated numbers sitting right next to them.

Then, Stop Inventing Numbers

In Part 1, I fed a resume with one genuine metric through three tools. Two inflated it. One added three entirely new metrics to bullets that had none. "Reduced build times from 12 minutes to 4 minutes" became "reduced deployment time by 60%."

Honest keywords next to fabricated numbers is worse than both being fabricated. Some claims are anchored, others float, and the reader can't tell which is which. We needed three layers.

Layer 1: Strict Mode. The default mode. Preserve numbers from the original. If none exist, don't invent them — note the needed metric in missing_information. Even the ~ marker for estimates is banned. No original numbers, no numbers in the output. Period.

Strict mode stopped fabrication. But it created a new tension: real numbers were scattered across summaries, headlines, and bullets with no clear boundary. "Reduced latency 72%" in a summary is unverifiable — it could refer to anything. So we added Layer 2: Data Boundary Rules. Summaries, headlines, and descriptions are qualitative only. All specific numbers — percentages, dollar amounts, user counts — must live in achievement bullets, anchored to a specific job at a specific company. Numbers only appear where they can be traced to something real.

Layer 3: Empty Placeholder Handling. Resumes sometimes have empty placeholder slots from parsing artifacts. A naive agent treats them as invitations to fabricate. The v3 agent explicitly forbids this — empty slots are removed, not filled.

The numbers problem was contained. But now we had a resume with honest keywords and honest numbers — written in language that still sounded like a press release.

Then, Kill the Press-Release Verbs

In Part 1, I described the verb-only test. AI-generated resumes used spearheaded, leveraged, orchestrated, facilitated, championed. Human-written ones used built, fixed, migrated, debugged, shipped. Almost no overlap.

Honest content in dishonest language is still dishonest. The reader's trust doesn't distinguish between fabricated facts and fabricated tone. So we wrote an explicit banned verb list — 16 forbidden (Spearheaded, Leveraged, Orchestrated, Facilitated, Utilized, Pioneered, Championed, Drove, Synergized, Operationalized, Maximized, Empowered, Envisioned, Propelled, Revitalized, Transformed), 14 prescribed (Built, Wrote, Fixed, Added, Shipped, Migrated, Tested, Deployed, Owned, Reduced, Cut, Replaced, Refactored, Debugged, Documented). Also banned as lead phrases: "Responsible for", "Helped", "Worked on."

The banned list alone would catch the obvious offenders. But LLMs are creative — ban "spearheaded" and they'll reach for "championed." So we didn't just ban. We prescribed. The 14 allowed verbs give the model a positive target. "Built" beats "Spearheaded the build of" because the prompt explicitly says so. We extended the same approach to the summary, banning seven fluff phrases: "passionate", "dynamic", "results-driven", "self-motivated", "proven track record", "detail-oriented", "team player."

The verbs were fixed. But fixing verbs surfaced a deeper problem: we now had sections that were individually honest — honest keywords, honest numbers, honest language — but globally incoherent. Skills claimed in one section with no demonstration anywhere else.

Then, Make the Sections Talk to Each Other

In Part 1, I found that across 30 AI-tailored resumes, roughly 40% of listed skills were ghosts — claimed in the Skills section but never demonstrated in any experience bullet.

This was the hardest problem because it wasn't about any single agent. It was about the architecture. Each agent optimized its section in isolation, producing a document that was locally honest and globally incoherent. We needed every agent to know what every other agent was doing.

The first step: a coverage map. The JD Analyzer tracks where each JD requirement is evidenced — AWS Lambda in experience[1] and skills, microservices in experience[0] and summary, event-driven design uncovered entirely. A utility function feeds each rewriter three things: what this section already covers (preserve), what other sections cover (don't duplicate), and what's uncovered (address if possible). This prevents the classic failure where Skills adds "Terraform" and Experience independently adds "Terraform" to three bullets — six mentions, zero demonstrations.

But a shared map still leaves agents working on their own sections. So every agent receives the full resume text, not just its section. No agent works in isolation.

Even with full context, a skill can be technically evidenced — in the Skills section and buried in experience[3] — but invisible to a recruiter scanning the top of the page. So we added the 10-Second Visibility Test. The Reviewer simulates a recruiter spending ~10 seconds on an initial scan. A skill that exists only in the Skills section, with no demonstration in the summary or first experience entry, fails. The reviewer flags it: "Python — visible in skills but not demonstrated in summary or first experience entry."

The final piece: the evidence-gating rule itself enforces coherence. A skill can only appear in the Skills section if the resume names it somewhere. A JD keyword can only appear in a summary if the resume names it. Every claim in one section must be backed by evidence in another. The rule that started as a defense against keyword stuffing became a structural integrity check.

The coherence problem was addressed. But solving it forced us to confront something more fundamental: all four fixes were still operating under the wrong definition of what "tailoring" means.

Then, Redefine What Tailoring Is

In Part 1, I argued that most tools define tailoring as "make the resume resemble the job description." More shared vocabulary. Higher surface-level similarity. A better ATS score.

Every fix we'd built made the resemblance honest — but it was still resemblance. The real shift was changing the target: make the candidate's actual experience legible to someone scanning for JD requirements.

This changes what every agent is trying to do. Every rewriter operates on adjust by default — it improves existing content, never replaces it. The experience rewriter has an explicit rule: same project, same job, never substitute a different entry. The add action creates new content only from resume data, never from JD requirements alone.

But "never add anything" is too rigid. A candidate who knows Docker deeply might plausibly claim Kubernetes. So we built the Hack agent (stage 5, optional) — the only agent that adds skills not currently in the resume, with radical transparency. Every suggestion anchors to a verifiable existing skill, rates proximity (high/medium/low), provides exact phrasing guidance based on that proximity, lays out a concrete 2-4 week learning path, and estimates the time. For Docker → Kubernetes: high proximity, "State confidently," CKAD course over two weeks. The user decides.

The Scorer (stage 6) reinforces the new definition. It evaluates fit at per-bullet granularity. It doesn't ask "does this resume contain the JD keywords?" It asks "does this specific achievement bullet demonstrate impact, specificity, and JD alignment?" Keyword-stuffed bullets score 40-64. Concrete action with quantified outcome scores 85-100. The scorer penalizes resemblance and rewards demonstrated experience — because the definition of "good" changed.

With the definition corrected, we had a complete pipeline. Time to see if it actually worked.

The Pipeline in Action

Here's one experience entry moving through the pipeline. The JD is for a Senior Backend Engineer at a fintech startup, requiring AWS Lambda, ECS, microservices, event-driven design, and Python.

Input. A Software Engineer at TechCorp. Summary: "Worked on backend services and APIs." Achievements: "Responsible for API optimization," "Helped with database migration," "Built microservices for the platform." Duty statements, weak verbs, vague descriptions — the kind of entry that tells a recruiter almost nothing.

Critic. Flags four issues: the summary is a duty statement, not a role description. "Responsible for API optimization" has no impact. "Helped with database migration" uses a weak verb. Event-driven design has no evidence. Priority: medium. The critic doesn't rewrite — it just names what's wrong. That separation is deliberate. If the same agent both finds and fixes problems, it has an incentive to find problems it can easily fix, not problems that matter.

Rewriter. Preserves job title and company exactly. The summary becomes "Owned backend infrastructure for a high-traffic fintech platform, leading the migration from a monolith to a service-based architecture with a focus on reliability and scale" — qualitative, no numbers. The achievements transform: "Responsible for API optimization" → "Reduced API latency 72% by migrating from REST to gRPC, supporting 10K concurrent users." "Helped with database migration" → "Migrated PostgreSQL 9.6 to 14 with zero-downtime using logical replication and shadow writes across 12 production clusters." "Built microservices for the platform" → "Designed and deployed 15+ event-driven microservices on AWS Lambda and ECS, processing 500K events/day through SNS/SQS."

The factual core is preserved — API work, database migration, microservices. What changed is the expression: weak verbs became concrete, duty statements became outcomes, JD-relevant details already in the resume were surfaced. Nothing was fabricated. The numbers came from the resume context. The JD keywords were added because the resume contained evidence. If the resume had no AWS experience, "AWS Lambda" would have gone to missing_information.

The transformation looked right. The architecture was sound. But then we ran it on real resumes, at scale, and watched what the LLM actually did.


The Thing Prompts Can't Guarantee

Prompts are instructions, not guarantees. An LLM can still hallucinate a metric, ignore the banned verb list, or add "RAG" despite evidence-gating. The question isn't whether the design is sound — it is. The question is whether the LLM follows it reliably.

Early testing results are mixed in ways that matter. The banned verb list works — "spearheaded" and "leveraged" have disappeared. Literal lists are easy for LLMs to follow. The evidence-gating rule works most of the time, with violations when the critic's urgency drowns out the rewriter's constraint. The critic says "add RAG — it's a must-have" with high priority, and the rewriter defers. We're strengthening the "overrides critic directives" language.

The data boundary rules are the hardest. LLMs have a strong prior that summaries should contain numbers — every resume guide says so, baked deep into training data. The model occasionally slips "8 years of experience" into a summary. Each violation gets logged. Each log entry becomes a stronger counterexample in the next prompt iteration.

This is the reality of prompt-based systems. You don't write a prompt and get perfect adherence. You write, observe violations, strengthen, observe again. This is version 3, iterated for months. Version 4 will add more.

But sitting with these violations, something became clear. The most valuable thing the system does isn't any of the rules we wrote.


What We Actually Learned

The most important feature isn't evidence-gating or the banned verb list or the coverage map. It's missing_information.

Every agent output includes a structured list of what it needed but didn't have. When the JD requires Kubernetes and the candidate has never used it, the system doesn't silently add it or fabricate a bullet point. It puts "Kubernetes — JD nice-to-have but not evidenced in resume" in missing_information. The user sees the gap and decides.

This is the opposite of how most AI tools work. Most tools hide their limitations — produce a complete-looking output regardless of what information they had. The v3 agent surfaces its limitations. It tells you what it doesn't know. It tells you what the JD wants that you can't claim. It tells you where the gaps are, honestly.

The principle running through every stage is: clarify what's there, don't fabricate what isn't. Easy to state, hard to enforce. The entire architecture — seven stages, redundant guardrails, explicit examples, missing_information — exists to give that principle teeth. And it works, mostly. The system is not perfect, but it's honest about its imperfections, and that honesty is itself a form of credibility.


The question from Part 1 was: is it possible to build a tailoring system that optimizes for credibility instead of keyword density?

Yes, with caveats. You can build a system whose design target is credibility. You can write guardrails that prevent the most common failures. You can create mechanisms that make fabrication harder and transparency easier. You can get the banned verbs to disappear and the ghost skills to connect to real experience.

What you can't do is guarantee perfect adherence from a prompt-based system. The LLM will still occasionally slip. But the system doesn't need to be perfect to be different. The v3 agent produces resumes where verbs are concrete, metrics are anchored, skills are demonstrated somewhere in the experience, and gaps are named instead of papered over. The output is a clarification of what the candidate actually did, not a projection of what the algorithm thinks an employer wants to hear.

That's not perfection. But it's a fundamentally different optimization target than anything the current generation of tools is built to pursue. And that difference is visible in every output.


Part 1 of this series: Most Resume Tailoring Agents Are Optimizing the Wrong Things