Programs need reliable fields, and schema-constrained output makes even small local models dependable for extraction.
Asking "reply in JSON" in the prompt works most of the time. For code that depends on the result, most of the time isn't enough. Ollama can constrain the output to a JSON schema (a description of the fields and their types), so the reply always parses.
Pass the schema in the format parameter. In Python, the easy way to write a schema is Pydantic, a library where you describe data as a class: a named template listing fields and their types. Pydantic converts it to a JSON schema and later checks the reply:
class Country(BaseModel):
name: str
capital: str
languages: list[str]
response = chat(model="gpt-oss", messages=[...], format=Country.model_json_schema())
country = Country.model_validate_json(response.message.content)country.capital is now a real field you can use.
Tips from the docs and practice:
- Set
temperatureto 0 for consistent extraction. - Also describe the expected fields in the prompt; it helps the model fill them with the right content.
format="json"only guarantees valid JSON of any shape; a schema is stricter.- The schema guarantees shape, not truth: values can still be wrong or invented. Validate important values in code and allow empty or optional fields when data may be missing.
- Over the REST API, put the schema object in the
"format"field. The OpenAI-compatible API supports structured output throughresponse_format.
Structured outputs are a local feature; the docs note Ollama's cloud does not support them.
Example
from typing import Literal, Optional
from ollama import chat
from pydantic import BaseModel
class Ticket(BaseModel):
category: Literal["billing", "bug", "other"]
urgency: int # 1 (low) to 5 (critical)
order_id: Optional[str] = None # None if not mentioned
text = "I was charged twice for order A-1042, please fix this today!"
response = chat(
model="gpt-oss",
messages=[{"role": "user", "content":
f"Extract category, urgency (1-5) and order_id from: {text}"}],
format=Ticket.model_json_schema(), # constrain the reply to this schema
options={"temperature": 0},
)
ticket = Ticket.model_validate_json(response.message.content)
print(ticket) # category='billing' urgency=5 order_id='A-1042'
print(ticket.order_id) # A-1042Remember
format=Model.model_json_schema()constrains output to a schemaModel.model_validate_json(...)parses and checks the reply- Schemas guarantee shape, not correctness; use temperature 0 and validate values
Watch out for
- Trusting a schema-valid reply as correct, when the model may still have invented a value such as an order ID.