Dev.to AI 🤖 Ai 👁 0 📖 4 min read

How I stopped my AI resume writer from inventing achievements

Ask most AI resume tools to "improve" a bullet like "worked on the website" and you'll get something like "Spearheaded a website redesign that boosted conversions by 35%." It sounds impressive. It's also made up. And th

Ask most AI resume tools to "improve" a bullet like "worked on the website" and you'll get something like "Spearheaded a website redesign that boosted conversions by 35%."

It sounds impressive. It's also made up. And the first time an interviewer asks "how did you measure that 35%?", it falls apart.

I recently built a free resume builder, and the one rule I cared about most was: the AI may reword what you wrote, but it may never add facts. Here's how I tried to enforce that, what I tested, and a strange PDF bug I found along the way.

The problem: LLMs are trained to be "helpful"

Language models are very good at producing text that sounds like a strong resume. Strong resumes have numbers, tools and outcomes, so when a model sees a vague bullet, the most "resume-like" completion includes a number, a tool and an outcome, whether or not they're true.

So "be helpful" and "be honest" pull in different directions. You have to make the honest option the only acceptable one.

Layer 1: Tell the model exactly what it can't do

Vague instructions like "don't make things up" aren't enough. I listed the specific kinds of invention I'd seen:

const HONESTY_RULES = `Rules you must follow:
- Only use facts the user provided. Never invent employers, job titles, dates, numbers,
  percentages, tools, technologies, awards or achievements.
- If a bullet has no measurable result, do not add one. Make it clear and specific
  with the facts given instead.
- Do not use placeholders like [X%] or "N users".
- Write in plain, professional English. No buzzwords like "synergy", "rockstar"
  or "results-driven".`

Two details mattered more than I expected:

  • Banning placeholders. Without that line, models "politely" add [X]% for you to fill in, which nudges users to invent a number themselves.
  • Saying what to do instead. "If a bullet has no measurable result, make it clear and specific with the facts given" gives the model a path that isn't "add a number".

Layer 2: Check the output's structure in cod

Prompts are guidance, not guarantees. So the cof the response before showing it to the user:

const out = await complete(systemPrompt, JSON.stringify({ role, company, current, bullets }))
const improved = (Array.isArray(out.bullets) ?
  .map((b: unknown) => clean(b, 600))
  .filter(Boolean)

// One output per input, or it's not a safe dro
if (improved.length !== bullets.length) throw new Error("Unexpected AI response")

Requiring **exactly one rewritten bullet per ins two useful things:

  1. The model can't sneak in an extra "achieveme
  2. The model can't merge or drop bullets, so the user can compare each one before and after.

If the check fails, the user sees an error instead of a questionable rewrite. I'd rather fail loudly than show something plausible but wrong.

I also use JSON output mode (`response_format: so parsing is reliable, and a low temperature
(0.4) to keep rewrites conservative.

Layer 3: Test it with inputs that tempt invention

The best test cases are vague bullets that almost beg for a made-up number:

Input Output
worked on the website Developed features for the website.
helped with customer support tickets Assistickets.
fixed bugs in the checkout page using React Resolved bugs in the checkout page using React.
made the product page load faster Optimizeder loading.

No numbers, no new tools, no invented outcomes.and that's the point: they're clearer, but still
true.

I also ran an automated check that flags any output containing digits, % or technology names that weren't in the input.
It's crude, but it catches the most common kind

The UI still tells users to **read the result bt is perfect, and the person whose name is on
the resume should have the final say.

Bonus bug: why "SKILLS" became "SKI L LS"

The PDF export is simply the browser's print-to-PDF of a single-column HTML preview. That gives real, selectable text, which matters because many employers run resumeng systems (ATS) that read the text.

To check that, I extracted the text from a gene

text
EDUCATION
SKI L LS
CERTI F ICATIONS

The cause was my section headings: uppercase wi`. With wide tracking, PDF text extraction
sometimes decides the gaps between letters are word breaks. A human sees "SKILLS"; a parser may see three words.

The fix was one line: remove the letter-spacing from the headings (and add a thin divider so they still stand out). After
that, extraction gave clean "SKILLS" and "CERTI

Lesson: **if machines will read your output, te, not just what it looks like.

Keeping it free without runaway costs

Every AI action costs money, so there's a limit per day. I store the counter in the user's
server-only metadata (Supabase app_metadata, which users can't edit), and only successful actions count against the
limit:

const usage = aiUsageToday(user)
if (usage.remaining <= 0) {
  return NextResponse.json({ error: "You've used all 20 AI actions for today." }, { status: 429 })
}
// ...call the model...
const ai = await recordAiUse(user) // only afte

Editing, saving and PDF export aren't limited at all. Only the part that costs money is.

What I'd tell anyone building AI writing features

  1. List the specific ways the model tends to go wrong, not just "be accurate".
  2. Give the model an honest alternative to
  3. Validate structure in code. Prompts guide; code enforces.
  4. **Test with inputs designed to tempt failuremples.
  5. Test what machines see, whether that's parsed PDFs, screen readers or search crawlers.

If you want to try it, the resume builder is free at nokku.payanai.com/resume-builder. I'd genuinely like to hear if you can getit to invent something. That's the kind of bug report I want.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.