Structured Output
with_structured_output wraps a chat model so it returns a validated Pydantic object instead of prose you then have to parse. The provider enforces the schema at generation time (constrained decoding or native tool-calling), which is far more reliable than parsing free-form output after the fact.
from pydantic import BaseModel, Field
class Movie(BaseModel):
"""A movie with details."""
title: str = Field(description="The title of the movie")
year: int = Field(description="The year the movie was released")
director: str = Field(description="The director of the movie")
rating: float = Field(description="The movie's rating out of 10")
structured_model = model.with_structured_output(Movie)
response = structured_model.invoke("Provide details about the movie Inception")
# Movie(title="Inception", year=2010, director="Christopher Nolan", rating=8.8)
Nested schemas work the same way — a BaseModel field can itself be a list of BaseModels:
class Actor(BaseModel):
name: str
role: str
class MovieDetails(BaseModel):
title: str
year: int
cast: list[Actor]
genres: list[str]
budget: float | None = Field(None, description="Budget in millions USD")
To keep the raw AIMessage alongside the parsed object (for token counts, request IDs, or to inspect a parsing failure), pass include_raw=True:
structured_model = model.with_structured_output(Movie, include_raw=True)
result = structured_model.invoke("...")
# {"raw": AIMessage(...), "parsed": Movie(...), "parsing_error": None}
Schema design
- Describe every field with
Field(description=...)— the description is part of the prompt the model sees. - Keep schemas flat where you can. Deep nesting degrades extraction quality.
- Prefer enums or
Literalover free-form strings for anything you'll branch on downstream.
Structured output is not a silver bullet: validation errors still happen under load with complex schemas, optional fields can silently default instead of signaling "the model didn't know," and deeply nested or very large schemas measurably degrade output quality. Test the schema against real inputs, not just the happy path.
See also
- Output Parsers — the weaker, parse-after-the-fact alternative.
- Chat Models — the model object
with_structured_outputwraps.