The problem with hoping for JSON
In LangChain, getting structured data out of a model usually meant either careful prompt engineering, "respond only with JSON matching this schema," or with_structured_output(SomeModel), which works, but is bolted onto a framework that otherwise treats output as text.
Pydantic AI makes this the default way of thinking about an agent: you pass a Pydantic BaseModel as output_type, and run_sync either returns a validated instance of it or raises. There's no separate "structured mode," it's the same Agent and the same run_sync.
Declaring an output type
from dotenv import load_dotenvfrom pydantic import BaseModelfrom pydantic_ai import Agent
load_dotenv()
class CityFact(BaseModel): city: str country: str population_millions: float
agent = Agent("google:gemini-3.5-flash-lite", output_type=CityFact)
def main() -> None: result = agent.run_sync("Tell me about Paris.")
fact = result.output print("Output type:", type(fact)) print("City:", fact.city) print("Country:", fact.country) print("Population (millions):", fact.population_millions)Under the hood, Pydantic AI turns CityFact into a JSON Schema, gives it to the model as the required output shape, and validates whatever comes back against the BaseModel before handing it to you. If the model returns something that doesn't validate, Pydantic AI feeds the validation error back to the model and asks it to try again, up to retries, default 1, you don't have to write that retry loop yourself.
Why this matters more than it looks
Every field on result.output is a real, typed Python attribute. Your editor autocompletes it, your type checker catches typos in it, and downstream code, saving to a database, rendering a UI, never has to defensively check if "population" in parsed_json. This is the single biggest practical difference from LangChain's default string output.
Checkpoint
output_type=SomeBaseModelmakesresult.outputa validated instance of that model, not a string you parse yourself.- Validation failures trigger an automatic retry with the error fed back to the model, up to a retry limit.
- This replaces
with_structured_outputfrom the LangChain course, but as the default way agents work, not a special mode.
If anything here still feels unclear, ask before moving to Lesson 4.