Python + IA: Fundamentos e Projetos Práticos
Uma automação deixa de ser confiável no instante em que precisa “adivinhar” o formato da resposta de uma inteligência artificial. Um campo que muda de nome, um valor fora do padrão ou uma frase antes do JSON pode quebrar o fluxo inteiro. Para sair do protótipo e chegar a uma rotina que toma decisões com segurança, o modelo precisa responder dentro de um contrato verificável.
Nesta aula da série Python + IA: Fundamentos e Projetos Práticos, vamos usar Structured Outputs, Pydantic e a Responses API da OpenAI para transformar uma solicitação de suporte em dados tipados. O resultado poderá alimentar uma fila, um banco de dados, um webhook ou uma automação no n8n sem depender de expressões regulares frágeis.
O objetivo não é apenas obter um JSON válido. É garantir que os campos esperados existam, que os valores respeitem o schema e que o código Python saiba o que fazer quando a resposta não puder ser processada.
Por que “responda em JSON” não basta
Pedir JSON no prompt ajuda, mas não cria um contrato. O modelo ainda pode devolver uma chave diferente, omitir um campo obrigatório ou escolher um valor que sua aplicação não reconhece. O modo JSON resolve parte do problema porque produz JSON sintaticamente válido, mas não garante aderência ao formato de negócio.
Structured Outputs acrescenta essa garantia de schema. Segundo a documentação oficial da OpenAI, a resposta segue o JSON Schema fornecido, incluindo chaves obrigatórias e enumerações compatíveis com os recursos suportados. No SDK Python, podemos declarar o formato com um modelo Pydantic e receber o objeto já analisado.
Essa distinção muda a arquitetura. Em vez de extrair texto e tentar corrigi-lo depois, definimos primeiro o que a aplicação aceita. A IA passa a preencher esse contrato. Ainda precisamos validar regras de negócio e tratar falhas operacionais, mas eliminamos uma grande classe de erros de formatação.
Prepare o projeto e proteja a chave
Crie um ambiente isolado e instale o SDK da OpenAI e o Pydantic. Se você acompanhou a aula anterior sobre ambientes reproduzíveis com uv e pyproject.toml, pode continuar no mesmo padrão:
uv init triagem-ia
cd triagem-ia
uv add openai pydantic
A introdução oficial da API orienta configurar OPENAI_API_KEY como variável de ambiente; o SDK a lê automaticamente. Não coloque a chave no arquivo Python, no repositório ou em uma captura de tela. Em produção, use o gerenciador de segredos da sua infraestrutura e conceda acesso apenas ao serviço que executa a automação.
O projeto deste tutorial terá um arquivo main.py. A entrada será uma mensagem livre enviada por um cliente. A saída deverá conter categoria, prioridade, resumo, indicação de atendimento humano e tags.
Modele o contrato com Pydantic
Comece pelos valores que não podem variar livremente. A prioridade será uma enumeração, enquanto o restante será descrito em um modelo Pydantic:
from enum import Enum
from pydantic import BaseModel, Field
class Prioridade(str, Enum):
baixa = "baixa"
media = "media"
alta = "alta"
class Triagem(BaseModel):
categoria: str = Field(
description="Fila curta: financeiro, acesso, suporte ou comercial"
)
prioridade: Prioridade
resumo: str = Field(
description="Resumo objetivo da solicitação em até duas frases"
)
requer_humano: bool
tags: list[str]
O schema é a fronteira entre o modelo e o restante do sistema. Quanto mais previsível ele for, mais simples será integrar. Uma enumeração é melhor que instruções como “use baixa, média ou alta”, pois impede variações inesperadas. Para categoria, poderíamos criar outra enumeração; deixamos como texto neste exemplo para demonstrar uma validação de negócio posterior.
Evite transformar o schema em um espelho de toda a sua base. Inclua somente os dados necessários para a próxima decisão. Campos demais aumentam complexidade, manutenção e risco de armazenar informações que não deveriam circular.
Gere a resposta estruturada com a Responses API
Agora crie o cliente e use responses.parse. A documentação atual recomenda começar novos projetos com a Responses API e mostra o parâmetro text_format para informar o modelo Pydantic:
from openai import OpenAI
client = OpenAI()
solicitacao = """
Não consigo acessar minha conta desde ontem.
Já redefini a senha duas vezes e preciso emitir uma nota hoje.
"""
response = client.responses.parse(
model="gpt-5.6",
input=[
{
"role": "system",
"content": (
"Classifique solicitações de suporte. "
"Não invente dados. Marque requer_humano como verdadeiro "
"quando houver urgência, bloqueio de acesso ou ambiguidade."
),
},
{"role": "user", "content": solicitacao},
],
text_format=Triagem,
)
triagem = response.output_parsed
if triagem is None:
raise RuntimeError("A resposta não pôde ser convertida para o schema.")
print(triagem.model_dump_json(indent=2))
O atributo output_parsed entrega uma instância de Triagem, não uma string para ser decodificada manualmente. O editor, os testes e o analisador de tipos passam a conhecer os campos disponíveis. Se o schema mudar, o ponto de integração fica explícito.
O prompt de sistema continua importante. Structured Outputs garante a forma, mas não torna verdadeira uma classificação ruim. Descreva critérios objetivos, forneça contexto suficiente e teste exemplos reais antes de automatizar uma consequência relevante.
Transforme o resultado em uma decisão de automação
Com o objeto validado, a aplicação pode decidir a fila sem interpretar linguagem natural novamente:
CATEGORIAS_PERMITIDAS = {
"financeiro",
"acesso",
"suporte",
"comercial",
}
def decidir_fila(triagem: Triagem) -> str:
categoria = triagem.categoria.strip().lower()
if categoria not in CATEGORIAS_PERMITIDAS:
return "revisao-manual"
if triagem.requer_humano or triagem.prioridade is Prioridade.alta:
return "atendimento-humano"
return f"fila-{categoria}"
fila = decidir_fila(triagem)
print({"fila": fila, "dados": triagem.model_dump()})
Observe que ainda validamos as categorias permitidas. O schema resolve o formato; a função protege uma regra local. Essa separação é saudável: o modelo organiza e classifica, enquanto o código controla quais ações podem acontecer.
O dicionário retornado pode ser enviado a um endpoint, gravado em uma tabela ou passado a uma ferramenta de automação. Se você estiver decidindo entre um fluxo visual e código, veja também quando usar n8n ou programação para automatizar processos. Uma arquitetura comum usa Python para a classificação tipada e n8n para orquestrar notificações, CRM e filas.
Trate recusas, falhas e validações operacionais
Uma resposta estruturada não elimina indisponibilidade de rede, limites da API, recusa do modelo ou entrada maliciosa. Em produção, trate esses casos como estados esperados, nunca como exceções impossíveis.
- Ausência de resultado analisado: não execute a ação. Envie o item para revisão ou uma fila de retentativa.
- Falha temporária: aplique tentativas limitadas com espera progressiva. Não crie um ciclo infinito.
- Regra sensível: pagamentos, bloqueios, decisões legais e alterações irreversíveis exigem confirmação humana.
- Dados pessoais: envie apenas o mínimo necessário e evite registrar a mensagem completa em logs.
- Observabilidade: registre identificador, duração, versão do schema, fila escolhida e tipo de falha, sem expor segredos.
- Testes: mantenha exemplos de mensagens urgentes, vagas, contraditórias e fora de escopo.
Também defina um limite de tamanho para a entrada e normalize o texto antes do envio. Em vez de aceitar qualquer categoria produzida, use enumerações sempre que o domínio for estável. Quando o objetivo for chamar funções ou ferramentas durante a resposta, use function calling; quando o objetivo for estruturar a resposta do próprio modelo, use Structured Outputs em text.format. Essa é a separação recomendada na documentação.
Checklist prático e próximo passo
- Criei um ambiente isolado e instalei
openaiepydantic. - Configurei
OPENAI_API_KEYfora do código. - Defini um modelo Pydantic pequeno e orientado à próxima decisão.
- Usei
responses.parsecomtext_format. - Verifiquei se
output_parsedexiste antes de agir. - Separei aderência ao schema de validação das regras de negócio.
- Criei uma rota segura para falhas, recusas e categorias desconhecidas.
- Adicionei logs mínimos, testes e revisão humana para ações sensíveis.
O salto de qualidade acontece quando a IA deixa de entregar “um texto que parece certo” e passa a participar de um sistema com contratos, limites e rotas de segurança. Structured Outputs torna a integração mais previsível; Pydantic aproxima o schema do código; e a função de decisão mantém a autoridade final na aplicação.
Como exercício, adapte Triagem ao seu contexto: leads, pedidos, currículos ou tickets internos. Separe dez entradas reais, defina a saída mínima que o próximo passo exige e teste os casos ambíguos. Na próxima evolução, esse mesmo objeto poderá alimentar uma API ou um fluxo completo de automação sem perder a rastreabilidade.