Consumindo Modelos de IA

Tratamento de Erros

Timeout, rate limits, falhas de rede, retry com backoff e respostas seguras ao usuário.

Intermediário 30 min 25 pontos Leitura 0%

Nesta aula você vai

  • Identificar erros comuns das APIs de LLM
  • Implementar retry idempotente com exponential backoff
  • Retornar mensagens amigáveis sem vazar detalhes internos

Tratamento de Erros

Objetivos

  • Criar integrações robustas — demo quebra, produção aguenta
  • Saber quando repetir chamada e quando desistir

Demo vs produção

No seu notebook, a API quase sempre responde. Em produção, na terça às 15h com pico de tráfego, aparecem 429, timeout, JSON inválido e instabilidade do provedor.

A diferença entre um hack de fim de semana e um produto confiável está aqui: o que o usuário vê quando algo dá errado — e o que você loga para investigar depois.

Pense nesta aula como seguro de carro. Esperamos não usar — mas quando precisar, salva o projeto.

Erros frequentes

HTTP Código típico Causa Ação
401 invalid_api_key Key errada/revogada Alerta ops, não retry
429 rate_limit_exceeded Muitas req/min Retry com backoff
500 server_error Instabilidade provedor Retry limitado
503 overloaded Pico de demanda Retry + fallback message
Timeout Rede lenta / resposta longa Retry ou reduzir max_tokens

Context length exceeded (400): prompt + histórico > limite — não adianta retry; resuma ou truncue histórico.

Diálogo de plantão:

Monitor: "Taxa de 503 subiu para 12%."
Dev: "É o provedor ou nosso timeout curto?"
Dev: "Logs mostram 45s de latência — aumentamos timeout e backoff."
Suporte: "Usuário vê mensagem amigável?"
Dev: "Sim — 'tente em instantes', com requestId."

Retry com exponential backoff

Regra de ouro: retry só em 429, 500, 503 e timeouts — nunca em 400/401.

Por quê? Repetir uma requisição com key inválida ou prompt gigante só gasta dinheiro e tempo sem resolver nada.

async function chatWithRetry(messages, maxAttempts = 3) {
  let delay = 1000;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await chat(messages);
    } catch (err) {
      const retryable = err.status === 429 || err.status >= 500;
      if (!retryable || attempt === maxAttempts) throw err;
      await sleep(delay);
      delay *= 2; // 1s → 2s → 4s
    }
  }
}

Use jitter (delay + random(0, 500)) se muitas instâncias retry juntas — evita que todos batam na API no mesmo segundo.

Analogia: backoff é como esperar filas espaçadas no banco, não empurrar a porta a cada meio segundo.

Circuit breaker (conceito)

Se 5 falhas seguidas em 1 minuto → pare de chamar LLM por 30s, retorne:

"Assistente temporariamente indisponível. Tente em instantes."

Evita cascata de custo e timeout no seu servidor. Sem circuit breaker, seu backend pode ficar preso esperando uma API que já está sobrecarregada.

Resposta ao usuário vs log interno

// ❌ Não exponha ao frontend
res.status(500).json({ error: err.stack, openai: err.raw });

// ✅ Mensagem genérica + correlation id
const requestId = crypto.randomUUID();
logger.error({ requestId, err });
res.status(503).json({
  error: 'Não foi possível processar sua mensagem agora.',
  requestId, // suporte pode rastrear
});

O usuário precisa de clareza e calma. O time de engenharia precisa de stack trace, status HTTP e requestId. Nunca misture os dois no JSON do frontend.

Timeout em camadas

  1. HTTP client: 45–60s para LLM
  2. Seu endpoint: 55s (menor que load balancer)
  3. Frontend: loading state + cancel após 60s

Cada camada um pouco menor que a anterior — assim o erro chega organizado, não como conexão pendurada.

Idempotência

Retry pode gerar duas respostas cobradas para a mesma pergunta. Para operações críticas:

  • Gere idempotencyKey por mensagem do usuário
  • Armazene hash(prompt) → resposta em cache curto (5 min)

Cenário:

Usuário clica "Enviar" duas vezes por impaciência. Sem idempotência, você paga dois completions. Com cache de 5 min, a segunda requisição devolve a mesma resposta sem nova chamada.

Checklist produção

  • Try/catch em toda chamada externa
  • Log estruturado: latência, tokens, status, model
  • Retry máximo 3× com backoff
  • Mensagem amigável ao usuário
  • Alertas se taxa de 429 > 5% por hora

Resumo

  • Nem todo erro merece retry — 401 e context overflow são fix no código/dados
  • Backoff exponencial para 429/5xx
  • Usuário vê mensagem simples; você loga detalhe
  • Timeout explícito evita thread/worker preso

Com a API consumida e erros tratados, você tem a base técnica. Na próxima matéria entramos no que separa resposta genérica de resposta útil: engenharia de prompt — o contrato entre seu sistema e o modelo.