Jev falhou ou demorou demais: como montar o plano B da sua camada de decisão?

Fallback do Jev é o que sua aplicação faz quando a resposta tipada não chega: rota padrão segura, fila para decidir depois e regra de emergência. A separação básica: 401 (chave) e 422 (corpo) são erro seu e não pedem retry, enquanto 429 e 529 pedem backoff exponencial, que os SDKs oficiais já fazem respeitando o header retry-after. Defina o orçamento de tempo, enquadre a pergunta para que escalar seja o caso verdadeiro e registre request_id (x-typesafe-request-id), model resolvido, answers com confidence e usage para reprocessar depois
Uma camada de decisão sem plano B não é uma camada, é um ponto único de falha
Fala aí, beleza? O Jev é o primeiro modelo do que a TypeSafe AI chama de System One Model: tu manda um estado e perguntas tipadas, ele devolve valores tipados com probabilidade calibrada, sem gerar texto
Isso é ótimo porque a saída entra direto no seu if
E é justamente aí que mora o perigo: quando uma resposta tipada vira o eixo de uma decisão de negócio, qualquer 401, 422, 429, 529 ou uma simples demora acima do aceitável vira uma pergunta que alguém vai ter que responder às pressas em produção
O que a sua aplicação faz quando a decisão não chega?
É isso que a gente vai desenhar aqui: rota padrão segura, fila pra decidir depois, regra simples de emergência e o que registrar pra reprocessar
O que você precisa antes de montar o plano B
A parte chata primeiro, que é rápida
O acesso ao Jev está aberto, a lista de espera foi encerrada, então qualquer pessoa entra em console.typesafe.ai e cria uma chave
Depois:
pip install typesafe-sdk- a variável de ambiente
TYPESAFE_API_KEYno ambiente onde o serviço roda (oTypeSafeClientlê ela sozinho) - decidir entre
TypeSafeClienteAsyncTypeSafeClient: a interface é a mesma, o assíncrono existe pra serviço assíncrono, sem desculpa pra bloquear o loop - e o modelo padrão já é o
jev-latest, então tu não precisa passar nada pra começar
Se a sua stack não é Python, o endpoint é POST https://api.typesafe.ai/v1/systemone com autenticação Bearer no header Authorization
curl https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d @payload.json
Curso de Jev: Sistemas Mais Inteligentes, Rápidos e Robustos
Desenvolva sistemas mais inteligentes, rápidos e robustos com Jev
Tem um último pré-requisito que não é técnico e é o mais importante de todos: saber qual decisão de negócio essa chamada alimenta
Porque a rota padrão segura depende disso
Sem resposta do modelo, o pedido é aprovado ou barrado? O conteúdo fica publicado ou vai pra revisão? Não dá pra responder isso no meio de um incidente 🙂
Como montar o plano B da camada de decisão, passo a passo
- Separe erro seu de erro do serviço
A API retorna status HTTP padrão com corpo JSON descrevendo o problema
Os documentados incluem 401 (chave ausente ou inválida), 422 (falha de validação do corpo da requisição), 429 (Too Many Requests) e 529 (Overloaded)
A divisão mental é essa:
| Status | De quem é o problema | Retry resolve? | O que fazer |
|---|---|---|---|
| 401 | seu | não | corrigir chave/env e derrubar o deploy quebrado |
| 422 | seu | não | validar o corpo antes de enviar |
| 429 | limite | sim, com backoff | backoff e respeitar retry-after |
| 529 | serviço | sim, com backoff | backoff e circuit breaker |
O erro comum deste passo: colocar tudo num except só e mandar repetir
401 repetido três vezes continua sendo 401, tu só gastou o orçamento de tempo do usuário pra chegar no mesmo lugar
- Ajuste o RetryPolicy conscientemente
A doc orienta backoff exponencial em 429 e 529 em vez de tentar de novo imediatamente, e os SDKs oficiais já fazem isso por padrão, respeitando o header retry-after quando ele vem
Os defaults do RetryPolicy do SDK Python (quando retry=None) são: max_retries=2, backoff_initial=0.5, backoff_max=5.0, backoff_jitter=0.25, http_statuses com 408, 429 e a faixa 500 a 599, e respect_retry_after=True
Repara numa coisa massa: 401 e 422 não estão nessa lista de status
O SDK já não repete o que não adianta repetir
from typesafe_sdk import TypeSafeClient
client = TypeSafeClient()
# override por chamada: backoff_max de 0.2s e timeout de 1.0s seguram o pior caso
resposta = client.system_one(
state=estado,
questions=perguntas,
retry=RetryPolicy(max_retries=3, backoff_max=0.2, timeout=1.0),
)
E quando o caminho simplesmente não pode esperar, tu desliga:
resposta = client.system_one(
state=estado,
questions=perguntas,
retry=RetryPolicy(max_retries=0),
)
A política é configurável no cliente e por chamada, então dá pra ter um default generoso no cliente e um override apertado na rota crítica
O erro comum deste passo: usar os mesmos retries em todo lugar
Job noturno e checkout não têm a mesma paciência, óbvio
- Defina o orçamento de tempo do caminho, não só o timeout da chamada
Backoff mais retry SOMA latência
Se tu permite 2 retries com espera crescente, o pior caso não é o timeout de uma chamada, é a soma de todas elas mais as esperas
Então a pergunta certa não é "quanto tempo o Jev demora", é "quanto tempo esse caminho de decisão pode gastar no total antes de eu seguir sem ele"
Escreve esse número em algum lugar, de preferência em constante com nome
Quem já apanhou de lentidão do GPT-6 Astra em produção sabe que orçamento de tempo indefinido vira timeout do usuário, não do serviço
E se tu quiser aprofundar só a parte de timeout e fallback no Jev, tem material dedicado pra isso
O erro comum deste passo: definir timeout generoso "por segurança"
Timeout generoso não é segurança, é enfileirar usuário na sua própria aplicação
- Escreva a rota padrão segura ANTES da chamada
Rota padrão segura é o que a aplicação decide sozinha quando não tem resposta
Aqui tem um truque de modelagem que o cookbook de cascata da TypeSafe já entrega: enquadre a pergunta de escalonamento pra que o caso "escalar" seja o caso verdadeiro
Por quê? Porque assim a ausência de resposta cai naturalmente pro lado conservador, sem você ter que inventar uma segunda lógica invertida no except
DECISAO_PADRAO = "escalar" # conservador de propósito
def decidir(estado, perguntas):
try:
resposta = client.system_one(state=estado, questions=perguntas)
except Exception:
registrar_falha(estado, perguntas)
return DECISAO_PADRAO
return aplicar_regras(resposta)
O padrão do cookbook é esse: o modelo propõe, o CÓDIGO decide
O erro comum deste passo: deixar a rota padrão implícita num return None que vira False lá na frente e libera tudo caladinho 😛
- Reduza a superfície de falha: N perguntas, uma chamada
Uma chamada system_one responde N perguntas num passe paralelo, e cada pergunta é pontuada isoladamente contra o estado
Ou seja: a resposta de uma não depende do que mais está na requisição
No cookbook de perguntas paralelas, juntar tudo numa chamada única saiu 12,2x mais barato e 10,0x mais rápido que uma requisição por pergunta, sem mudança nas respostas
Pra plano B isso importa por um motivo extra: cinco chamadas são cinco lugares onde a coisa pode falhar pela metade
Uma chamada falha inteira ou responde inteira, e isso é MUITO mais fácil de tratar
Só fica de olho no orçamento de contexto: 64k cobre o estado mais todas as perguntas somadas, e 32k se aplica ao estado mais a pergunta individual mais longa
O erro comum deste passo: jogar o estado inteiro do usuário no payload "porque sim"
A acurácia cai conforme o estado cresce com conteúdo irrelevante pra decisão, isso está documentado
- Regra simples de emergência: circuit breaker
Depois de X falhas seguidas, para de chamar
Manda todo mundo pra rota padrão por um intervalo e tenta de novo depois
import time
falhas = 0
aberto_ate = 0.0
LIMITE = 5
PAUSA = 30 # segundos
def decidir_com_breaker(estado, perguntas):
global falhas, aberto_ate
if time.time() < aberto_ate:
return DECISAO_PADRAO # circuito aberto: rota padrão explícita
try:
r = client.system_one(state=estado, questions=perguntas)
except Exception:
falhas += 1
if falhas >= LIMITE:
aberto_ate = time.time() + PAUSA
registrar_falha(estado, perguntas)
return DECISAO_PADRAO
falhas = 0
return aplicar_regras(r)
Repara que aqui NÃO tem return None: o breaker devolve a mesma DECISAO_PADRAO do passo 4, tanto com o circuito aberto quanto na falha
É o mesmo caminho conservador, só que agora sem nem gastar a chamada
Isso protege os dois lados: tu para de queimar tempo do usuário e para de martelar um serviço que já está sobrecarregado
A checagem humana que acompanha isso é a página pública status.typesafe.ai
O erro comum deste passo: circuit breaker que abre e nunca fecha porque ninguém escreveu o caminho de volta
- Fila pra decidir depois
Nem toda decisão precisa ser agora
Quando o caminho tolera espera, enfileira o estado e as perguntas exatamente como iriam na requisição, e reprocessa com o MESMO payload
item = {
"state": estado,
"questions": perguntas,
"tentativas": 0,
}
fila.publicar(item)
Como cada pergunta é pontuada isoladamente contra o estado, reprocessar o mesmo payload é uma operação bem previsível
O erro comum deste passo: enfileirar só o ID do registro e reconstruir o estado na hora do reprocessamento
Aí o estado mudou, a decisão sai diferente e ninguém entende por quê
- Registre o que permite reprocessar e auditar
Sem isso, plano B vira adivinhação
O mínimo:
registro = {
"request_id": request_id, # header x-typesafe-request-id
"model": model, # versão resolvida, ex: jev-1.13.0
"question_ids": list(perguntas.keys()),
"answers": answers, # Choice e Score trazem confidence de 0 a 1
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"state": estado,
}
Detalhe importante: Choice e Score carregam uma confiança entre 0 e 1 derivada da distribuição da resposta, e o Noul NÃO tem campo de confiança separado, porque a distribuição dele tem só dois desfechos (yes e no) e o próprio valor já descreve ela por completo
Então não fica procurando confidence no Noul, não é bug 😀
O erro comum deste passo: logar só a decisão final
Quando alguém perguntar "por que isso foi aprovado dia 12", tu vai querer o model resolvido e o request_id, não um booleano solitário
Três caminhos de decisão e o plano B que cada um pede
Não existe um plano B universal
Existem três classes de caminho, e cada uma pede uma configuração diferente:
| Caminho | Timeout e retries | Rota padrão | Fila |
|---|---|---|---|
| Síncrono no pedido do usuário | curto, retries mínimos ou zero | imediata e conservadora | opcional, só pra auditoria |
| Batch ou job noturno | generoso, backoff à vontade | não precisa | sim, com reprocessamento |
| Moderação e risco | curto | escalar pra revisão humana | sim, pra fila de revisão |
(a) Decisão síncrona no pedido do usuário
Aqui o inimigo é a soma de esperas
RetryPolicy(max_retries=0) ou um override bem apertado, e a rota padrão dispara na hora
(b) Decisão em batch ou job noturno
Tempo aqui é barato
Retries generosos, backoff tranquilo, e o que falhar vai pra fila e é reprocessado com o mesmo payload
Nesse caminho a rota padrão costuma nem existir: é melhor não decidir do que decidir errado sem revisão
(c) Moderação e risco, onde errar barato é bloquear
Rota padrão = escalar pra revisão humana
E tem um detalhe de agregação que o cookbook de cascata deixa explícito: agregue flags por campo com max, pra que uma bandeira vermelha confiante escale em vez de ser diluída na média de dez campos tranquilos
risco = max(flags_por_campo) # bandeira vermelha confiante não se dilui
E olha, vale lembrar do óbvio que a gente esquece: baixa confiança JÁ deveria descer pra algo mais lento e mais capaz (modelo de raciocínio maior ou pessoa), mesmo quando a API respondeu normalmente
Plano B não é só pra erro de rede, é pra resposta fraca também
Falhas e lentidões mais comuns: sintoma, causa e o que fazer
401: chave ausente ou inválida
Sintoma: todas as chamadas falhando desde o deploy
Causa: TYPESAFE_API_KEY não chegou no ambiente
O que fazer: não é hora de retry, é hora de olhar o deploy
Como prevenir: checagem de env no start do serviço, falhando cedo e alto
422: falha de validação do corpo
Sintoma: uma rota específica quebrou, o resto está de pé
Causa: contrato de pergunta quebrado, payload fora do formato esperado
O que fazer: validar antes de enviar, e tratar como bug, não como instabilidade
Como prevenir: teste que monta o payload real das perguntas em CI
429: Too Many Requests
Sintoma: funciona no pico baixo, quebra no pico alto
Causa: estourou o limite de tokens por segundo ou de requisições por minuto, e esses limites estão sendo ajustados dinamicamente por causa do volume de demanda, podendo mudar sem aviso
O que fazer: backoff exponencial, respeitar retry-after, e agrupar perguntas numa chamada só em vez de disparar N
529: Overloaded
Sintoma: erro intermitente que não tem a ver com o seu código
Causa: lado do serviço
O que fazer: backoff, circuit breaker e uma olhada em status.typesafe.ai antes de sair caçando fantasma no seu log
"Respondeu rápido, mas respondeu errado"
Esse é o mais traiçoeiro, porque não dispara alarme nenhum
A TypeSafe mantém uma página de jaggedness datada pro jev-1.13, com modos de falha conhecidos e um workaround recomendado pra cada um, revisada em 17 de setembro de 2026
Entre eles:
- ele responde a pergunta que você ESCREVEU, não a que você quis dizer: negações e condições implícitas são lidas ao pé da letra
- não é calculadora e não conta de forma confiável: aritmética pertence ao código
- lê datas como texto, não como quantidades ordenadas
- a acurácia cai conforme o estado cresce com conteúdo irrelevante pra decisão
Plano B também é isso: saber quando uma resposta bem-sucedida não deveria decidir sozinha
Próximo passo: escreva a rota padrão antes de escrever a chamada
A ordem usual é escrever a integração e depois, um dia, tratar o erro
Inverte
Primeiro define o que a aplicação faz sem resposta, depois pluga o Jev nesse esqueleto
O plano de ação prático pra esta semana:
- mapear cada chamada em uma das três classes de caminho (síncrona, batch, risco)
- definir o orçamento de tempo e a rota padrão de cada uma
- passar a registrar
request_idemodelresolvido desde já, mesmo que ninguém vá olhar hoje
E um argumento a favor de retry e reprocessamento sem culpa: o Jev cobra por token de entrada a US$ 0,042 por milhão, com tokens de saída a US$ 0,00
Uma decisão reprocessada não vai quebrar o orçamento, uma decisão errada silenciosa pode quebrar outras coisas…
Fecha o loop acompanhando a página de jaggedness e o status do serviço, porque camada de decisão sem plano B envelhece mal
Até o próximo post! =)
Perguntas frequentes
O que fazer quando o Jev retorna 529 Overloaded em produção?
529 é problema do serviço, não seu, e a própria doc orienta backoff exponencial em vez de repetir na hora. O SDK Python já faz isso por padrão (max_retries=2, backoff_initial=0.5, backoff_max=5.0) e respeita o header retry-after quando ele vem. Se o caminho não pode esperar, esse é o cenário certo pra cair na rota padrão segura em vez de insistir.
Qual a diferença entre retry e fallback no Jev?
Retry é repetir a mesma chamada esperando que ela funcione da próxima vez, útil em 429 e 529. Fallback é o que a aplicação decide sozinha quando os retries se esgotam ou quando o erro não tem retry que resolva, como 401 e 422. Um plano de Jev fallback maduro usa os dois, retry pra falha temporária e fallback pra quando a resposta simplesmente não vem.
Dá para desligar os retries automáticos do SDK do Jev?
Sim, passando RetryPolicy(max_retries=0) no cliente ou por chamada. Isso é útil quando o orçamento de tempo do caminho é curto e você prefere cair direto na rota padrão segura em vez de esperar o backoff rodar. A política aceita outros parâmetros também, como backoff_max e timeout, pra ajustar caso a caso.
Por que repetir uma chamada que deu 401 ou 422 não resolve nada?
401 é chave ausente ou inválida e 422 é falha de validação do corpo da requisição, os dois são erro seu, não do serviço. Repetir a mesma chamada errada três vezes devolve 401 ou 422 três vezes, só consumindo o orçamento de tempo do usuário. O SDK já reflete isso: esses dois status nem entram na lista padrão de http_statuses que disparam retry.
Como saber se um problema é do Jev ou da minha aplicação?
A TypeSafe AI mantém uma página pública de status em status.typesafe.ai pra acompanhar incidentes do serviço. Se o status estiver normal e você continuar recebendo 401 ou 422, o problema é local, chave ou payload. Se aparecer 429 ou 529 em volume, alinhe com o status antes de sair mexendo no seu código.
O custo de retries no Jev pesa no orçamento da aplicação?
O Jev cobra US$ 0,042 por milhão de tokens de entrada e US$ 0,00 na saída, então cada retry que efetivamente chega no modelo reprocessa tokens de entrada de novo. É outro motivo pra não deixar max_retries alto por padrão em toda rota: o custo de uma chamada malsucedida se multiplica junto com o backoff.
Formações
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
Blog | Mais populares

Como montar um workflow de automação combinando decisões do Jev em código
Aprenda a montar um workflow com Jev: decisões tipadas encadeadas em código, alta confiança agindo sozinha e casos incertos escalando para revisão.

Jev decide, LLM escreve: como dividir os papéis dentro de um agente de IA
Jev é o modelo que decide, não escreve: entenda como dividir papéis entre Jev e LLM dentro de um agente de IA e quando usar cada um.

Para quem o Jev serve (e para quem não serve)?
Jev serve pra roteamento, scoring e guardrails em IA, não pra texto ou código. Veja pra quem o Jev serve e quando evitar.
