How I stopped an LLM from inventing things on people's CVs
I'm building Cveelo, a tool that turns a CV into a portfolio site. The first problem was obvious: if you let a model "help" with someone's CV, it will happily add a degree they never earned. Here is how I made the extraction step boring and safe. The rule: the model reads, it never writes The AI st

I'm building Cveelo, a tool that turns a CV into a portfolio site. The first problem was obvious: if you let a model "help" with someone's CV, it will happily add a degree they never earned. Here is how I made the extraction step boring and safe.
The AI step has one job, to extract what is in the CV into a typed profile. It never designs the site and never rewrites achievements. The design is done by hand, by people.
The schema makes every optional field nullable, with no default that looks like real data:
const Experience = z.object({ company: z.string(), role: z.string(), start: z.string().nullable(), end: z.string().nullable(), current: z.boolean().nullable(), description: z.string().nullable(), }); Enter fullscreen mode Exit fullscreen mode (This is simplified. The real schema is larger.) If a date isn't in the CV, the model has a legal way to say so instead of guessing.
I send the CV text with structured outputs bound to the schema. I then parse the result with Zod again on my side. If it fails, I retry once, and after that the upload is marked as failed. Nothing unvalidated is ever saved.
A CV is user-supplied text, so it can say "ignore previous instructions". The prompt tells the model to treat everything in the CV as data. I also test this with a CV that contains injected instructions and check that the output doesn't change.
A scanned PDF has no text layer. Sending empty text to a model is a good way to get fiction back, so I detect it and ask for a text-based CV. There's no OCR yet, on purpose.
Contact details and raw CV text don't go to the decision layer or to product analytics. The decision step only sees a compact summary.
[Add one or two honest mistakes you actually made here. Readers trust these more than anything else in the post.]
If you want to try it, it's free right now: cveelo.com. I'd like to hear how you'd break it.