Como montar fallback entre Fable 5.1 e GPT-6 Astra quando um dos dois falha

diagrama de fallback entre Fable 5.1 e GPT-6 Astra com roteamento de erros
Resposta rápida

Montar um fallback entre Fable 5.1 e GPT-6 Astra é criar uma camada de roteamento que troca de provedor sem o app perceber. Você define um contrato interno único, escreve um adaptador para cada modelo respeitando as regras de cada API (o Fable 5.1 recusa tool_choice any ou tool e thinking desabilitado, o Astra não aceita temperature, top_p nem top_logprobs), deixa o SDK refazer o que é refazível e só troca de modelo em erro persistente, indisponibilidade ou saída que não valida no JSON Schema. No fim, normaliza a resposta e loga qual modelo atendeu

Fala aí, beleza? Depender de um modelo só em produção é combinar de cair junto com ele

Basta um 429, um 529 ou um 503 chegando na hora errada e a sua feature inteira vira tela de erro, mesmo que o outro provedor esteja respondendo lindamente ali do lado

Os dois modelos da vez são novinhos: o Claude Fable 5.1 saiu em 1 de setembro de 2026, junto com o Mythos 5.1, e o GPT-6 Astra começou como preview limitado em 3 de setembro e teve o rollout concluído em 6 de setembro, já disponível na API

A ideia deste post é uma só: desenhar a camada de roteamento que troca de provedor no meio do caminho sem o resto do app descobrir que a troca aconteceu

Bora ver na prática?

O que você precisa antes de montar o fallback

Nada de PC da Nasa aqui, é tudo configuração de projeto mesmo

  • Conta e chave de API nos dois provedores, porque fallback com um provedor só é só um retry com nome bonito
  • SDK oficial da Anthropic (Python ou TypeScript). Ele já refaz a requisição em 529, 503 e 500 por padrão, com até 2 tentativas, e o número é ajustável via max_retries na criação do client
  • SDK oficial da OpenAI a partir da versão 1.0.0, que refaz automaticamente 429 e 5xx com backoff exponencial e respeita o header Retry-After nas tentativas elegíveis
  • Um JSON Schema único pra resposta, já que os dois modelos suportam structured outputs por JSON Schema. É esse schema que vira o seu contrato
  • Log por requisição com modelo usado, código de erro e latência. Sem isso tu nunca vai saber quem atendeu o quê

Por que começar pelo schema e não pelo código? Porque o schema é a única coisa que os dois modelos têm em comum de verdade

O resto (nome do modelo, formato de ferramenta, parâmetros aceitos) muda de um pro outro, e é justamente isso que a sua camada vai esconder

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 114 aulas
  • 4 projetos
  • 9h 18min

Fable 5.1 x GPT-6 Astra: o que muda na chamada e precisa ser normalizado

Troca cega de modelo quebra por detalhe bobo

Se liga no que muda de um lado pro outro:

O que muda Claude Fable 5.1 GPT-6 Astra
Identificador do modelo claude-fable-5-1 na Claude API, Google Cloud, Microsoft Foundry e Claude Platform na AWS, e anthropic.claude-fable-5-1 no Amazon Bedrock gpt-6-astra
Contexto e saída máxima 1 milhão de tokens de contexto, até 128 mil tokens de saída por resposta 1.050.000 tokens de contexto, até 128.000 tokens de completion
Tool calling tool_choice do tipo any ou tool retorna 400 invalid_request_error. A orientação é usar tool_choice auto com ferramentas strict: true Tool calling exige a Responses API. Chat Completions atende requisições básicas
Thinking Enviar thinking: {type: "disabled"} retorna erro 400
Parâmetros de amostragem Não suporta temperature, top_p nem top_logprobs. Em Chat Completions, remova logprobs, e em Responses remova message.output_text.logprobs do include
Structured outputs JSON Schema em response_format JSON Schema informado em json_schema dentro de response_format
Preço de entrada e saída US$ 10,00 por milhão de entrada e US$ 50,00 por milhão de saída US$ 10,00 por milhão de entrada e US$ 50,00 por milhão de saída no Standard (Batch e Flex custam 50% do Standard, o modo Fast custa 2x)
Leitura de cache US$ 0,25 por milhão (queda de 75% em relação a US$ 1,00 do Fable 5) US$ 1,00 por milhão de leitura e US$ 12,50 por milhão de escrita
Prompt muito longo Acima de 272 mil tokens de entrada, cobra 2x nas tarifas de entrada e de cache e 1,5x na saída, valendo pra requisição inteira

