Production systems need machine-readable output every time — not "usually". This is how.
Always climb as high on this list as your provider supports.
1{
2 "type": "object",
3 "properties": {
4 "sentiment": { "type": "string", "enum": ["positive","negative","mixed"] },
5 "bugs": { "type": "array", "items": { "type": "string" } },
6 "priority": { "type": "integer", "minimum": 1, "maximum": 5 }
7 },
8 "required": ["sentiment", "bugs", "priority"],
9 "additionalProperties": false
10}The model is constrained during decoding to emit only tokens that keep the output valid against this schema. Enums, types, and required fields become guarantees, not hopes.
Schema enforcement controls structure, not semantics. The model can still put the wrong value in a correctly-typed field. You still need:
"description")null, or an "unknown" enum member)1Extract the fields per the schema.
2If sentiment is unclear, use "mixed".
3If no bugs are mentioned, return an empty array — never invent bugs.Belt and suspenders: validate the parsed object against the schema in code (e.g. with a validator like Zod/JSON-Schema). On failure, either repair-prompt ("Your output failed validation: <error>. Return corrected JSON only.") or fail safe.
| Anti-pattern | Why it hurts |
|---|---|
| Deeply nested 6-level schemas | Lower accuracy; flatten where possible |
| Free-text field that code parses | Defeats the purpose — make it structured |
| No "unknown"/empty representation | Forces the model to hallucinate a value |
Takeaway: Constrain the structure with schemas, steer the content with the prompt, and validate in code. All three, every time.