DEV Community

Salmon Joy
Salmon Joy

Posted on

Structured outputs as application contracts

Your LLM Output Should Have a Contract, Not a Vibe

So there I was, an AI engineer at a fintech startup, trying to turn a GPT‑4 chat into a credit‑score suggestion engine. The user typed “I just got a promotion, how much should I loan?” and the model spat back a paragraph that looked like poetry: “Congrats! Maybe $5‑10k could work, but keep your debt‑to‑income ratio low…” I tried to yank the numbers out with a regex like /\$(\d+)-(\d+)k/. Spoiler: it broke the moment someone said “$5‑10 k” with a thin space or used “5‑10k” without the dollar sign. One mis‑typed hyphen and my downstream service crashed, mis‑classifying a safe applicant as risky. It felt like trying to catch a greased pig with a colander.

That’s when I discovered the power of structured outputs as application contracts. Instead of hoping the model “gives me what I need”, I told it exactly what shape the response must have – a JSON object that matches a JSON Schema and a typed TypeScript interface. The prompt became:

{
  "loan_range": { "min": int, "max": int },
  "confidence": "high" | "medium" | "low"
}
Enter fullscreen mode Exit fullscreen mode

Now the model can’t accidentally sprinkle a poem in there; it either obeys or says “I’m sorry, I can’t comply”. That refusal is a feature: my code checks if (response.refusal) … and falls back to a safe default.

Why is this better than regex? Regex is a brittle detective that looks for patterns in free text. Anything outside the pattern – extra whitespace, emojis, a different phrasing – sends it into a dead end. A schema validator, on the other hand, parses a proper data structure and instantly tells you which fields are missing or of the wrong type. It’s like checking a passport instead of eyeballing a face.

In my edutech side‑project, we needed a list of quiz questions with “question”, “options”, and “answer”. By versioning the schema ("$schema": "http://json-schema.org/draft-07/schema#", "title": "QuizV2"), we could roll out a new field “explanation” without breaking older clients. Downstream safety checks – e.g., “no option string longer than 200 chars” – are baked into the schema, so the LLM can’t hand us a monster answer that blows up the UI.

A quick case study: after switching to structured outputs and enabling refusals, our fintech loan‑approval pipeline’s error rate dropped from 12% to under 1%. The only time we got a refusal was when the user asked for advice on illegal activity, and we gracefully returned a polite error message.

Bottom line: treat the LLM like a junior dev who follows a contract, not a poet who vibes with your mood. Define a JSON Schema, generate typed interfaces, validate, version, and let refusals be your safety net.

Got a funny regex story or a schema win? Drop a comment – I’d love to hear how you’re taming your LLMs!

If you are someone who loves to know the technical work and architecture design I have shared more details based on my experience on this here: https://github.com/SalmonJoy/My_guide_for_building_AI_systems/blob/main/Structured_outputs_as_application_contracts.md

Top comments (0)