Conteúdo

Saídas estruturadas no Ollama: faça o modelo retornar JSON em um formato fixo

Ao integrar um modelo a um programa, muitas vezes precisamos de mais do que um texto: precisamos de dados que o programa consiga ler diretamente. Por exemplo, extrair o nome, o número do pedido e o tipo de problema de uma reclamação de cliente.

O parâmetro format do Ollama pode obrigar o modelo a gerar uma saída compatível com o formato JSON especificado.

A forma mais simples: pedir apenas JSON

curl http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{ "role": "user", "content": "Liste três cidades do Brasil e suas populações em JSON" }],
  "format": "json",
  "stream": false
}'

A saída será um JSON válido, mas os nomes dos campos e a estrutura ficam a critério do modelo e podem variar a cada resposta.

Especifique um JSON Schema

Coloque o JSON Schema em format para definir os campos com precisão:

curl http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{ "role": "user", "content": "Apresente a cidade de São Paulo" }],
  "format": {
    "type": "object",
    "properties": {
      "city": { "type": "string" },
      "population": { "type": "integer" },
      "landmarks": { "type": "array", "items": { "type": "string" } }
    },
    "required": ["city", "population", "landmarks"]
  },
  "stream": false
}'

message.content será uma string JSON compatível com o formato.

Python: defina o formato com Pydantic

from ollama import chat
from pydantic import BaseModel


class Complaint(BaseModel):
    customer_name: str
    order_id: str | None
    category: str          # por exemplo: entrega, qualidade, reembolso, outros
    urgency: int           # 1~5
    summary: str


email = """
Olá, sou Maria Silva. O secador de cabelo do pedido A20261012-88 chegou com a embalagem danificada,
e não funcionou quando o conectei à tomada. Vou viajar na próxima semana; por favor, providenciem a troca ou o reembolso com urgência. Obrigada.
"""

response = chat(
    model="qwen3:8b",
    messages=[
        {"role": "system", "content": "Extraia informações do conteúdo da reclamação. category só pode ser: entrega, qualidade, reembolso, outros."},
        {"role": "user", "content": email},
    ],
    format=Complaint.model_json_schema(),
    options={"temperature": 0},
)

data = Complaint.model_validate_json(response.message.content)
print(data)
print(data.urgency)
customer_name='Maria Silva' order_id='A20261012-88' category='qualidade' urgency=4 summary='Embalagem do secador de cabelo danificada e aparelho não funciona; solicita troca ou reembolso com urgência'
4

model_validate_json também valida os tipos mais uma vez, garantindo que o programa receba dados corretos.

Limite as opções: use Literal ou Enum

from typing import Literal


class Complaint(BaseModel):
    category: Literal["entrega", "qualidade", "reembolso", "outros"]

Assim, o modelo só pode escolher um desses quatro valores, o que é mais confiável do que explicar as opções no prompt.

JavaScript: defina o formato com Zod

npm install ollama zod
import ollama from "ollama";
import { z } from "zod";

const Product = z.object({
  name: z.string(),
  price: z.number(),
  tags: z.array(z.string()),
});

const response = await ollama.chat({
  model: "qwen3:8b",
  messages: [{ role: "user", content: "Esta capa para iPhone custa 590 reais, é resistente a quedas, leve e compatível com MagSafe" }],
  format: z.toJSONSchema(Product),
});

const product = Product.parse(JSON.parse(response.message.content));
console.log(product);

(O Zod 4 inclui z.toJSONSchema; em versões anteriores do Zod 3, você pode usar o pacote zod-to-json-schema.)

Exemplo: classificação em lote

from typing import Literal
from ollama import chat
from pydantic import BaseModel


class Review(BaseModel):
    sentiment: Literal["positivo", "negativo", "neutro"]
    topics: list[str]


reviews = [
    "A comida estava muito gostosa, mas esperei quase uma hora.",
    "Os atendentes foram muito simpáticos. Voltarei!",
    "O preço é razoável e o sabor também.",
]

for text in reviews:
    r = chat(
        model="qwen3:8b",
        messages=[{"role": "user", "content": f"Analise esta avaliação: {text}"}],
        format=Review.model_json_schema(),
        options={"temperature": 0},
    )
    print(text, "→", Review.model_validate_json(r.message.content))

Extraia dados de imagens

Com um modelo de visão, você pode extrair dados estruturados de notas fiscais, cartões de visita e capturas de tela:

class BusinessCard(BaseModel):
    name: str
    company: str | None
    title: str | None
    phone: str | None
    email: str | None


r = chat(
    model="gemma3",
    messages=[{"role": "user", "content": "Extraia as informações do cartão de visita", "images": ["./card.jpg"]}],
    format=BusinessCard.model_json_schema(),
    options={"temperature": 0},
)
print(BusinessCard.model_validate_json(r.message.content))

Dicas para aumentar a precisão

  • Defina temperature como 0 para deixar a saída mais estável.
  • Explique também o formato e o significado dos campos no prompt; não dependa apenas do schema. Por exemplo: “urgency é um número inteiro de 1 a 5; 5 indica a maior urgência”.
  • Escolha nomes de campos claros e fáceis de entender (order_id é melhor do que oid).
  • Defina como opcionais os campos que podem não ter dados (str | None) para evitar que o modelo invente informações para preenchê-los.
  • A saída estruturada garante o formato, mas não a correção do conteúdo. Ainda é necessário validar dados importantes.

Atualizado 12 de outubro de 2026