Skip to content

Structured Output & Format Control

"Return your response as JSON" is not enough. The model needs to know the exact schema, what each field means, and what to do when data is missing. Structured output turns a text generator into a reliable API endpoint. This lesson covers JSON mode, XML tags, markdown formatting, and programmatic validation with Pydantic.

Gnome mascot

What you'll learn

  • JSON mode (response_format) forces valid JSON, but you still need to describe the schema
  • XML tags (<finding>...</finding>) work well with Claude, which has strong XML parsing in training
  • Always validate the output against a schema before using it, Pydantic or a JSON Schema validator

Build it

JSON with schema description

Explicitly defining a JSON schema within the prompt ensures the model returns data in a predictable format.

python
prompt = """Analyze the following code. Return your response as JSON with this exact schema:

{
  "bugs": [
    {
      "line": int,
      "severity": "high" | "medium" | "low",
      "description": "string explaining the bug",
      "fix": "string with the suggested fix"
    }
  ],
  "style_issues": [
    {"line": int, "rule": "string", "fix": "string"}
  ],
  "summary": "one-sentence summary"
}

Code:
{code}"""

Programmatic validation

Using Pydantic allows you to enforce strict validation rules on the structured output returned by the model.

python
from pydantic import BaseModel, Field
from typing import Literal

class Bug(BaseModel):
    line: int
    severity: Literal["high", "medium", "low"]
    description: str
    fix: str

class CodeReview(BaseModel):
    bugs: list[Bug] = Field(default_factory=list)
    style_issues: list[dict] = Field(default_factory=list)
    summary: str

def review_code(code):
    response = call_llm(prompt.format(code=code))
    return CodeReview.model_validate_json(response)

XML tags (Claude-optimized)

XML tags provide a robust alternative to JSON for structuring output, particularly when working with models trained heavily on markup.

python
prompt = """<task>Analyze the following code</task>
<code>{code}</code>
<format>
For each bug found:
<finding>
  <severity>high|medium|low</severity>
  <line>number</line>
  <description>what's wrong</description>
  <fix>how to fix it</fix>
</finding>
</format>"""

What goes wrong

MistakeHow you notice itThe fix
Model returns invalid JSONjson.loads() or Pydantic raises an errorFeed the error back: "Your response was not valid JSON. Please fix: {error}"
Model returns valid JSON but wrong schemaMissing fields, wrong typesDescribe the schema explicitly. Use required fields. Validate with Pydantic
Model adds commentary outside the JSON"Here's my analysis: {json}"Add "Return ONLY the JSON object, no other text" to the prompt
JSON mode rejects valid-but-unusual valuesresponse_format is too strictUse Pydantic validation instead of API-level JSON mode for more flexibility

Confirm it worked

Validate the parsed output against your expected data models to catch hallucinations or schema violations early.

python
response = review_code("print('hello')")
assert isinstance(response, CodeReview)
assert isinstance(response.bugs, list)
assert isinstance(response.summary, str)

Next: Self-Consistency & Verification