Lendo a tabela de trás pra frente: o preço de entrada e saída é idêntico nos dois, então o custo não é o que decide a troca

O que decide é o formato da chamada

A sua camada de roteamento tem que esconder do app quatro coisas: o identificador do modelo, o jeito de pedir ferramenta, os parâmetros que um aceita e o outro recusa, e o caminho até o JSON Schema

Se o app precisar saber qualquer uma dessas quatro, o fallback já nasceu vazando 😅

Passo a passo para montar a camada de roteamento

  1. Definir o contrato interno

Uma função só, que recebe mensagens e um JSON Schema e devolve um objeto já validado

# contrato.py

def gerar(mensagens: list, schema: dict, ferramentas: list | None = None) -> dict:
    """Devolve sempre o mesmo formato:
    {"dados": {...}, "texto": str, "tool_calls": [...], "modelo": str}
    O app nao sabe (e nao precisa saber) quem respondeu
    """

O erro comum deste passo: deixar o nome do modelo entrar como argumento vindo do app. Aí não é camada de roteamento, é if espalhado pelo código todo

  1. Escrever o adaptador do Fable 5.1

Aqui valem duas regras duras: tool_choice auto com ferramentas strict: true, e nunca mandar thinking desabilitado

from anthropic import Anthropic

client_anthropic = Anthropic(max_retries=3)

def chamar_fable(mensagens, schema, ferramentas=None):
    kwargs = {
        "model": "claude-fable-5-1",  # anthropic.claude-fable-5-1 no Bedrock
        "max_tokens": 8192,
        "messages": mensagens,
        "response_format": {"type": "json_schema", "json_schema": schema},
    }
    if ferramentas:
        kwargs["tools"] = ferramentas       # cada ferramenta com strict: true
        kwargs["tool_choice"] = {"type": "auto"}
    return client_anthropic.messages.create(**kwargs)

O erro comum deste passo: copiar um código antigo que forçava ferramenta. tool_choice do tipo any ou tool retorna 400 invalid_request_error no Fable 5.1

O segundo erro comum: mandar thinking: {type: "disabled"} achando que economiza. Retorna 400 e pronto

  1. Escrever o adaptador do GPT-6 Astra

Se tem ferramenta na jogada, é Responses API. Chat Completions fica pras requisições básicas

from openai import OpenAI

client_openai = OpenAI(max_retries=3)

def chamar_astra(mensagens, schema, ferramentas=None):
    kwargs = {
        "model": "gpt-6-astra",
        "input": mensagens,
        "response_format": {"type": "json_schema", "json_schema": schema},
        # nada de temperature, top_p ou top_logprobs aqui
    }
    if ferramentas:
        kwargs["tools"] = ferramentas
    return client_openai.responses.create(**kwargs)

O erro comum deste passo: arrastar temperature e top_p do preset antigo do time. O Astra não suporta esses parâmetros, nem top_logprobs

E se tu usa Responses com include, lembra de tirar message.output_text.logprobs de lá

  1. Camada de retry no mesmo modelo, antes de pensar em trocar

Deixa o SDK trabalhar, ele já sabe fazer isso

É o MESMO client dos passos 2 e 3, criado uma vez só, com o número de tentativas escolhido ali na criação

client_anthropic = Anthropic(max_retries=3)   # o padrao do SDK sao 2 tentativas
client_openai = OpenAI(max_retries=3)

O SDK da Anthropic já refaz 529, 503 e 500 por padrão, com até 2 tentativas ajustáveis

O SDK da OpenAI, da 1.0.0 em diante, refaz 429 e 5xx com backoff exponencial e respeita o Retry-After

O erro comum deste passo: escrever o seu próprio loop de retry POR CIMA do retry do SDK. Aí tu multiplica tentativa por tentativa e a conta cresce sozinha

  1. Definir o gatilho de troca

São três, e só três: erro persistente depois das tentativas, indisponibilidade do provedor, e saída que não valida no schema

def gerar(mensagens, schema, ferramentas=None):
    ordem = [("claude-fable-5-1", chamar_fable), ("gpt-6-astra", chamar_astra)]
    ultimo_erro = None

    for nome, chamar in ordem:
        try:
            bruto = chamar(mensagens, schema, ferramentas)
            return normalizar(bruto, nome, schema)   # valida contra o schema aqui dentro
        except ErroDeCliente:      # 400, 401, 403, 404: nao troca, e bug seu
            raise
        except (ErroDeCapacidade, SaidaInvalida) as e:
            ultimo_erro = e
            continue

    raise ultimo_erro

