Domínio 5 — Context Management & Reliability · Lição 4 de 5

Confiabilidade: erros, retries e propagação multi-agente

Objetivos de aprendizagem

Erros HTTP: o que retentar e o que corrigir

CódigoTipoRetryable?Ação correta
400invalid_request_errorNãoCorrigir o request (parâmetro inválido, mensagens fora de ordem, beta header inexistente). Retentar o mesmo payload falha para sempre.
401 / 403authentication_error / permission_errorNãoCorrigir credencial / permissões.
413request_too_largeNãoReduzir o input (truncar histórico, dividir documentos).
429rate_limit_errorSimRespeitar o header retry-after; backoff exponencial.
500api_errorSimBackoff exponencial.
529overloaded_errorSimBackoff; considerar espaçar requests ou usar outro modelo temporariamente.

O SDK já retenta por você. Os SDKs oficiais retentam automaticamente erros de conexão, 429 e ≥500 com backoff exponencial — max_retries=2 por padrão, configurável no client ou via with_options(). Timeouts (default de 10 minutos) levantam APITimeoutError e também são retentados. Só implemente retry customizado quando precisar de comportamento além do SDK (ex.: mais tentativas, logging, fila).

Idempotência: antes de retentar operações com efeito colateral (criar pedido, enviar e-mail via tool), garanta que a repetição não duplica o efeito — desenhe as ferramentas para serem idempotentes ou deduplique por chave. Retry seguro pressupõe operação segura de repetir.

Exemplo: exceções tipadas + backoff exponencial

import time
import random
import anthropic

client = anthropic.Anthropic()  # o proprio SDK ja retenta 2x (max_retries=2)

def call_with_retry(max_retries: int = 5, **kwargs):
    """Retry com backoff exponencial + jitter para alem do que o SDK cobre."""
    last_exception = None
    for attempt in range(max_retries):
        try:
            return client.messages.create(**kwargs)
        except anthropic.RateLimitError as e:  # 429
            retry_after = int(e.response.headers.get("retry-after", "0"))
            last_exception = e
            delay = max(retry_after, 2 ** attempt + random.uniform(0, 1))
        except anthropic.APIStatusError as e:
            if e.status_code >= 500:  # 500 api_error, 529 overloaded_error
                last_exception = e
                delay = 2 ** attempt + random.uniform(0, 1)
            else:
                raise  # 4xx (exceto 429): corrigir o request, NUNCA retentar
        except anthropic.APIConnectionError as e:  # falha de rede antes da resposta
            last_exception = e
            delay = 2 ** attempt + random.uniform(0, 1)
        print(f"Tentativa {attempt + 1}/{max_retries}; aguardando {delay:.1f}s")
        time.sleep(min(delay, 60.0))
    raise last_exception

response = call_with_retry(
    model="claude-opus-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Resuma o status do pipeline."}],
)
print(response.content[0].text)

Propagação de erro em sistemas multi-agente (TS 5.3)

Quando um subagente falha, o que ele reporta ao coordenador determina o que o coordenador consegue decidir. Compare:

ReporteO que o coordenador consegue fazer
Genérico: "search unavailable"Quase nada — não sabe se retenta, reformula, ou usa outra fonte.
Estruturado: tipo da falha (timeout? permissão?), query tentada, resultados parciais obtidos, alternativas possíveisDecisão inteligente de recuperação: retentar, delegar a outra fonte, prosseguir com parciais anotando lacunas.

Distinção que a prova cobra: falha de acesso ≠ resultado vazio válido. Um timeout (a fonte não foi consultada) exige decisão de retry; uma query bem-sucedida com zero matches é um resultado, não um erro. Reportá-los da mesma forma faz o coordenador tratar dados inexistentes como dados verificados ausentes — ou vice-versa.

Recuperação local antes de propagar

O subagente deve tentar recuperação local de falhas transitórias (retry do timeout, reformulação da query) e propagar apenas o que não conseguiu resolver — incluindo o que foi tentado e os resultados parciais. Assim o coordenador não é acionado para cada soluço de rede, mas recebe contexto completo quando a falha é real.

Os dois anti-padrões

flowchart TD
    A[Subagente executa query] --> B{Falhou?}
    B -- "Nao" --> C{Zero resultados?}
    C -- "Sim" --> D[Reportar resultado vazio VALIDO
query ok, sem matches] C -- "Nao" --> E[Reportar achados] B -- "Sim, transitoria" --> F[Recuperacao local: retry/reformular] F -- "Resolveu" --> C F -- "Nao resolveu" --> G[Propagar erro ESTRUTURADO:
tipo da falha + query tentada +
parciais + alternativas] G --> H{Coordenador decide} H --> I[Retentar depois / outra fonte /
prosseguir com parciais + anotar lacuna de cobertura]

Erros estruturados também nas suas ferramentas

O mesmo princípio vale para tool_results devolvidos ao modelo: use is_error: true e um corpo estruturado (categoria do erro — transient/validation/permission —, flag de retryability, descrição legível). Isso permite ao agente retentar erros transitórios e explicar erros de negócio ao usuário, em vez de alucinar em cima de uma falha opaca.

Pegadinhas da prova

Resumo em 5 linhas

  1. 429/5xx/529/conexão: retryable com backoff exponencial (respeitando retry-after no 429); 400: corrigir o request, nunca retentar.
  2. O SDK oficial já retenta 2x por padrão (max_retries configurável); timeouts levantam APITimeoutError; retry pressupõe idempotência.
  3. Erro propagado ao coordenador deve ser estruturado: tipo da falha, query tentada, resultados parciais, alternativas.
  4. Falha de acesso (timeout) ≠ resultado vazio válido (query ok, zero matches) — reporte-os de forma distinguível.
  5. Subagente recupera localmente o transitório e propaga só o irrecuperável; nunca suprima erro como sucesso nem derrube o workflow inteiro por uma falha.

Documentação oficial

Questões de fixação

1. Um pipeline recebe HTTP 400 (invalid_request_error) em todas as chamadas de um lote e o código atual retenta cada uma 5 vezes com backoff. Qual é o problema?

2. Em um sistema de pesquisa multi-agente, o subagente de busca acadêmica sofre timeout na API externa. Qual retorno ao coordenador segue as melhores práticas?

3. Por que um coordenador precisa distinguir "falha de acesso" de "resultado vazio válido" nos reports dos subagentes?

4. Uma equipe quer tratamento de rate limit "mais robusto" e propõe escrever do zero um loop de retry para TODOS os erros HTTP. O que o arquiteto deve apontar sobre o SDK oficial?

5. Uma chamada retorna HTTP 529. O que esse código significa e qual a resposta adequada?