Structured Outputs in Practice
메뉴

AI Backend

Structured Outputs in Practice

Turning model text into versioned, validated API output contracts.

Structured Outputs in Practice hero image

Create model output as a JSON Schema contract

In this article, we summarize how to use Structured Outputs to handle LLM responses as JSON Schema-based contracts.

The first question you encounter when attaching an LLM response to a service is not “Is the answer plausible?” “Can the backend parse it?”

If your model answers in free text, your frontend, DB, and subsequent APIs won't be able to use it reliably. In particular, tasks that require structured results, such as document summarization, information extraction, classification, routing, and tool call preprocessing, require model outputs to be contracts.

Analysis base date: 2026-05-12 Practice standard environment: OpenAI API, Python, Pydantic, FastAPI Main reference materials: OpenAI Structured Outputs Docs, JSON Schema, Pydantic

Key takeaways

  • Structured Outputs is a function that makes model responses follow the specified JSON Schema.
  • LLM responses must be treated as contracts like API responses within the service.
  • Without a schema version, it is difficult to track changes in prompts and response structures.
  • Validation failure should be viewed as a normal operating scenario and a retry/fallback should be designed.
  • Instead of leaving all fields to the model, the fields to be calculated by the server should be filled in by the server.

1. Why Structured Outputs?

Free text responses are great for humans to read, but difficult for systems to process.

For example, let's say you made the following request to the model:

# This is an example.Summarize the main content of this document and include sources and credibility.

The model can answer in many different ways.

# This is an example.The key point is that Redis cache reduces DB load. The source is the Redis official document.

or:

# This is an example.{"summary": "Redis cache reduces DB load","source": "Redis Docs"}

Services hate this volatility. The frontend expects an array ofcitations, but if the model gives it the stringsource, it breaks.

2. Difference between JSON mode and Structured Outputs

JSON mode helps you generate valid JSON. However, this is different from necessarily adhering to a specific schema.

Structured Outputs focuses on ensuring that the response follows the JSON Schema defined by the developer.

divisionJSON modeStructured Outputs
targetJSON format outputJSON Schema compliance
Ensure required fieldsweaknessstrong
enum constraintsweaknessschema based
API contract compliancelimitedheight

In practice, schema-based output is safer than JSON mode.

3. Response schema design principles

A good schema doesn't leave everything to the model.

principleexplanation
Leave only the fields you needExcessive fields increase the failure rate
Actively use enumsEnsure stability in subsequent quarters
Exclude server calculated fieldsToken usage, trace_id, etc. are filled in by the server.
Use nullable carefullyNullability increases subsequent processing complexity
put versionSchema change tracking

4. Document Q&A response schema example

// This is an example JSON structure.{  "type": "object",  "properties": {    "answer": {      "type": "string",      "description": "Answers to user questions"    },    "confidence": {      "type": "string",      "enum": ["high", "medium", "low"]    },    "citations": {      "type": "array",      "items": {        "type": "object",        "properties": {          "document_id": { "type": "string" },          "chunk_id": { "type": "string" },          "quote": { "type": "string" }        },        "required": ["document_id", "chunk_id", "quote"]      }    },    "needs_human_review": {      "type": "boolean"    }  },  "required": ["answer", "confidence", "citations", "needs_human_review"],  "additionalProperties": false}

This schema clearly separates answers, their credibility, source, and whether they require review.

5. Validation and fallback

Even when using Structured Outputs, backend validation is required. Output from external systems should always be verified.

# This is example code.from pydantic import BaseModelfrom typing import Literal # This declaration shows an example flow.class Citation(BaseModel):    document_id: str    chunk_id: str    quote: str # This declaration shows an example flow.class AnswerOutput(BaseModel):    answer: str    confidence: Literal["high", "medium", "low"]    citations: list[Citation]    needs_human_review: bool

Failure Scenario:

# This is an example.1. Model response schema validation failed2. Retry once3. If it still fails, return a fallback response4. Save failure cases in eval/debug dataset

Fallback example:

// This is an example JSON structure.{  "answer": "Could not reliably generate answers. Please be a little more specific with your question.",  "confidence": "low",  "citations": [],  "needs_human_review": true}

6. Schema versioning

Response structures change over time. Therefore, schema version is required.

# This is an example.answer.v1answer.v2classification.v1extraction.receipt.v1

The schema version must be recorded along with the prompt version.

record entryreason
schema_versionResponse structure tracking
prompt_versionTrack prompt changes
modelQuality comparison by model
validation_resultFailure rate analysis

7. Separate DB storage and API response

There is no need to make the model output schema and API response schema exactly the same.

Model output:

// This is an example JSON structure.{  "answer": "...",  "confidence": "high",  "citations": []}

API response:

// This is an example JSON structure.{  "answer_id": "ans_123",  "answer": "...",  "confidence": "high",  "citations": [],  "usage": {    "input_tokens": 1000,    "output_tokens": 300  },  "trace_id": "0af..."}

usage,trace_id, andanswer_idare values ​​added by the server.

8. Practice risks

riskreact
Schema is too complexStart with a small schema
enum does not contain realityunknown or needs_review design
Model fabricates originCitations are limited to selection only by search result chunk_id
Client crashes when changing schemaversioning and migration
View validation failure only as a failureCollected as normal operating indicators

9. Practical checklist

# This is an example.[ ] Did you distinguish between model output schema and API response schema?[ ] Are the required fields clear?[ ] Have you restricted additionalProperties?[ ] Are enum values ​​connected to subsequent logic?[ ] Do you save schema_version?[ ] Is the validation failure rate recorded as a metric?[ ] Is there a retry/fallback policy in case of failure?[ ] Did you prevent the model from randomly generating citations?

Failure case: JSON received but no contract

Using Structured Outputs does not immediately create a stable API contract. The most common failure is when you create a schema but don't have a version, validation, or fallback policy. The model returns JSON, which the parser also passes, but the meaning may not be what the downstream service expects. For example, if it is not documented what thresholdrisk: "medium"means, the UI, DB, and alert rule interpret it differently.

Another problem is leaving all the fields to the model. Some values, such ascreatedAt,sourceId,confidenceScore, andneedsReview, must be calculated by the server. If you trust the timestamp or internal ID filled in by the model, data consistency will be broken. Structured Outputs is a tool for controlling output format, not a replacement for business rule validation.

Example implementation: schema version and server calculated fields

type ExtractionV1 = {  schemaVersion: "customer_intent.v1";  intent: "refund" | "upgrade" | "bug_report" | "unknown";  evidence: string[];  modelConfidence: number;}; type StoredExtraction = ExtractionV1 & {  requestId: string;  validatedAt: string;  needsReview: boolean;};

The model only producesExtractionV1. The server calculatesrequestId,validatedAt,needsReview. In particular,needsReviewmust not only look at model confidence, but also look at server information such as user rating, sensitive keywords, and previous failure history.

Comparison table: output formats and operating agreements

itemWhat Structured Outputs can help you withWhat the server must continue to do
JSON formatGenerate response matching schemaHandling parsing failures and retries
enumAllowed value limitsDefine the business meaning of the value
required fieldReduce missing fieldsRecalculate Untrusted Values
schemastructural fixationCompatibility with version migration

Based on this table, Structured Outputs are at the front of the validation layer. The backend still needs to validate external input and check for business invariants before saving. This ensures that model replacement or prompt changes do not directly lead to downstream failures.

Practical application example: storing inquiry classification results

Let's say you want to create a function that classifies customer inquiries into intents such as refund, billing, bug, and account. It is best to keep the model output schema small.

{  "schemaVersion": "ticket_intent.v1",  "intent": "billing",  "evidence": ["invoice number", "charged twice"],  "confidence": "medium"}

Here, the server separately addsrequestId,receivedAt,customerId, andneedsHumanReview. In particular,needsHumanReviewis not determined by model confidence alone. Sensitive intents such as refunds, personal information, and payment failures can be sent for review even if the confidence level is high.

A failure is to make your schema too large too quickly. If you receivedepartment,assignee,refundAmount,legalRisk, andreplyDraftat once from the beginning, it is easy to mix up the business meaning even if the model format matches. Separating the classification schema and the answer draft schema reduces the scope for failure. Even if the classification is incorrect, answer generation may not start, and even if the answer draft fails, a classification log may be left.

The comparison criteria are simple. What Structured Outputs guarantees is that “the response is close to the schema shape.” What the service must guarantee is that “the value matches the current user, permissions, DB state, and policy.” Separating the two prevents you from running the wrong subsequent API just because you received JSON.

10. Q&A

Q1. If I use Structured Outputs, can I avoid using validation code?

no. Validation still needs to be done in the backend. It is fundamental not to trust external system results.

Q2. Should all LLM responses be JSON?

no. The final answer you show your users might be Markdown. However, it is a good idea to structure the values ​​that the system must subsequently process.

Q3. What if my schema gets too big?

It's better to break it down into smaller pieces. Separate the extraction, classification, response, and review needs into step-by-step schemas rather than putting them into one large schema.

11. References and uncertainty

References

uncertainty

  • Supported models and detailed API parameters may change over time.
  • Performance and failure rates of complex schemas require real-world testing.

댓글

GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.

TOP