O erro comum deste passo: cair pro outro modelo em 400. Erro de cliente nunca deve ser refeito, e trocar de provedor só troca a mensagem de erro que tu vai ler no log

  1. Normalizar a resposta antes de devolver ao app

Mesmo objeto, mesmos campos, texto e tool calls no mesmo formato, sempre

def normalizar(bruto, modelo, schema):
    dados = extrair_json(bruto)          # cada provedor tem seu caminho ate o conteudo
    if not valida(dados, schema):
        raise SaidaInvalida(modelo)
    return {
        "dados": dados,
        "texto": extrair_texto(bruto),
        "tool_calls": extrair_tool_calls(bruto),   # nome + argumentos, no seu formato
        "modelo": modelo,
    }

O erro comum deste passo: normalizar só o texto e deixar as tool calls no formato cru de cada API. Aí o app funciona no primário e explode no fallback, que é o pior momento possível pra descobrir isso

  1. Observabilidade e idempotência

Cada requisição precisa carregar uma chave de tarefa, pra que uma troca de modelo no meio não gere trabalho (e cobrança) duplicado

log.info("geracao", extra={
    "tarefa_id": tarefa_id,
    "modelo": resultado["modelo"],
    "tentativas": tentativas,
    "codigo_erro": codigo,
    "latencia_ms": latencia,
})

Se o fluxo for longo e cheio de ferramenta, dá pra ganhar visibilidade no lado do Claude usando os progress updates em beta e acompanhar o que acontece entre as tool calls

O erro comum deste passo: logar só o erro final. Sem registrar QUAL modelo atendeu cada requisição, tu não consegue nem explicar a variação de custo no fim do mês

Qual erro deve refazer, qual deve trocar de modelo

Aqui é onde a maioria dos fallbacks erra: trata todo erro igual

429 na Claude API

  • Sintoma: a requisição volta com rate_limit_error
  • Causa: a organização bateu um limite de taxa, o teto mensal de gasto do tier ou um limite de gasto do workspace. Os limites são medidos por requisições por minuto (RPM), tokens de entrada por minuto (ITPM) e tokens de saída por minuto (OTPM)
  • Solução: ler o header retry-after, que vem em segundos, e esperar o tempo indicado antes de tentar de novo
  • Como prevenir: distribuir a carga no minuto e olhar as três métricas separadas, porque estourar OTPM com resposta longa é diferente de estourar RPM com muita chamada curta

529 na Claude API

  • Sintoma: overloaded_error
  • Causa: a API está temporariamente sobrecarregada do lado da Anthropic, não é problema do seu uso
  • Solução: esse é o candidato mais claro a troca de provedor, porque não existe valor de retry-after confiável pra seguir aqui
  • Como prevenir: não dá pra prevenir do seu lado, e é exatamente por isso que o fallback existe

429 na API da OpenAI

  • Sintoma: 429 com type rate_limit_error e code slow_down
  • Causa: ritmo de requisições acima do que o serviço aceita, e pode ser também saldo pré-pago esgotado ou limite de gasto atingido
  • Solução: backoff mais longo. Se vier header Retry-After, esperar pelo menos o tempo indicado
  • Como prevenir: monitorar saldo e limite de gasto junto com o ritmo. Já me ferrei com isso: parece rate limit, e no fim era saldo

503 na API da OpenAI

  • Sintoma: 503 com type service_unavailable_error e code server_is_overloaded
  • Causa: o modelo pedido está sem capacidade no momento
  • Solução: backoff mais longo e, se persistir, cair pro outro provedor
  • Como prevenir: mesma coisa do 529, é capacidade do outro lado

A regra geral de o que refazer

A classificação da OpenAI é bem direta e serve de guia pro seu roteador inteiro:

  • erros de cliente (400, 401, 403, 404) nunca devem ser refeitos
  • erros de servidor (500, 502) devem ser refeitos com backoff
  • erros de capacidade (429, 503) pedem backoff mais longo

E a saída fora do formato esperado?

Essa não tem código HTTP nenhum: chega 200, chega bonita, e vem quebrada pro seu app

Por isso a validação contra o JSON Schema entra dentro da camada, antes de devolver qualquer coisa

Se não validou, trata como falha e segue pro próximo adaptador, igual erro de capacidade

Quando vale acionar o outro modelo (e quando não vale)

Escolher o primário não é torcida, é perfil de carga

Carga com muita releitura de contexto: o Fable 5.1 leva vantagem pela leitura de cache a US$ 0,25 por milhão, contra US$ 1,00 por milhão no Astra. Entrada e saída custam igual nos dois, então o cache é onde a diferença aparece de verdade

