Validando e Analisando Respostas Estrutadas em Python
Definir um esquema é apenas metade do trabalho - você também precisa transformar a resposta que recebe de volta em um objeto Python utilizável e saber o que fazer quando essa resposta não está completa.
Esta página cobre client.messages.parse(), o helper baseado em Pydantic que faz a validação e desserialização para você, além do caminho manual com client.messages.create() para quando você precisar de mais controle.
client.messages.parse() é a maneira recomendada de consumir uma saída estruturada em Python: passe um esquema (tipicamente de um modelo Pydantic) e ele retorna uma resposta cuja atributo parsed_output já é uma instância validada desse modelo.
Isso substitui a sequência manual de extrair texto da resposta, chamar json.loads() e, em seguida, construir seu próprio objeto a partir do dict.
Você ainda pode usar client.messages.create() diretamente quando quiser o conteúdo bruto da resposta, por exemplo, para inspecionar stop_reason antes de decidir se a saída é segura para analisar.
Ambos os caminhos usam o mesmo output_config.format de esquema por baixo dos panos - parse() é uma camada de conveniência, não um recurso de API diferente.
from pydantic import BaseModel, ValidationErrorfrom anthropic import Anthropic, APIStatusErrorclass SupportTicket(BaseModel): subject: str urgency: str customer_email: strclient = Anthropic()def extract_ticket(raw_text: str) -> SupportTicket | None: try: response = client.messages.parse( model="claude-sonnet-5", max_tokens=1024, output_config={ "format": {"type": "json_schema", "schema": SupportTicket.model_json_schema()} }, messages=[{"role": "user", "content": f"Extrair campos de ticket de:\n\n{raw_text}"}], ) except APIStatusError as e: print(f"Erro de API: {e.status_code} {e.message}") return None if response.stop_reason == "max_tokens": print("Resposta foi truncada antes de completar - não é seguro confiar.") return None if response.stop_reason == "refusal": print("Claude recusou-se a produzir uma resposta para esta entrada.") return None return response.parsed_outputticket = extract_ticket( "De: jane@example.com\nAssunto: Não consigo fazer login\n\n" "Fui bloqueado da minha conta desde esta manhã, por favor ajude o mais rápido possível.")if ticket: print(ticket.subject, ticket.urgency, ticket.customer_email)
O que isso demonstra:
stop_reason é verificado antes de confiar em response.parsed_output, porque uma resposta truncada ou recusada não garante um valor analisado utilizável.
APIStatusError captura falhas de rede/API separadamente de problemas de nível de conteúdo, como truncamento ou recusa.
A função retorna None em qualquer caminho de falha em vez de deixar uma exceção de uma resposta parcialmente formada propagar inesperadamente.
response.parsed_output só é lido depois que todas as verificações anteriores foram aprovadas.
client.messages.parse() envia a mesma solicitação que client.messages.create(), com o esquema construído a partir do seu modelo Pydantic (ou dict bruto) passado via output_config.format.
Assim que a resposta retorna, o SDK valida o texto JSON retornado contra o esquema e constrói uma instância do modelo que você forneceu, expondo-a como response.parsed_output.
O objeto response subjacente ainda tem os mesmos campos de um Message normal - stop_reason, usage, content - parsed_output é aditivo, não uma substituição.
A validação neste nível confirma que a resposta corresponde à forma descrita pelo esquema; ela não verifica se os valores estão factualmente corretos.
import jsonfrom anthropic import Anthropicclient = Anthropic()schema = { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"], "additionalProperties": False,}response = client.messages.create( model="claude-sonnet-5", max_tokens=64, # deliberadamente pequeno - provavelmente truncará em uma resposta longa output_config={"format": {"type": "json_schema", "schema": schema}}, messages=[{"role": "user", "content": "Escreva um resumo extremamente detalhado de vários parágrafos."}],)raw_text = response.content[0].textif response.stop_reason == "max_tokens": print("Truncado - não tente json.loads() nisso, tente novamente com mais max_tokens em vez disso.")else: data = json.loads(raw_text) print(data["summary"])
Usar create() diretamente aqui torna a verificação de truncamento explícita e visível, o que é útil quando você deseja controle total sobre o caminho de falha em vez de depender do tratamento interno de parse().
Chamar json.loads() em uma string conhecida como truncada levantará json.JSONDecodeError - verificar stop_reason primeiro evita atingir essa exceção no fluxo de controle normal.
from pydantic import BaseModel, field_validatorclass Invoice(BaseModel): vendor: str total: float @field_validator("total") @classmethod def total_must_be_positive(cls, v: float) -> float: if v < 0: raise ValueError("total must be non-negative") return v
Validadores Pydantic ainda são executados quando client.messages.parse() constrói parsed_output - uma resposta válida de esquema que falha em um validador personalizado levanta um pydantic.ValidationError, que é um modo de falha distinto de uma resposta de API malformada.
Isso é útil para restrições que a camada JSON Schema não impõe (como intervalos numéricos), permitindo que Pydantic capture o que a garantia de nível de esquema não pode.
Ler response.parsed_output antes de verificar stop_reason. Em uma resposta truncada, o valor analisado pode estar ausente ou o próprio passo de análise pode ter falhado. Correção: verifique stop_reason != "max_tokens" (e != "refusal") antes de acessar parsed_output.
Assumir que parse() e create() precisam de esquemas diferentes. Eles usam exatamente o mesmo esquema output_config.format - a única diferença é o que o SDK faz com a resposta depois. Correção: defina o esquema uma vez e passe-o para qualquer chamada que você usar.
Não capturar pydantic.ValidationError quando você tem validadores personalizados no modelo. Uma resposta válida de esquema ainda pode falhar em uma restrição de nível Pydantic, como um field_validator personalizado. Correção: envolva a chamada parse() em um try/except que capture tanto erros de API quanto erros de validação Pydantic se o seu modelo tiver lógica de validação personalizada.
Tratar uma análise bem-sucedida como prova de que os dados estão corretos. A validação confirma a forma e quaisquer restrições de nível Pydantic, não que os valores extraídos sejam factualmente precisos. Correção: adicione uma etapa de revisão ou verificação posterior para qualquer coisa de alto risco.
Esquecer que create() ainda retorna texto que você deve json.loads() você mesmo. Desenvolvedores às vezes esperam que create() analise automaticamente da maneira que parse() faz. Correção: use parse() quando quiser o objeto; use create() apenas quando você especificamente quiser o texto bruto ou mais controle manual sobre o tratamento da resposta.
Ambos enviam a mesma forma de solicitação com o mesmo esquema output_config.format.
parse() adicionalmente valida e desserializa a resposta em response.parsed_output; create() deixa você para chamar json.loads() você mesmo.
Preciso de um modelo Pydantic para usar parse()?
Um modelo Pydantic é o padrão comum porque model_json_schema() gera o esquema e lhe dá um valor de retorno tipado.
Um dict de esquema bruto também funciona com parse(), mas você perde a conveniência do objeto tipado - parsed_output se torna um dict simples nesse caso.
Devo verificar stop_reason antes ou depois de chamar parse()?
Verifique no objeto response retornado antes de confiar em parsed_output, da mesma forma que faria com create().
Um motivo de parada max_tokens ou refusal significa que o conteúdo pode não estar completo ou utilizável, independentemente de qual método você chamou.
Que tipos de exceção devo capturar em torno de uma chamada parse()?
Falhas de nível de API (rede, autenticação, limites de taxa) aparecem como anthropic.APIStatusError e suas subclasses.
Problemas de nível de conteúdo, como um validador Pydantic personalizado falhando, aparecem como pydantic.ValidationError - capture ambos se o seu modelo tiver validação personalizada.
É garantido que response.parsed_output não seja None?
Somente quando a resposta foi concluída com sucesso e passou na validação - sempre verifique stop_reason e trate exceções antes de assumir que está preenchido.
Posso ainda acessar o texto bruto ao lado de parsed_output?
Sim - o objeto response retornado por parse() ainda carrega o campo content normal com os blocos de texto brutos, além de parsed_output.
parse() tenta novamente automaticamente em caso de truncamento?
Não - parse() não tenta novamente automaticamente com um max_tokens maior. Você precisa detectar o truncamento via stop_reason e implementar sua própria lógica de retentativa.
Validadores Pydantic podem rejeitar uma resposta que é válida em esquema?
Sim - um field_validator ou model_validator em seu modelo Pydantic é executado após a verificação do JSON Schema passar, então ele ainda pode rejeitar valores que o próprio esquema permitiu.
É mais lento usar parse() em vez de create()?
A chamada da API é idêntica - parse() apenas adiciona uma etapa de validação/desserialização no lado do cliente após a resposta chegar, o que é insignificante em comparação com a latência da rede.
Como fica parsed_output se eu passar um dict de esquema bruto em vez de um modelo Pydantic?
É um dict Python simples que corresponde à forma do esquema, em vez de uma instância de modelo tipada.
Use um modelo Pydantic quando quiser acesso a atributos e tipagem estática em vez de indexação de dict.
Posso reutilizar o mesmo modelo Pydantic para parse() e json.loads() manual?
data = json.loads(raw_text)invoice = Invoice.model_validate(data)
Sim - Model.model_validate(data) faz a mesma etapa de validação que parse() faz internamente, útil se você estiver no caminho manual create() mas ainda quiser um objeto tipado.
Versões da Stack: Escrito contra a linha de modelos Claude atual a partir de ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5 - e o SDK oficial anthropic para Python (última versão 0.x). Nomes de modelos, versões de SDK e preços mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs antes de confiar neles.
Revisado por Chris St. John·Última atualização: 13 de jul. de 2026