RU Send your project
All articles

Structured outputs instead of parsing free text

Why you shouldn't scrape data out of model replies with regexes, how to define a schema, validate the result and what to do when validation fails.

· 4 min readAICode

The first version of almost every model-backed feature has a line like “reply in the format: Category: …, Priority: …”. Then the code splits the text on colons. It works for a week. Then the model replies “Sure! Here’s the result:”, adds asterisks for bold, or writes “Priority: urgent” instead of high, and the parser breaks without a sound.

The model isn’t the problem. You asked for text and got text. If your code needs data, ask for data.

Ask for JSON against a schema

Most major providers can return output that conforms to a JSON schema you supply, either through a dedicated structured-output mode or through a tool call whose arguments are defined by a schema. If that mode exists, use it instead of asking nicely in the prompt. It’s the difference between “please fill this in carefully” and a form with required fields.

The schema states exactly what you need:

{
  "type": "object",
  "properties": {
    "category": { "type": "string", "enum": ["refund", "delivery", "other"] },
    "priority": { "type": "string", "enum": ["low", "normal", "high"] },
    "order_id": { "type": ["string", "null"] },
    "summary":  { "type": "string", "maxLength": 200 }
  },
  "required": ["category", "priority", "order_id", "summary"]
}

A few habits that save time:

  • enums over free strings: if there’s a finite set of values, list them. Then “High,” “urgent” and “very important” never show up;
  • explicit null: if a value may be absent, allow null and say so in the field description. Otherwise the model will invent an order number just to fill the slot;
  • field descriptions: a short hint about what goes in a field works better than a long paragraph in the prompt;
  • flat structure: the deeper the nesting, the more places to go wrong. If one level will do, use one level.

Validate anyway

Even in strict mode, check the result in your own code. First, not every provider and model guarantees schema conformance equally. Second, a schema checks shape, not meaning: order_id can be filled with a number that never appeared in the input.

Validation has two layers:

  • shape: types, required fields, allowed values. Every language has libraries for this, and one schema can drive both the model request and the check;
  • meaning: business rules. The order exists and belongs to this customer, the amount isn’t negative, the date isn’t from the last century.

Whatever passes becomes ordinary typed data. Whatever fails goes no further, not into the database and not to the user.

When validation fails

Don’t re-ask forever. A sensible sequence:

  • one retry with the error: send the model its own output plus the specific validation error, such as “priority must be one of low, normal, high.” That usually does it;
  • a fallback: if the retry fails too, the feature should behave predictably: a default value, a “needs human review” flag, or an honest error in the UI;
  • a log entry: store every failure with its input and output. If one field fails regularly, the schema or its description is the problem, and you’ll only see that across accumulated cases.

Keep reasoning and data apart

Sometimes you want the model to think before it answers. That’s fine, but keep the reasoning in its own schema field or a separate step, not in prose wrapped around the JSON. Code reads the fields, a human debugging reads the reasoning, and nobody gets confused.

The upshot

Free text is for people, structure is for code. Define a schema, use the provider’s structured-output mode if it has one, validate shape and meaning, and decide up front what happens on failure. Then the model’s reply stops being a string nobody dares touch and becomes ordinary validated input.

SUBXCAT can move your product’s features from free text to validated schemas and find where parsing is quietly failing today.

Want us to do this for you?

Tell our assistant about your project — a rough estimate in five minutes, a review in 48 hours.

Book an intro call 20 minutes: an engineer, a designer and a marketer look at your project with you.
Book a call