A lot of real LangChain use cases aren't "have a conversation," they're "read this text and give me back a category, a score, or a set of extracted fields, as actual structured data my code can use directly." Free-text output makes that painful, you're left regex-ing a JSON blob out of a paragraph and hoping the model didn't wrap it in explanation. Structured output solves this properly.
Defining the shape you want with Pydantic
The standard approach is to describe the output you want as a Pydantic model, then pass it to with_structured_output, which returns a new model that's constrained to producing exactly that shape:
from pydantic import BaseModel, Field
class SupportTicket(BaseModel):
category: str = Field(description="billing, technical, or general")
urgency: str = Field(description="low, medium, or high")
summary: str = Field(description="one-sentence summary of the issue")
structured_model = model.with_structured_output(SupportTicket)
result = structured_model.invoke("My payment failed twice and I need this sorted today.")
# result is a SupportTicket instance: result.category, result.urgency, result.summary
No parsing, no regex, no hoping. You get back an actual instance of your Pydantic class, with the type checking and validation that comes with it.
What's actually happening underneath
Under the hood, with_structured_output is built on the same function calling (also called tool calling) capability covered in the tools and agents guide: the model provider's API accepts a schema alongside the prompt and constrains its response to match it. The Pydantic model you write gets converted into that schema automatically. So structured output and tool calling aren't two separate features, they're the same underlying mechanism aimed at two different jobs.
Structured output vs. an agent: picking the right one
| Structured output | Agent (create_agent) | |
|---|---|---|
| Shape of the task | One input, one classification or extraction, done | Multi-step, may need to call several tools in sequence |
| What comes back | A typed object matching your schema | A final message, after however many tool calls it took |
| Good fit for | Classification, extraction, form-filling, routing decisions | Research, lookups plus actions, anything requiring judgment about what to do next |
Nesting and lists work as you'd expect
Pydantic fields can be lists, nested models, or optional fields, and with_structured_output handles all of it the same way. Extracting a list of line items from an invoice, each with its own nested fields, is the same pattern as the single-object example above, just with a field typed as list[LineItem] instead of a plain string.