When interacting with LLMs the response you get is a free-form text. In many scenarios you may need a structured response which follows a pre-defined format. Some of those scenarios are listed below-
- API calling- You may need to pass the response of the model to API calls. REST APIs are built to consume structured data such as JSON or XML. If LLM returns structured output you can directly send it to API methods.
- Storing to DB- If LLM response is compatible to the DB table structure where that data has to be stored then it is easy to directly save the LLM response into the DB table.
- Agents- In a multi-agent workflow, communication between agent becomes more reliable and efficient by enforcing a structured output. With free-form text there is a greater chance of next agent in the workflow misinterpreting the response.
Structured output in LangChain
In LangChain, structured output is a process used to force LLMs to return their responses in a specific, pre-defined format (such as JSON or a Pydantic object) instead of free-form text. This ensures predictability and allows for direct integration with downstream applications and APIs.
Structured output in LangChain, frequently reduces token usage too by eliminating unnecessary conversational text, whitespace, and formatting tokens.
How to achieve formatted output
- with_structured_output()- This is a helper method that lets you wrap a model call so its response is automatically parsed into a structured schema.
- Output parsers- Using output parsers is another way to format the output. It is used for models that do not natively support structured output. With structured output, formatting is done by the model itself and you get the formatted response where as with output parsers, the parser performs the actual formatting by transforming that text into a structured programmatic object after the LLM returns its raw text response.
In this article we’ll stick to structured output in LangChain for formatting output.
Supported Schema Formats for Structured Output
1. TypedDictA Pythonic way to define dictionary structures where we specify which key and value pairs should form the output. One drawback of using TypedDict is that it does not validate the data at runtime.
Structured Output with TypedDict example
Suppose you want to send a prompt to the model to list 3 books of the given author. You want response in a specific format with only 3 fields title, year first published and main characters in the book. That schema structure can be defined using TypedDict. Note that TypedDict in Python is always defined by creating a class that inherits from TypedDict.
Inference provider used in the example is Groq, so you need to define GROQ_API_KEY. Also ensure installation of langchain-groq package.
from typing import List, Optional, TypedDict
from langchain_groq import ChatGroq
from langchain_core.prompts import HumanMessagePromptTemplate, ChatPromptTemplate
from dotenv import load_dotenv
load_dotenv()
# Define a TypedDict for a single book
class BookModel(TypedDict):
title: str
year_first_published: int
main_characters: Optional[list[str]]
# Define a TypedDict for multiple books (container)
class BooksResponse(TypedDict):
books: List[BookModel]
prompt = ChatPromptTemplate.from_messages([
{"role": "system", "content": "You are a helpful assistant that provides information about books."},
HumanMessagePromptTemplate.from_template("List 3 books of author {author}.")
])
model = ChatGroq(model="qwen/qwen3-32b", temperature=0.2)
# Wrap with structured output using TypedDict
structured_model = model.with_structured_output(BooksResponse)
chain = prompt | structured_model
response = chain.invoke({"author": "Agatha Christie"})
print(response)
Output
{'books': [{'main_characters': ['Hercule Poirot', 'Detective Inspector Japp', 'Countess Andrenyi'],
'title': 'Murder on the Orient Express', 'year_first_published': 1934},
{'main_characters': ['Hercule Poirot', 'Dr. Sheppard', 'Roger Ackroyd'],
'title': 'The Murder of Roger Ackroyd', 'year_first_published': 1926},
{'main_characters': ['Justice Wargrave', ' Vera Claythorne', 'Philip Lombard'],
'title': 'And Then There Were None', 'year_first_published': 1939}]}
Points to note here-
- Since list of books is needed as response so two separate TypedDict classes are defined, one of them acts as a container of books.
- Main characters field is kept optional. That makes it a non-mandatory field in response.
- You have to wrap model in with_structured_output() method, passing the output schema as a TypedDict class in this case.
structured_model = model.with_structured_output(BooksResponse)
Pydantic Models
Pydantic model provides robust data validation which is missing in TypedDict. With Pydantic you can define a schema structure which guarantees type safety. When you instantiate a Pydantic model, it checks that the provided values match the declared types. If they don’t, it raises a ValidationError.
Structured output with Pydantic example
If we take the same example of listing 3 books of the given author. You want response in specific format with only 3 fields title, year first published and main characters in the book. That schema structure can be defined using Pydantic.
Gemini model is used in the example, so you need to define GEMINI_API_KEY. Also ensure installation of langchain_google_genai package.
from pydantic import BaseModel
from typing import Optional, List
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import HumanMessagePromptTemplate, ChatPromptTemplate
from dotenv import load_dotenv
load_dotenv()
# Define a Pydantic model for a single book
class BookModel(BaseModel):
title: str
year_first_published: int
main_characters: Optional[list[str]]
# Define a Pydantic model for multiple books (container)
class BooksResponse(BaseModel):
books: List[BookModel]
prompt = ChatPromptTemplate.from_messages([
{"role": "system", "content": "You are a helpful assistant that provides information about books."},
HumanMessagePromptTemplate.from_template("List 3 books of author {author}.")
])
model = ChatGoogleGenerativeAI(model="gemini-3.1-flash-lite-preview", temperature=0.2)
# Wrap with structured output using Pydantic
structured_model = model.with_structured_output(BooksResponse)
chain = prompt | structured_model
response = chain.invoke({"author": "Agatha Christie"})
print(response)
Output
books=[BookModel(title='The Murder of Roger Ackroyd', year_first_published=1926,
main_characters=['Hercule Poirot', 'James Sheppard']),
BookModel(title='Murder on the Orient Express', year_first_published=1934,
main_characters=['Hercule Poirot']),
BookModel(title='And Then There Were None', year_first_published=1939,
main_characters=['Vera Claythorne', 'Philip Lombard', 'Lawrence Wargrave'])]
{'books': [{'title': 'The Murder of Roger Ackroyd', 'year_first_published': 1926,
'main_characters': ['Hercule Poirot', 'James Sheppard']},
{'title': 'Murder on the Orient Express', 'year_first_published': 1934,
'main_characters': ['Hercule Poirot']},
{'title': 'And Then There Were None', 'year_first_published': 1939,
'main_characters': ['Vera Claythorne', 'Philip Lombard', 'Lawrence Wargrave']}]}
JSON Schema
You can also define structured output using JSON Schema which is a standard, language-agnostic format useful for cross-system interoperability. If you have API calls which are written in different programming languages then JSON Schema is a better option. It is a universal standard, any system (Python, Java, Node.js, etc.) can validate against it, making it ideal for cross platform workflows.
Structured output with JSONSchema example
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import HumanMessagePromptTemplate, ChatPromptTemplate
from dotenv import load_dotenv
load_dotenv()
# Define a JSON Schema for a single book
book_schema = {
"title": "BookModel",
"type": "object",
"properties": {
"title": {"type": "string"},
"year_first_published": {"type": "integer"},
"main_characters": {
"type": "array",
"items": {"type": "string"},
"maxItems": 3 # restrict to 3 characters
}
},
"required": ["title", "year_first_published"]
}
# Define a JSON Schema for multiple books (container)
books_response_schema = {
"title": "BooksResponse",
"type": "object",
"properties": {
"books": {
"type": "array",
"items": book_schema,
"minItems": 3,
"maxItems": 3
}
},
"required": ["books"]
}
prompt = ChatPromptTemplate.from_messages([
{"role": "system", "content": "You are a helpful assistant that provides information about books."},
HumanMessagePromptTemplate.from_template("List 3 books of author {author}.")
])
model = ChatGoogleGenerativeAI(model="gemini-3.1-flash-lite-preview", temperature=0.2)
# Wrap with structured output using JSON Schema
structured_model = model.with_structured_output(books_response_schema)
chain = prompt | structured_model
response = chain.invoke({"author": "Agatha Christie"})
print(response)
Output
{'books': [{'title': 'The Murder of Roger Ackroyd', 'year_first_published': 1926,
'main_characters': ['Hercule Poirot', 'James Sheppard']},
{'title': 'Murder on the Orient Express', 'year_first_published': 1934,
'main_characters': ['Hercule Poirot']},
{'title': 'And Then There Were None', 'year_first_published': 1939,
'main_characters': ['Vera Claythorne', 'Philip Lombard', 'Lawrence Wargrave']}]}
Points to note here-
- book_schema defines the structure for one book.
- books_response_schema wraps a list of exactly 3 books.
- Main characters is restricted to max 3 items using maxItems: 3.
- With JSON Schema you can enforce rules like maxItems, minItems, pattern, enum, or numeric ranges directly in the schema.
That's all for this topic Structured Output In LangChain. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
You may also like-
No comments:
Post a Comment