Prompt muito longo: presta atenção no corte de 272 mil tokens de entrada do Astra. Acima disso, a cobrança vira 2x nas tarifas de entrada e de cache e 1,5x na saída, valendo pra requisição inteira. Um fallback que joga um prompt gigante pro Astra sem avisar ninguém consegue ser caro e silencioso ao mesmo tempo

Fluxo que dependia de uso obrigatório de ferramenta: esse precisa ser repensado antes de virar fallback, porque o Fable 5.1 removeu o uso forçado. Com tool_choice auto e ferramentas strict: true, o desenho do prompt passa a ter mais peso

E tem o outro lado da moeda, que é saber quando NÃO acionar

Erro de cliente (400, 401, 403, 404) é bug seu: schema errado, chave errada, parâmetro que o modelo não aceita. Trocar de provedor aí só esconde o problema, e ele volta em dobro depois

Vale também o exercício de custo antes de escolher o primário, porque existem tarefas em que o Astra é exagero e um modelo mais barato entrega igual

Vídeo: comparativo de modelos no canal

Pra começar do zero na comparação entre modelos de topo, este vídeo do canal traz um panorama do assunto, colocando modelos frente a frente em teste

Conclusão

Fallback bom é aquele que ninguém percebe

O app manda mensagens e um schema, recebe um objeto validado de volta, e nunca fica sabendo se quem respondeu foi o claude-fable-5-1 ou o gpt-6-astra

O próximo passo é bem concreto: implementa o adaptador de UM dos dois primeiro, valida a saída contra o JSON Schema em ambiente de teste, confere se as tool calls saem no seu formato normalizado e só depois disso liga a troca automática

E liga o log de qual modelo atendeu cada requisição desde o primeiro dia, porque no dia do 529 tu vai querer olhar esse gráfico 😀

até o próximo post!

Perguntas frequentes

Qual a diferença entre erro 429 e 529 na Claude API na hora de montar o fallback?

O 429 rate_limit_error indica que a organização bateu um limite de taxa, o teto mensal de gasto do tier ou um limite de gasto do workspace, e vem com o header retry-after em segundos. Já o 529 overloaded_error é sobrecarga temporária do lado da Anthropic, sem relação com o seu uso, e não traz um retry-after confiável para seguir. Por isso o roteamento não pode tratar os dois do mesmo jeito.

O código slow_down no erro 429 da OpenAI sempre é limite de requisições?

Não necessariamente. O 429 com type rate_limit_error e code slow_down indica ritmo de requisições acima do que o serviço aceita, mas também pode significar saldo pré-pago esgotado ou limite de gasto atingido. Vale sempre checar o header Retry-After quando ele vier na resposta.

O SDK oficial já resolve sozinho o fallback entre Fable 5.1 e GPT-6 Astra?

Não, cada SDK só refaz a própria chamada dentro do próprio provedor. O SDK da Anthropic já refaz 529, 503 e 500 com até 2 tentativas por padrão, ajustável via max_retries, e o da OpenAI, a partir da versão 1.0.0, refaz 429 e 5xx com backoff exponencial respeitando o Retry-After. Trocar de provedor no meio do caminho é a parte que fica por conta da sua camada de roteamento.

Dá pra usar o mesmo JSON Schema no Fable 5.1 e no GPT-6 Astra sem adaptar nada?

O schema em si pode ser o mesmo, mas o campo onde ele entra muda: no Fable 5.1 ele vai direto em response_format, e no GPT-6 Astra vai em json_schema dentro de response_format. Os dois suportam structured outputs via JSON Schema, então é diferença de encaixe, não de conteúdo.

Faz diferença de custo escolher Fable 5.1 ou GPT-6 Astra no fallback?

No preço padrão de entrada e saída, não: os dois cobram US$ 10,00 por milhão de tokens de entrada e US$ 50,00 por milhão de saída. A diferença aparece na leitura de cache, US$ 0,25 por milhão no Fable 5.1 contra US$ 1,00 por milhão no GPT-6 Astra, e no GPT-6 Astra acima de 272 mil tokens de entrada, faixa em que a tarifa sobe pra 2x na entrada e no cache e 1,5x na saída.

Preciso usar a Responses API para chamar ferramentas no GPT-6 Astra?

Sim, a orientação atual da OpenAI é que tool calling no GPT-6 Astra exige a Responses API. A Chat Completions atende só requisições básicas ao modelo, sem cobrir o uso de ferramentas.




Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares