Structured outputs

LlamaIndex · Practical · 10 min

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 a PromptTemplate and returns a Model` 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.response is the Model instance and source_nodes still 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("...").response

Remember

  • structured_predict(Model, prompt, **vars) for one-off extraction
  • as_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.

Check yourself

  1. Where is the Pydantic object when you call a structured LLM's complete()?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developers.llamaindex.ai/python/framework/module_guides/models/llms/