DEV Community

Wade Allen
Wade Allen

Posted on

Stop hand-parsing LLM JSON: structured outputs with pydantic-ai

If you have ever written json.loads(response) around an LLM call and then a defensive try/except because the model returned

```json

fences, a trailing comma, or prose before the object — this is for you.

The fix is to stop treating the model's output as text you parse, and start treating it as a typed object the library validates for you. With pydantic-ai you declare the shape once and get a validated Python object back, with a retry on the model when it doesn't conform.

from pydantic import BaseModel, Field
from pydantic_ai import Agent

class Invoice(BaseModel):
    vendor: str
    total: float = Field(..., description='Grand total in USD')
    due_date: str | None = None

agent = Agent('anthropic:claude-sonnet-4-6', output_type=Invoice,
              system_prompt='Extract the invoice fields from the text.')

result = agent.run_sync('Acme Corp — $1,240.00 due 2026-07-01')
print(result.output.total)  # 1240.0  (a float, already validated)
Enter fullscreen mode Exit fullscreen mode

What you no longer write: the JSON fence stripping, the KeyError guards, the float coercion, the "the model added an apology before the JSON" handling. If the model returns something that doesn't fit Invoice, pydantic-ai sends the validation error back to the model and asks it to try again — so your application code only ever sees a clean object.

Three things this buys you in production:

  1. The type is the contract. Your endpoint signature, your tests, and your prompt all agree, because they reference the same model.
  2. Failures are explicit. A field that won't validate raises where you can catch it, instead of silently becoming None three functions later.
  3. It's auditable. result.output is a real object you can log, diff, and assert on.

The pattern scales from one field to a nested schema, and it's the same whether you're on Claude, GPT, or a local model. Once you've used it you stop writing parsers entirely.


I package patterns like this as small open-source pydantic-ai + FastAPI templates — the repos are on GitHub, and complete, ready-to-run versions are on Gumroad. Feedback and issues welcome.

Top comments (0)