Sections in this lesson

Structured Outputs

Validate a machine-readable response before using it in application logic.

Reviewed: · Content owner: Interview Preparator

What you will learn

  • Distinguish JSON syntax, schema and semantic validity.
  • Handle refusals, truncation and parse failure.
  • Keep authorization separate from shape validation.

Before you start

The flow

  1. 1.Output contract
  2. 2.Model output
  3. 3.Parse
  4. 4.Validate
  5. 5.Business rule

Three layers of validity

Valid JSON can have an invalid schema; a valid schema can still describe a false fact or forbidden action. Define required properties, enums, length bounds and unknown-field behavior. Validate business invariants after structural validation.

Provider-native schema features and prompt-based converters offer different guarantees and support different schema subsets. Check the selected model and API rather than assuming every provider behaves identically.

Failure is part of the interface

Handle empty output, safety refusal, invalid JSON and output truncation separately. Use a bounded repair attempt for recoverable formatting errors; do not execute partially parsed content.

Do not silently fill a missing permission or account ID with a privileged default. Return a typed application error or request clarification. Keep raw sensitive output out of logs.

Evolve the contract

Version schemas and retain fixtures from earlier releases. When adding a field, check whether old clients reject unknown properties or require a migration. The example manually validates a small contract so its failure cases are visible.

Try it

structured-outputs.mjs
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import assert from 'node:assert/strict';
// A small executable contract; production can use a JSON Schema validator.
function parseDecision(text) {
const value = JSON.parse(text);
if (value === null || typeof value !== 'object' || Array.isArray(value) ||
Object.keys(value).sort().join(',') !== 'action,reason' ||
!['ESCALATE', 'ANSWER'].includes(value.action) ||
typeof value.reason !== 'string' || !value.reason.trim()) {
throw new Error('Invalid decision');
}
return value;
}
assert.equal(parseDecision('{"action":"ESCALATE","reason":"Missing source"}').action, 'ESCALATE');
for (const invalid of ['null', '[]', '{}', '{"action":"DELETE","reason":"x"}',
'{"action":"ANSWER","reason":"x","admin":true}', 'not json']) {
assert.throws(() => parseDecision(invalid));
}
console.log('Valid shape; factual correctness and authorization still need separate checks.');

Run with Node.js 22: node examples/generative-ai/structured-outputs.mjs. This is a deterministic teaching example, not a live model call.

Interview questions

Does schema compliance prove truth?

No. Grounding, business rules and permissions require separate checks.

When should repair stop?

At a configured attempt/time budget, or immediately for a refusal or forbidden action.

Why reject unknown fields?

To avoid accidentally consuming unsupported or attacker-influenced parameters; compatibility needs an explicit version policy.

Practice

Add a schemaVersion field and a non-empty evidenceIds array for ANSWER decisions.

Check your solution

  • Malformed JSON and null are rejected.
  • ANSWER without evidence fails.
  • Valid structure still passes through authorization.

Official references

Continue learning

Category sequence

Generative AI

Lesson 4 / 8

Was this lesson useful?

Change notes: Initial bilingual lesson and executable example.