Extraction and classification over documents feed other code, which needs fields, not prose.
Sometimes you want data, not an answer: invoice fields from a PDF, a category for a ticket, a list of action items from meeting notes. Describe the data as a Pydantic model, a class with typed fields that Pydantic validates, and let LlamaIndex fill it.
Three entry points:
- `llm.structured_predict(Model, prompt, variables)
** formats aPromptTemplateand returns aModel` instance. Good for one-off extraction. - `llm.as_structured_llm(Model)` wraps an LLM so every chat or completion returns the model. The parsed object is in
response.raw. - `index.as_query_engine(output_cls=Model)` runs RAG and returns a structured answer:
response.responseis theModelinstance andsource_nodesstill shows the evidence.
Agents accept output_cls=Model too; read the result with response.get_pydantic_model(Model).
Under the hood LlamaIndex uses the provider's function calling or JSON mode where available, and a text-parsing fallback otherwise. Function-calling models (current OpenAI, Anthropic, Gemini and many Ollama models) are most reliable.
Schema tips:
- Use descriptive field names and
Field(description=...); the model reads them. - Use
Literal[...]or enums for categories. - Make fields optional when data may be missing, so the model isn't pushed to invent values.
- Keep schemas small; for long documents, extract per chunk and merge in code.
Example
from typing import Literal, Optional
from pydantic import BaseModel, Field
from llama_index.core import PromptTemplate
from llama_index.llms.openai import OpenAI
class Invoice(BaseModel):
vendor: str
total: float = Field(description="Total amount including tax")
currency: Literal["EUR", "USD", "GBP"]
due_date: Optional[str] = Field(None, description="YYYY-MM-DD if stated")
llm = OpenAI(model="gpt-4o-mini", temperature=0)
prompt = PromptTemplate("Extract the invoice details from:\n{text}")
inv = llm.structured_predict(Invoice, prompt, text="ACME Ltd. Total due: 1,210.00 EUR by 2026-11-30.")
print(inv) # vendor='ACME Ltd.' total=1210.0 currency='EUR' due_date='2026-11-30'
sllm = llm.as_structured_llm(Invoice)
resp = sllm.complete("Bill from Globex, 99 USD, no due date.")
print(resp.raw.vendor, resp.raw.due_date) # Globex None
# RAG + structure: index.as_query_engine(output_cls=Invoice).query("...").responseRemember
structured_predict(Model, prompt, **vars)for one-off extractionas_structured_llm(Model)makes every call return the model (in.raw)as_query_engine(output_cls=Model)adds structure to RAG answers
Watch out for
- Making every field required when source documents often lack some values, which encourages the model to make them up.