Preparing your learning space...
40% through AI Engineering for FDEs tutorials
An LLM naturally returns free text, but your code wants data — a JSON object, a set of fields, a row to insert. Structured outputs are the techniques for forcing the model to return a predictable, parseable format instead of prose. This is the bridge between "nice text" and "usable software."
Free text is fine for reading but painful for code: parsing prose is fragile, and typos break your pipeline. When an LLM feeds a database, an API, or downstream logic, you need the reply in a known shape — fields with known names and types.
Why it's useful: structured output turns an unpredictable text generator into a dependable data source. You can .get("amount"), cast to int, and insert the result without guessing.
There are two ways to get structure:
| Prompted JSON | Schema-based | |
|---|---|---|
| Setup | Just write it in the prompt | Define a schema |
| Reliability | Model can deviate | API enforces it |
| Best for | Prototyping, simple shapes | Production, feeding systems |
For production, prefer schema-based where your provider supports it.
The lightweight approach: request JSON and specify the fields. Good for quick scripts, less safe for critical paths.
Extract order info and return ONLY JSON: {"order_id": int, "customer": string, "total": number}
Example output:
{"order_id": 8821, "customer": "Dana Reyes", "total": 149.50}
Explanation: naming exact fields and types guides the model. The risk is it adds a "Here is the JSON:" line or wraps it in backticks — which is exactly why parsing must be tolerant.
The robust approach: define a schema and let the API guarantee conformance. The model is constrained to output valid data matching your shape.
from pydantic import BaseModel
import anthropic
class Order(BaseModel):
order_id: int
customer: str
total: float
client = anthropic.Anthropic()
resp = client.messages.parse(
model="claude-sonnet-5",
max_tokens=300,
messages=[{"role": "user", "content": "Order #8821 for Dana Reyes, total $149.50."}],
output_format=Order, # structured output schema
)
order = resp.parsed_output # validated Order instance
print(order.order_id, order.total)
Explanation: Order declares the fields and types. messages.parse returns the reply already validated against that schema as resp.parsed_output, so you get a real typed object instead of raw text you have to trust and re-parse.
Best Practice: Use typed schemas (like Pydantic models) so the returned data becomes real typed objects, not raw dicts you have to trust.
Structured or not, always validate what comes back. Validation catches wrong types, missing fields, and nonsense values before they corrupt your database.
if order.total <= 0:
raise ValueError("Bad total from model")
if not order.customer:
order.customer = "UNKNOWN"
Explanation: the model can be confident and wrong. Validating ranges, presence, and types is a cheap guardrail that keeps bad data out of the rest of your system.
Best Practice: Validate at the boundary — the moment output enters your code — so a bad value can't flow silently downstream.
Even with good prompting, you'll get malformed or non-conforming replies. Have a plan, not a prayer:
try:
resp = client.messages.parse(
model="claude-sonnet-5", max_tokens=300,
messages=[{"role": "user", "content": order_text}],
output_format=Order,
)
order = resp.parsed_output
except Exception:
order = retry_parse(order_text) # re-prompt once, then fail clearly
Explanation: the `try` wraps parsing so a malformed reply doesn't crash the whole pipeline. The fallback re-prompts once; if it still fails, the caller knows — never silently store bad data.
---
Save your progress and earn XP for completing tutorials.
4 questions · Pass with 70%+
1Why are structured outputs useful for FDEs?
2What is the advantage of schema-based output over just prompting for JSON?
3With structured output, what does resp.parsed_output give you?
4A reply is perfectly formatted JSON but contains a negative total. What should you do?
Technology
Forward Deployed Engineer
Lesson group
AI Engineering for FDEs
Progress
40% complete