
Saída estruturada de LLM é o mecanismo que garante que a resposta de um modelo de linguagem esteja em conformidade com um esquema predefinido, não apenas JSON válido, mas JSON válido segundo o esquema, com os campos, tipos e restrições exatos que especificou. Todos os principais fornecedores suportam-no agora nativamente, e isso mudou a forma como as aplicações LLM de produção são construídas.
Resumo Rápido: Saídas Estruturadas num Relance
Se tem pouco tempo, eis o panorama em 2026:
| Aspeto | Detalhes |
|---|---|
| O que é | Respostas impostas por esquema de LLMs, estrutura garantida, não "melhor esforço" |
| Quem suporta | OpenAI, Anthropic, Gemini, Cohere, xAI (Grok), mais localmente via Ollama/vLLM |
| Mecanismo chave | Descodificação restrita, tokens inválidos são mascarados antes da amostragem |
| Modo JSON vs Modo Estrito | Modo JSON = apenas sintaxe válida. Modo Estrito = conformidade total com o esquema |
| Biblioteca Python | Pydantic (BaseModel + Field) para definição de esquema |
| Biblioteca TypeScript | Zod (z.object + .describe) para definição de esquema |
| Melhor abordagem inicial | OpenAI com Pydantic ou Zod via SDK nativo |
| Melhor biblioteca de produção | Instructor (Python) ou SDK nativo (TypeScript) |
| Maior armadilha | Colocar o campo de raciocínio DEPOIS do campo de resposta; o modelo decide antes de pensar |
| Sobrecarga de latência | 50-200ms na primeira chamada (compilação do esquema), armazenado em cache depois |
Agora, vamos decompor cada parte.
O Que São Saídas Estruturadas de LLM?
A saída estruturada é a diferença entre esperar que um LLM devolva JSON válido e garantir isso. Quando ativa a saída estruturada, o modelo fisicamente não pode produzir tokens que violem o seu esquema. Define um Esquema JSON (ou modelo Pydantic, ou esquema Zod), passa-o à API e recebe uma resposta que corresponde ao mesmo todas as vezes.
Porque é que isto importa? Antes da saída estruturada, os programadores escreviam analisadores regex frágeis, envolviam cada chamada LLM em blocos try/catch JSON.parse e ainda lidavam com respostas "quase certas", JSON válido que faltava um campo ou tinha o tipo errado. Toda essa classe de erros desapareceu.
Existem três níveis de imposição de estrutura, e representam uma evolução clara:
- Engenharia de prompts, "Por favor, devolva JSON com estes campos." Pouco fiável. O modelo pode cumprir 80-90% das vezes.
- Modo JSON, Garante JSON sintaticamente válido, mas não impõe o seu esquema. Poderia obter
{"foo": "bar"}quando esperava{"name": string, "age": number}. - Modo Estrito / Descodificação restrita, Garante 100% de conformidade com o esquema. O modelo literalmente não pode emitir tokens inválidos. Isto é o que "saída estruturada" significa em 2026.
No início de 2026, OpenAI, Anthropic e Google Gemini suportam todos saída estruturada nativa. O ecossistema convergiu.
Veredito: Se está a analisar respostas de LLM com regex ou JSON.parse em produção, está a fazê-lo da maneira difícil. A saída estruturada nativa elimina todo esse modo de falha.
Modo JSON vs Modo Estrito: O Que Realmente Mudou?
Esta distinção confunde muitos programadores porque os nomes soam semelhantes. Não são.
| Funcionalidade | Modo JSON | Modo Estrito (Saídas Estruturadas) |
|---|---|---|
| Parâmetro da API | type: "json_object" | type: "json_schema" com strict: true |
| Garante JSON válido | Sim | Sim |
| Garante conformidade com o esquema | Não | Sim |
| Mecanismo | Viés de tokens pós-hoc | Descodificação restrita (FSM) |
| Pode devolver campos inesperados | Sim | Não |
| Pode omitir campos obrigatórios | Sim | Não |
| Imposição de tipo | Nenhuma | Total (string, number, array, etc.) |
| Quando usar | Não tem um esquema prévio | Tudo em produção |
A linha temporal: A OpenAI introduziu o Modo JSON no final de 2023. Foi um passo em frente, mas os programadores perceberam rapidamente que "JSON válido" não era suficiente; precisavam de JSON válido segundo o esquema. Em agosto de 2024, a OpenAI lançou as Saídas Estruturadas com Modo Estrito, que utiliza descodificação restrita para garantir a conformidade com o esquema. Em 2025-2026, todos os principais fornecedores adotaram a mesma abordagem.
O Modo JSON ainda tem um caso de uso restrito: quando genuinamente não sabe a forma da resposta antecipadamente e apenas quer algum JSON válido para exploração não estruturada. Mas isso é raro em produção.
Veredito: Use o Modo Estrito para tudo em produção. O Modo JSON está efetivamente obsoleto para casos de uso vinculados a esquemas. Se tem um esquema (e deveria ter), use type: "json_schema" com strict: true.
Como Funciona Realmente a Descodificação Restrita?
Eis o mecanismo que torna possível 100% de conformidade com o esquema, não 99,9%, mas literalmente 100%.
Quando envia um Esquema JSON para um fornecedor com o Modo Estrito ativado, o esquema é compilado numa máquina de estados finitos (FSM). Esta FSM representa todos os caminhos válidos através do seu esquema. Em cada passo de geração de tokens, o motor de inferência verifica quais os tokens que manteriam a saída num caminho válido e quais não o fariam. Os tokens inválidos têm os seus logit definidos para infinito negativo antes da amostragem, o que significa que têm probabilidade zero de serem selecionados.
<!-- IMAGE: constrained-decoding diagram showing FSM token masking during structured output generation -->Pense nisso como preenchimento automático com esteroides. Se o modelo acabou de emitir {"rating": e o seu esquema diz que rating é um inteiro, os únicos tokens permitidos a seguir são tokens de dígitos. Aspas, letras, parênteses, tudo mascarado. O modelo não pode emitir "five" mesmo que "queira".
Este é o mesmo mecanismo central usado pelo XGrammar (o motor por trás do vLLM, SGLang e da maioria dos servidores de inferência locais) e pelo Outlines (a biblioteca Python de código aberto para geração restrita). Os fornecedores de API apenas o integraram na sua infraestrutura de inferência.
Existe uma compensação a conhecer: a primeira solicitação com um novo esquema incorre numa penalização de latência de compilação (tipicamente 50-200ms) enquanto a FSM é construída. As solicitações subsequentes com o mesmo esquema utilizam uma FSM em cache e adicionam uma sobrecarga quase nula. Há também uma consideração subtil de qualidade; restringir o vocabulário de tokens pode ocasionalmente reduzir a qualidade da saída para campos criativos ou de forma livre, por isso mantenha os seus esquemas focados em dados verdadeiramente estruturados.
Veredito: A descodificação restrita é o que separa "geralmente funciona" de "sempre funciona". É a engenharia que torna a saída estruturada pronta para produção.
Implementação Multi-Fornecedor: OpenAI, Anthropic e Gemini
Aqui está algo que nenhum outro guia lhe mostra: a mesma tarefa de extração implementada em todos os três principais fornecedores. Vamos extrair uma revisão de produto estruturada a partir de texto não estruturado.
O esquema Pydantic (partilhado entre todos os fornecedores):
from pydantic import BaseModel, Field
from typing import Literal
class ProductReview(BaseModel):
reasoning: str = Field(description="Think through the review before scoring")
rating: int = Field(description="Rating from 1-5", ge=1, le=5)
sentiment: Literal["positive", "negative", "neutral"]
pros: list[str] = Field(description="Key positive points")
cons: list[str] = Field(description="Key negative points")
summary: str = Field(description="One-sentence summary")Implementação OpenAI
from openai import OpenAI
client = OpenAI()
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "Extract a structured review from the text."},
{"role": "user", "content": review_text}
],
response_format=ProductReview, # Pydantic model directly
)
review = response.choices[0].message.parsed # Typed ProductReview objectA implementação da OpenAI é a mais madura. O método parse() aceita um modelo Pydantic diretamente e devolve um objeto tipado. Uma restrição: o Modo Estrito da OpenAI suporta um subconjunto do Esquema JSON, sem $ref, anyOf limitado, e todos os campos devem ser obrigatórios com additionalProperties: false.
Implementação Anthropic
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": f"Extract a structured review:\n\n{review_text}"}
],
output_config={
"format": {
"type": "json_schema",
"json_schema": ProductReview.model_json_schema()
}
}
)
import json
review_data = json.loads(response.content[0].text)
review = ProductReview(**review_data)A saída estruturada nativa da Anthropic utiliza output_config.format com um Esquema JSON. Alcançou disponibilidade geral (GA) no início de 2026. A Anthropic também suporta o padrão mais antigo de definir uma ferramenta "falsa" e extrair via tool_use; isso ainda funciona, mas a saída estruturada nativa é mais limpa para extração pura.
Implementação Gemini
from google import genai
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=f"Extract a structured review:\n\n{review_text}",
config={
"response_mime_type": "application/json",
"response_schema": ProductReview, # Pydantic model directly
}
)
import json
review = ProductReview(**json.loads(response.text))Gemini suporta modelos Pydantic diretamente no SDK Python via response_schema. Uma funcionalidade única: o Gemini respeita propertyOrdering no esquema, por isso pode controlar a ordem de saída dos campos (útil para o padrão de raciocínio primeiro).
Comparação de Fornecedores
| Funcionalidade | OpenAI | Anthropic | Gemini |
|---|---|---|---|
| Parâmetro da API | response_format | output_config.format | response_schema |
| Entrada do esquema | Pydantic ou Esquema JSON | Esquema JSON | Pydantic ou Esquema JSON |
| Modo estrito | strict: true | Implícito com json_schema | Implícito |
| Streaming | Sim (JSON parcial) | Sim | Sim |
| Gestão de recusa | Campo message.refusal | Resposta de erro | Resposta de erro |
| Alternativa de uso de ferramentas | Sim | Sim (método original) | Sim |
| Cache de compilação de esquema | Sim (lado do servidor) | Sim | Sim |
| Ordenação de propriedades | Sem suporte nativo | Não | Sim (propertyOrdering) |
Veredito: A OpenAI tem a experiência de desenvolvimento (DX) mais polida com o seu método parse(). A Anthropic oferece os modelos subjacentes mais capazes. A ordenação de propriedades do Gemini é unicamente útil. Todos os três fazem o trabalho; escolha com base na sua relação existente com o fornecedor.
Padrões Pydantic para Programadores Python
Pydantic é o padrão de facto para definir esquemas de saída estruturada em Python. Eis os padrões que importam.
Esquema Básico com Descrições
from pydantic import BaseModel, Field
from typing import Literal, Optional
class ExtractedEntity(BaseModel):
reasoning: str = Field(description="Think step by step about the entity")
name: str = Field(description="Full name of the entity")
entity_type: Literal["person", "company", "location"]
confidence: float = Field(description="Confidence score 0.0-1.0", ge=0.0, le=1.0)
context: Optional[str] = Field(description="Surrounding context, if relevant")Aquelas strings description não são apenas para documentação; tornam-se parte do Esquema JSON enviado ao modelo e influenciam diretamente o que o modelo gera. Pense nelas como engenharia de prompts dentro do esquema.
Modelos Aninhados
class Address(BaseModel):
street: str
city: str
country: str
postal_code: Optional[str] = None
class Company(BaseModel):
reasoning: str = Field(description="Analysis of the company details")
name: str
industry: Literal["tech", "finance", "healthcare", "retail", "other"]
headquarters: Address # Nested model
key_products: list[str] = Field(description="Top 3 products or services")Mantenha o aninhamento num máximo de 2-3 níveis. Esquemas profundamente aninhados aumentam as taxas de erro e desaceleram a compilação do esquema.
O Padrão de Raciocínio Primeiro
Este é o padrão de design de esquema mais impactante. Coloque um campo reasoning antes dos seus campos de resposta:
# Reliable JSON from Any LLM: Pydantic + Zod Patterns for 2026
class ClassificationBad(BaseModel):
category: Literal["spam", "ham"]
confidence: float
# Good -- model reasons through the problem first
class ClassificationGood(BaseModel):
reasoning: str = Field(description="Analyze the text before classifying")
category: Literal["spam", "ham"]
confidence: float = Field(ge=0.0, le=1.0)Os LLMs geram tokens da esquerda para a direita. Se category vier primeiro, o modelo escolhe uma categoria e depois racionaliza-a. Se reasoning vier primeiro, o modelo trabalha o problema e depois compromete-se com uma categoria. É o pensamento em cadeia embutido no esquema.
Exportação de Esquema JSON
# Generate the JSON Schema for any Pydantic model
schema = ProductReview.model_json_schema()
# Pass this to any provider that accepts raw JSON SchemaVeredito: Pydantic + campos descritivos + ordenação de raciocínio primeiro é a trindade da saída estruturada em Python. Domine estes três padrões e lidará com 90% dos casos de uso.
Padrões Zod para Programadores TypeScript
Zod é o equivalente TypeScript do Pydantic, e é igualmente central nos fluxos de trabalho de saída estruturada.
Esquema Básico com Descrições
import { z } from "zod";
const ProductReview = z.object({
reasoning: z.string().describe("Think through the review before scoring"),
rating: z.number().int().min(1).max(5),
sentiment: z.enum(["positive", "negative", "neutral"]),
pros: z.array(z.string()).describe("Key positive points"),
cons: z.array(z.string()).describe("Key negative points"),
summary: z.string().describe("One-sentence summary"),
});
// Infer the TypeScript type automatically
type ProductReview = z.infer<typeof ProductReview>;Tal como o Field(description=...) do Pydantic, o .describe() do Zod torna-se parte do Esquema JSON e orienta a saída do modelo.
Integração com o SDK Node da OpenAI
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
const client = new OpenAI();
const response = await client.beta.chat.completions.parse({
model: "gpt-4o-2024-08-06",
messages: [
{ role: "system", content: "Extract a structured review." },
{ role: "user", content: reviewText },
],
response_format: zodResponseFormat(ProductReview, "product_review"),
});
const review = response.choices[0].message.parsed; // Typed!Integração com o Vercel AI SDK
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
const { object: review } = await generateObject({
model: openai("gpt-4o"),
schema: ProductReview,
prompt: `Extract a structured review:\n\n${reviewText}`,
});
// review is fully typed as ProductReviewO Vercel AI SDK utiliza Zod nativamente com generateObject(), tornando-o a integração TypeScript mais limpa. Funciona com OpenAI, Anthropic, Gemini e outros fornecedores através de uma API unificada.
Conversão para Esquema JSON
import { zodToJsonSchema } from "zod-to-json-schema";
const jsonSchema = zodToJsonSchema(ProductReview);
// Use with any provider that accepts raw JSON SchemaVeredito: Zod + .describe() + o Vercel AI SDK é a pilha de saída estruturada TypeScript. Se está no ecossistema Node/Next.js, este é o caminho de menor resistência.
Saída Estruturada vs Chamada de Função: Quando Usar Cada Uma?
Esta é uma das fontes de confusão mais comuns. Ambas envolvem esquemas, ambas devolvem dados estruturados, mas resolvem problemas diferentes.
Saída estruturada diz: "Dê-me dados nesta forma exata." É para extração, classificação e formatação. Está a retirar informação estruturada de texto não estruturado.
Chamada de função (uso de ferramentas) diz: "Aqui estão ações que pode tomar, decida qual executar e forneça os argumentos." É para fluxos de trabalho de agentes onde o modelo escolhe entre várias ferramentas e desencadeia ações.
A confusão faz sentido historicamente. A "saída estruturada" original da Anthropic era literalmente chamada de função; definia uma ferramenta falsa chamada extract_review e capturava os argumentos. Isso ainda funciona, mas a saída estruturada nativa é mais simples para extração pura.
| Cenário | Melhor Abordagem | Porquê |
|---|---|---|
| Extrair dados de texto | Saída estruturada | Direta, menor latência, esquema único |
| Classificar em categorias | Saída estruturada | Uma resposta, um esquema |
| Agente a decidir qual ferramenta chamar | Chamada de função | Modelo escolhe entre várias ferramentas |
| Orquestração em vários passos | Chamada de função | Invocações sequenciais de ferramentas |
| Extrair dados E decidir a próxima ação | Ambas | Saída estruturada para extração, chamada de função para orquestração |
A saída estruturada alimenta as pipelines de chamada de ferramentas em sistemas de agentes de IA. Consulte o nosso guia de agentes de IA para empresas para saber como estes se encaixam nos fluxos de trabalho de produção.
Veredito: Use saída estruturada quando souber qual deve ser a forma dos dados. Use chamada de função quando o modelo precisar de escolher uma ação. Na prática, a maioria das aplicações usa ambas: saída estruturada para extração de dados e chamada de função para orquestração de agentes.
Padrões de Produção: Erros, Novas Tentativas e Streaming
Fazer a saída estruturada funcionar numa demonstração é fácil. Mantê-la fiável em produção requer lidar com três coisas: recusas, falhas de validação e streaming.
Gestão de Recusas
Às vezes, um modelo recusa-se a gerar a saída solicitada, tipicamente porque filtros de segurança sinalizaram a entrada. Quando isto acontece, as APIs de saída estruturada não devolvem o seu esquema. Devolvem uma recusa.
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ProductReview,
)
# ALWAYS check for refusal before accessing parsed content
if response.choices[0].message.refusal:
print(f"Model refused: {response.choices[0].message.refusal}")
else:
review = response.choices[0].message.parsedSe ignorar a verificação de recusa e tentar aceder a .parsed numa recusa, obterá None e um erro descendente confuso. Verifique primeiro, sempre.
Padrões de Nova Tentativa com Feedback de Validação
A conformidade com o esquema é garantida pela descodificação restrita, mas a correção semântica não é. O modelo pode devolver {"rating": 1, "sentiment": "positive"}, esquema válido, conteúdo contraditório. É aqui que entram a validação + novas tentativas.
import instructor
client = instructor.from_openai(OpenAI())
# Instructor handles retries automatically
review = client.chat.completions.create(
model="gpt-4o",
response_model=ProductReview,
max_retries=3, # Retries with validation error feedback
messages=[
{"role": "user", "content": review_text}
],
)O Instructor alimenta o erro de validação de volta ao modelo na nova tentativa, para que este possa autocorrigir-se. Para padrões manuais de nova tentativa sem o Instructor:
from pydantic import ValidationError
for attempt in range(3):
try:
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ProductReview,
)
review = response.choices[0].message.parsed
# Run additional semantic validation here
break
except ValidationError as e:
messages.append({"role": "assistant", "content": str(response)})
messages.append({"role": "user", "content": f"Validation error: {e}. Fix it."})Streaming de Saída Estruturada
Para respostas estruturadas grandes, matrizes longas, muitos campos, objetos aninhados complexos, o streaming permite renderizar resultados parciais progressivamente.
import instructor
client = instructor.from_openai(OpenAI())
# Stream partial results as fields populate
review_stream = client.chat.completions.create_partial(
model="gpt-4o",
response_model=ProductReview,
messages=[{"role": "user", "content": review_text}],
)
for partial_review in review_stream:
# Fields populate one by one as tokens stream in
if partial_review.summary:
print(f"Summary so far: {partial_review.summary}")Uma armadilha: fragmentos individuais de streaming não são válidos segundo o esquema por si só. O campo reasoning pode estar preenchido enquanto rating ainda é None. Planeie a sua interface de utilizador em conformidade; mostre um estado de carregamento para campos não preenchidos.
Veredito: As verificações de recusa são inegociáveis. As novas tentativas com feedback de validação capturam erros semânticos. O streaming vale a pena para qualquer resposta que demore mais de alguns segundos.
Bibliotecas de Saída Estruturada Comparadas
Pode usar saída estruturada através de APIs nativas, mas as bibliotecas adicionam validação, novas tentativas, streaming e suporte multi-fornecedor. Eis o panorama.
Instructor é a opção mais popular, com mais de 11 mil estrelas no GitHub e mais de 3 milhões de downloads mensais. Envolve OpenAI, Anthropic, Gemini, Cohere, Ollama e mais, com uma interface unificada baseada em Pydantic. Funcionalidades chave: novas tentativas automáticas com feedback de validação, streaming via create_partial() e configuração extremamente simples (instructor.from_openai(client)). Se é uma equipa Python, comece aqui.
BAML adota uma abordagem diferente: esquema primeiro via uma DSL personalizada. Define esquemas em ficheiros .baml e gera automaticamente clientes para Python, TypeScript, Ruby e mais. O seu algoritmo SAP (parsing alinhado ao esquema) lida graciosamente com saídas de modelos desorganizadas. Melhor para equipas multilinguagem ou quando deseja contratos entre a sua camada LLM e a camada da aplicação. Compensação: passo de compilação extra e uma nova sintaxe para aprender.
LangChain oferece .with_structured_output(schema) para saída estruturada agnóstica do fornecedor. Conveniente se já estiver no ecossistema LangChain. Compensação: é uma dependência pesada, e a abstração pode ocultar funcionalidades específicas do fornecedor de que possa precisar.
APIs Nativas, chamadas diretas com response_format / output_config, requerem zero dependências além do SDK do fornecedor. Obtém controlo total e visibilidade total. Melhor para casos de uso simples ou equipas que preferem abstração mínima.
| Biblioteca | Linguagens | Fornecedores | Novas Tentativas Automáticas | Streaming | Estrelas GitHub | Curva de Aprendizagem |
|---|---|---|---|---|---|---|
| Instructor | Python, TS | 15+ | Sim | Sim | 11K+ | Baixa |
| BAML | Python, TS, Ruby, Go | Todos (agnóstico DSL) | Sim | Sim | 7K+ | Média |
| LangChain | Python, TS | 20+ | Parcial | Sim | 100K+ | Média-Alta |
| APIs Nativas | Qualquer | 1 por SDK | Não | Sim | N/A | Baixa |
Escolher a biblioteca de saída estruturada certa faz parte de uma decisão mais ampla da pilha de IA. Decomponhamos a pilha completa no nosso Guia da Melhor Pilha de IA para SaaS.
Consulte as nossas Melhores Bibliotecas para Saídas Estruturadas de LLM [em breve] para uma comparação detalhada do Instructor, BAML, Mirascope e mais.
Veredito: Comece com Instructor para Python, APIs nativas para TypeScript. Mude para BAML se precisar de contratos de esquema multilinguagem. Evite LangChain apenas para saída estruturada; é exagero.
Melhores Práticas de Design de Esquema (e Erros Comuns)
O seu design de esquema impacta diretamente a qualidade da saída. Eis os padrões que importam e os erros que custam precisão.
Coloque o Raciocínio Antes das Respostas
Abordámos isto na secção Pydantic, mas vale a pena repetir porque é a decisão de design de maior impacto:
# Before: model guesses the answer, then rationalizes
class Bad(BaseModel):
answer: str
reasoning: str
# After: model thinks first, then commits
class Good(BaseModel):
reasoning: str = Field(description="Think step by step")
answer: strOs LLMs geram da esquerda para a direita. A ordem dos campos é a ordem do prompt. Raciocínio primeiro significa que o modelo tem de trabalhar o problema antes de se comprometer com uma resposta.
Tabela de Anti-Padrões
| Erro | Problema | Correção |
|---|---|---|
| Campo de raciocínio após a resposta | Modelo decide antes de pensar | Mover raciocínio antes da resposta |
| Profundamente aninhado (4+ níveis) | Maior taxa de erro, compilação mais lenta | Achatar para 2-3 níveis |
| Sem descrições de campo | Modelo adivinha o que quer | Adicionar .describe() / Field(description=...) |
| Falta de tratamento de nulos | Modelo alucina um valor para preencher o campo | Usar Optional / .nullable() |
| Esquemas excessivamente grandes (50+ campos) | Tempo limite de compilação, degradação da qualidade | Dividir em várias chamadas |
| Opções de enum vagas | Modelo escolhe a categoria errada | Usar opções específicas e não sobrepostas |
Trate Nulos Explicitamente
Se um campo pode não ter dados no texto de origem, torne-o opcional. Forçar um campo obrigatório quando os dados não existem leva à alucinação:
class PersonInfo(BaseModel):
name: str # Always present
email: Optional[str] = Field(None, description="Email if mentioned, null otherwise")
phone: Optional[str] = Field(None, description="Phone if mentioned, null otherwise")Mantenha os Esquemas Focados
Um esquema por tarefa. Não tente extrair tudo num único esquema massivo. Se precisar de 50+ campos, divida em várias chamadas de extração. O Modo Estrito da OpenAI tem limites práticos na complexidade do esquema, e mesmo quando funciona, esquemas muito grandes degradam a qualidade da saída.
Veredito: Raciocínio primeiro, campos descritivos, nulos explícitos e esquemas focados. Acerte nestes quatro e a precisão da sua saída estruturada salta mensuravelmente.
Saída Estruturada com LLMs Locais
Não precisa de um fornecedor de API para saída estruturada. Motores de inferência locais suportam-na através de descodificação restrita baseada em gramática, o mesmo mecanismo fundamental, executado no seu próprio hardware.
Ollama
O caminho mais fácil para saída estruturada local. Ollama aceita um Esquema JSON via o parâmetro format:
import ollama
from pydantic import BaseModel
class Country(BaseModel):
name: str
capital: str
languages: list[str]
response = ollama.chat(
model="llama3.2",
messages=[{"role": "user", "content": "Tell me about Japan."}],
format=Country.model_json_schema(),
)
import json
country = Country(**json.loads(response.message.content))O Ollama usa XGrammar nos bastidores para descodificação restrita. A mesma garantia que os fornecedores de API: 100% de conformidade com o esquema.
vLLM e SGLang
Para inferência local de grau de produção, vLLM e SGLang suportam ambos saída estruturada através dos parâmetros guided_json e guided_regex. XGrammar é o backend padrão, entregando sobrecarga quase nula na geração de JSON, até 3,5x mais rápido que motores de gramática alternativos.
Outlines
Outlines é a biblioteca Python de código aberto que pioneirizou a geração restrita baseada em gramática. Funciona com qualquer modelo Hugging Face e suporta Esquema JSON, regex e restrições de gramática livre de contexto completa (CFG/EBNF). Também está integrado no vLLM e SGLang como uma opção de backend de gramática.
A diferença chave em relação aos fornecedores de API: a saída estruturada local não tem limitações de subconjunto de esquema. Controla a gramática totalmente. Mas a qualidade do modelo varia mais; um modelo local de 7 mil milhões de parâmetros não corresponderá ao GPT-4o ou Claude em tarefas de extração complexas. O esquema será sempre válido; a qualidade do conteúdo depende do modelo.
Veredito: Ollama para desenvolvimento, vLLM/SGLang com XGrammar para produção. A saída estruturada local é suficientemente madura para a maioria dos casos de uso, com a ressalva de que modelos menores produzem conteúdo de menor qualidade dentro do esquema.
FAQ
O que é saída estruturada em LLMs?
Saída estruturada é um mecanismo que garante que a resposta de um LLM esteja em conformidade com um Esquema JSON predefinido. Ao contrário de texto simples ou mesmo do Modo JSON, a saída estruturada utiliza descodificação restrita para assegurar que cada campo, tipo e restrição no seu esquema seja cumprido -- 100% das vezes, não "geralmente".
Qual é a diferença entre Modo JSON e Saídas Estruturadas?
O Modo JSON garante JSON sintaticamente válido, mas não impõe o seu esquema; poderia obter qualquer objeto JSON válido. Saídas Estruturadas (Modo Estrito) garantem conformidade total com o esquema através de descodificação restrita. Use o Modo Estrito para produção; o Modo JSON só é relevante quando não tem um esquema prévio.
Quais fornecedores de LLM suportam nativamente saída estruturada?
OpenAI (desde agosto de 2024), Google Gemini (2024, expandido 2026), Anthropic (beta novembro 2025, GA início 2026), Cohere e xAI (Grok) suportam todos saída estruturada nativa. No lado local, Ollama, vLLM e SGLang suportam-na através de descodificação restrita baseada em gramática.
Como é que a descodificação restrita garante conformidade com o esquema?
O Esquema JSON é compilado numa máquina de estados finitos (FSM). Em cada passo de geração de tokens, apenas os tokens que mantêm a saída num caminho válido através da FSM são permitidos; os tokens inválidos têm os seus logit definidos para infinito negativo. Isto significa que os tokens inválidos têm probabilidade zero de serem gerados, dando-lhe uma garantia matemática, não estatística.
Devo usar saída estruturada ou chamada de função?
Use saída estruturada para extração e classificação, quando quer dados numa forma específica. Use chamada de função para fluxos de trabalho de agentes, quando o modelo precisa de decidir qual ação tomar. Muitas aplicações de produção usam ambas: saída estruturada para extração de dados e chamada de função para orquestração.
Posso fazer streaming de saída estruturada?
Sim. A OpenAI suporta streaming com o método parse(), e o Instructor fornece create_partial() para streaming de modelos Pydantic que preenchem campo por campo. Tenha em mente que fragmentos individuais de streaming não são individualmente válidos segundo o esquema; os campos são preenchidos incrementalmente.
O que é a biblioteca Instructor?
Instructor é a biblioteca de saída estruturada mais popular (mais de 11 mil estrelas no GitHub, mais de 3 milhões de downloads mensais). Envolve SDKs de fornecedores com validação baseada em Pydantic, novas tentativas automáticas com feedback de validação e suporte de streaming. Funciona com OpenAI, Anthropic, Gemini, Cohere, Ollama e mais de 10 outros fornecedores.
A saída estruturada funciona com LLMs locais?
Sim. O Ollama suporta saída estruturada via o parâmetro format com Esquema JSON. vLLM e SGLang suportam-na através de parâmetros guided_json. Todos os três usam XGrammar ou Outlines para descodificação restrita. A garantia de conformidade com o esquema é a mesma que a dos fornecedores de API; a qualidade do conteúdo depende do modelo.
Quais são os erros comuns de design de esquema?
Os principais erros: colocar o campo de raciocínio após o campo de resposta (o modelo decide antes de pensar), esquemas profundamente aninhados (4+ níveis aumentam erros), falta de descrições de campo (o modelo adivinha a intenção), sem tratamento de nulos para dados opcionais (força alucinação) e esquemas excessivamente grandes (50+ campos degradam a qualidade).
A saída estruturada adiciona latência?
Existe uma sobrecarga de compilação de esquema na primeira solicitação, tipicamente 50-200ms enquanto a FSM é construída. As solicitações subsequentes com o mesmo esquema usam uma FSM em cache e adicionam latência quase nula. Para a maioria das aplicações, isto é negligenciável comparado com o tempo total de inferência do modelo.
Posso usar saída estruturada com imagens ou entradas multimodais?
Sim. A saída estruturada aplica-se ao formato da resposta, não da entrada. Pode enviar uma imagem para o GPT-4o ou Gemini com um esquema de saída estruturada e receber de volta uma análise da imagem em conformidade com o esquema. Isto é poderoso para fluxos de trabalho de extração visual, extraindo dados estruturados de recibos, formulários ou imagens de produtos.
Fontes
- Guia de Saídas Estruturadas da OpenAI
- Documentação de Uso de Ferramentas da Anthropic
- Saída Estruturada do Google Gemini
- Documentação da Biblioteca Instructor
- Documentação do BAML
- Documentação do Pydantic
- Documentação do Zod
- Biblioteca Outlines
- GitHub do XGrammar
- Saídas Estruturadas do Ollama
- Vercel AI SDK