DEV Community

Cover image for Design de Erros de API para Agentes de IA: Erros Recuperáveis
Lucas
Lucas

Posted on Originally published at apidog.com

Design de Erros de API para Agentes de IA: Erros Recuperáveis

Como projetar respostas de erro que agentes de IA conseguem corrigir

Sua API retorna 400 Bad Request com o corpo {"error": "invalid input"}. Um desenvolvedor humano abre a documentação, verifica o payload, encontra o campo ausente e o corrige em um minuto. Um agente lê as mesmas duas palavras, não tem nada em que agir e faz a única coisa que pode: envia a mesma requisição novamente. E de novo. Então desiste e informa ao usuário que a API está quebrada.

Experimente o Apidog hoje

As respostas de erro são a parte da API da qual os agentes mais dependem, mas que as equipes costumam projetar por último. Um bom erro informa:

  • o que deu errado;
  • se tentar novamente pode ajudar;
  • o que precisa ser alterado.

Um agente pode agir sobre essas três informações. Um erro vago transforma um problema recuperável em uma tarefa falhada.

Este guia trata do lado da API. A recuperação de erros de agentes aborda o que o cliente deve fazer com retentativas, backoff e disjuntores. Aqui, o foco é o que sua API precisa retornar para que essa lógica funcione.

O Apidog é útil porque respostas de erro são a parte menos testada da maioria das APIs. Você pode defini-las na especificação, simulá-las e fazer asserções sobre elas no mesmo lugar em que testa o caminho feliz.

Respostas de erro estruturadas para agentes de IA

As três perguntas que todo erro deve responder

Toda resposta de erro recebida por um agente deve permitir que ele responda a três perguntas sem adivinhar.

A culpa é minha ou sua?

Um 4xx significa que a requisição estava errada e repeti-la sem alterações falhará novamente. Um 5xx indica um problema no servidor, e a mesma requisição pode funcionar mais tarde.

Agentes que não diferenciam esses casos podem tentar novamente para sempre em um erro de validação ou desistir diante de um problema transitório.

Devo tentar novamente? Quando?

Alguns erros 4xx permitem retentativas; outros não:

  • 429: retentável após uma espera;
  • 409: pode ser retentável depois de reler o estado do recurso;
  • 422: não é retentável sem alterar o payload.

Informe essa decisão explicitamente.

O que exatamente devo alterar?

Este é o campo que a maioria das APIs omite. "Validation failed" é inútil. Já "O campo customer.postal_code é obrigatório quando country é US" indica ao agente a correção que deve ser aplicada na próxima tentativa.

Inclua essas três informações em cada erro e a maioria das tempestades de retentativas desaparecerá.

Use um formato de erro estruturado

Não invente um formato novo. A RFC 9457, Problem Details for HTTP APIs, define um padrão amplamente suportado:

{
  "type": "https://api.example.com/errors/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The field 'customer.postal_code' is required when 'country' is 'US'.",
  "instance": "/v1/orders",
  "errors": [
    {
      "field": "customer.postal_code",
      "code": "required_conditional",
      "message": "Required when country is US. Provide a 5-digit or 9-digit US postal code.",
      "example": "94107"
    }
  ],
  "retryable": false,
  "next_action": "Add customer.postal_code to the request body and send again."
}
Enter fullscreen mode Exit fullscreen mode

Quatro partes carregam a maior parte do valor para um agente:

  • detail: uma frase completa que identifica o campo e a regra que falhou. Não apenas uma categoria.
  • errors: uma lista legível por máquina, com uma entrada por problema e um caminho de campo que o agente possa mapear de volta ao payload.
  • retryable: um booleano que informa se a requisição pode ser repetida. Não obrigue o agente a inferir isso do status HTTP.
  • next_action: uma instrução simples sobre o próximo passo. Modelos seguem instruções explícitas com mais confiabilidade do que deduzem uma ação a partir de códigos de erro.

Retorne todos os problemas de uma vez. Exibir uma falha por resposta transforma uma única correção em várias viagens de ida e volta.

O guia de design de erros de API do Google chega a uma conclusão semelhante: detalhes de erro devem pertencer a uma lista estruturada, e não ficar apenas em prosa.

Diga quando o agente deve tentar novamente

Para qualquer erro transitório, informe o tempo de espera. Um agente que sabe esperar 30 segundos espera 30 segundos. Sem essa informação, ele escolherá um intervalo — geralmente curto demais.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode
{
  "type": "https://api.example.com/errors/rate-limited",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "You have used 1000 of 1000 requests in the current minute window.",
  "retryable": true,
  "retry_after_seconds": 30,
  "next_action": "Wait 30 seconds before sending this request again. Do not retry sooner."
}
Enter fullscreen mode Exit fullscreen mode

O cabeçalho Retry-After aceita um atraso em segundos ou uma data HTTP. Segundos são mais fáceis para o cliente interpretar.

Envie o valor:

  1. no cabeçalho, para clientes HTTP padrão;
  2. no corpo, para que o modelo também possa usá-lo.

A duplicação é barata e atende aos dois consumidores.

O mesmo padrão se aplica a:

  • 503 durante manutenção;
  • 409 quando um recurso está temporariamente bloqueado;
  • qualquer erro em que esperar seja a ação correta.

Todo erro retentável deve conter um número.

Para aprofundar o tema, consulte o guia de limite de taxa excedido e o guia sobre como implementar limite de taxa de API.

Nunca vaze informações internas — e nunca retorne um erro vazio

Dois modos de falha estão em extremos opostos, e ambos prejudicam os agentes.

Não retorne stack traces

Texto de exceção interno pode expor:

  • versões de frameworks;
  • caminhos de arquivos;
  • fragmentos de consultas;
  • detalhes da infraestrutura.

Isso é um problema de segurança antes de ser um problema para o agente. As recomendações do artigo sobre testar APIs contra entrada não confiável também se aplicam aqui.

Além disso, um stack trace ocupa a janela de contexto com texto que o modelo não consegue usar.

Não retorne um erro vazio

Um 500 sem corpo ou um corpo como {"error": true} não ensina nada ao agente. As únicas opções restantes são tentar novamente ou desistir.

O caminho do meio é um erro público, estável e acompanhado de um ID de correlação:

{
  "type": "https://api.example.com/errors/internal",
  "title": "Internal error",
  "status": 500,
  "detail": "The order could not be created due to an internal error. No order was created.",
  "retryable": true,
  "retry_after_seconds": 5,
  "request_id": "req_01J8ZK3M2Q",
  "next_action": "Retry once after 5 seconds. If it fails again, stop and report request_id req_01J8ZK3M2Q."
}
Enter fullscreen mode Exit fullscreen mode

A frase "No order was created" é especialmente importante. Diante de uma escrita ambígua, o agente precisa decidir se uma nova tentativa pode criar uma duplicata. Informe explicitamente o estado da operação.

Quando não for possível garantir o estado, torne a operação idempotente e documente isso. O artigo sobre chaves de idempotência para agentes de IA aborda esse padrão em detalhes.

O request_id permite localizar a falha nos logs quando uma pessoa precisar investigar a execução. Combine-o com as práticas do guia de observabilidade de API.

Erros pertencem à especificação OpenAPI

Se o formato de erro não estiver no documento OpenAPI, ele não existe para clientes gerados, mocks e ferramentas de agente. Muitas especificações descrevem um 200 em detalhes e deixam os demais casos como exceções.

responses:
  '201':
    description: Pedido criado
    content:
      application/json:
        schema: { $ref: '#/components/schemas/Order' }

  '422':
    description: >
      Falha na validação. Não retentável sem alterar o corpo da requisição.
      O array de erros nomeia cada campo inválido.
    content:
      application/problem+json:
        schema: { $ref: '#/components/schemas/Problem' }

  '429':
    description: >
      Limite de taxa excedido. Retentável. Aguarde retry_after_seconds antes de enviar novamente.
    content:
      application/problem+json:
        schema: { $ref: '#/components/schemas/Problem' }
Enter fullscreen mode Exit fullscreen mode

Essas descrições não são decoração. Ao gerar ferramentas de agente a partir da especificação, como no guia sobre transformar uma especificação OpenAPI em ferramentas de agente, esse texto se torna o que o modelo lê sobre o caso de falha.

Uma descrição como "Retentável; aguarde antes de tentar novamente" produz um comportamento melhor do que apenas "Too Many Requests".

Teste os erros, não apenas os sucessos

A cobertura dos caminhos de erro costuma colapsar porque acioná-los exige esforço. A simulação remove esse obstáculo.

Defina cada resposta de erro no projeto da API e simule-as para que o agente possa lidar com cada caso sob demanda. No Apidog, você pode adicionar respostas de falha à definição do endpoint e alternar o mock entre elas. Assim, é possível executar o agente repetidamente contra 422, 429 e 500 sem afetar dados reais.

O artigo sobre executar agentes contra mocks em vez de produção aborda esse fluxo com mais detalhes.

Crie pelo menos estes cinco cenários:

  1. Validação com vários campos inválidos

    Retorne todos os problemas na mesma resposta e confirme que a próxima tentativa corrige todos eles.

  2. Limite de taxa com espera

    Confirme que o agente aguarda pelo menos retry_after_seconds antes de tentar novamente.

  3. Erro de servidor durante uma escrita

    Verifique que uma retentativa não cria duplicatas silenciosamente.

  4. Falha de autenticação

    Confirme que o agente para em vez de tentar novamente. Nenhuma espera corrige um token inválido. Consulte também o guia sobre chaves de API de privilégio mínimo para agentes de IA.

  5. Corpo de erro malformado

    Retorne algo que não seja JSON válido e confirme que o agente degrada graciosamente. Proxies upstream farão isso eventualmente.

Testes de respostas de erro de API com mocks

Salve esses cenários e execute-os no CI. O tratamento de erros pode regredir silenciosamente quando alguém refatora um serializador; os testes do caminho feliz não detectarão o problema.

Meça o valor de erros melhores

O impacto aparece em três áreas e é fácil de medir.

Menos retentativas desperdiçadas

Diante de {"error": "invalid input"}, um agente normalmente tenta reenviar o payload idêntico duas ou três vezes antes de desistir. Cada tentativa custa uma chamada ao modelo e uma nova mensagem com o histórico completo como contexto.

Uma resposta que nomeia o campo ausente geralmente produz uma tentativa corrigida. Em um erro de validação comum, isso pode reduzir quatro chamadas para duas.

Menos escalonamentos

Agentes que não conseguem se recuperar transferem a tarefa para uma pessoa. Cada transferência evitável é um trabalho que a automação deveria ter concluído.

Erros que nomeiam a correção mantêm a execução dentro do fluxo automatizado.

Depuração mais curta

Quando uma pessoa precisa intervir, request_id combinado com um detail preciso transforma a busca nos logs em uma única consulta.

As mesmas melhorias também ajudam desenvolvedores humanos. Uma mensagem de erro raramente é criticada por ser específica demais sobre o campo inválido.

Projete também para o escalonamento humano

Alguns erros são genuinamente irrecuperáveis pelo agente:

  • escopo de permissão ausente;
  • conta encerrada;
  • regra que exige uma decisão humana.

Nesses casos, o erro deve entregar a tarefa de forma limpa:

  1. explique o que aconteceu;
  2. informe o que uma pessoa precisa fazer;
  3. inclua o ID de correlação.

A resposta precisa chegar a um lugar onde uma pessoa possa lê-la. Se o agente for um runtime de codificação que trabalha em tarefas atribuídas, a plataforma ao redor geralmente cumpre esse papel.

O Sharkly mantém o resultado do agente e o rastreamento da execução na tarefa, além de encaminhar itens que precisam de resposta ou revisão para uma caixa de entrada. Assim, uma execução bloqueada aparece como trabalho, e não apenas como uma linha em um log.

O texto do erro é o que torna essa entrega útil. "Invalid input" não dá ao revisor mais informação do que deu ao agente.

Não force o agente a interpretar prosa

Este é um anti-padrão comum em APIs que cresceram organicamente. O código de status está correto, mas cada falha recebe uma frase diferente:

{
  "message": "Sorry, that didn't work. Please check your details and try again."
}
Enter fullscreen mode Exit fullscreen mode

O agente só consegue responder adivinhando. Pior: algumas APIs combinam esse corpo com um status 200, impedindo que a biblioteca cliente sequer identifique uma falha.

Duas regras resolvem o problema:

  1. dê a cada falha distinta um código estável e legível por máquina, como insufficient_funds;
  2. nunca retorne uma falha com um código de sucesso.

Um 200 com um erro dentro é invisível para políticas de retentativa, dashboards e alertas.

Checklist de erros legíveis por agentes

  • [ ] Toda a API usa um formato de erro estruturado e consistente.
  • [ ] detail nomeia o campo ou a condição específica, nunca apenas uma categoria.
  • [ ] Erros de validação retornam todos os problemas de uma vez, com caminhos de campo.
  • [ ] Todo erro contém um booleano retryable.
  • [ ] Erros retentáveis informam o tempo de espera em segundos, no cabeçalho e no corpo.
  • [ ] Falhas durante escritas informam se algo foi criado ou alterado.
  • [ ] Todo erro contém um ID de correlação que pode ser localizado nos logs.
  • [ ] Não há stack traces, strings de framework ou SQL nas respostas.
  • [ ] As respostas de erro estão documentadas na especificação com descrições legíveis por agentes.
  • [ ] Existem mocks para cada erro, executados por testes salvos no CI.

Erros são uma interface. Projete-os para o chamador que você realmente tem: cada vez mais, um modelo que fará exatamente o que o corpo da resposta disser.

Baixe o Apidog para definir formatos de erro e simulá-los antes que um agente os encontre em produção.

Perguntas frequentes

Devo usar a RFC 9457 ou meu próprio formato?

Use a RFC 9457, a menos que você já tenha um formato consistente em produção. Consistência é mais importante do que padronização: mudar apenas metade dos endpoints para um formato novo é pior do que manter um formato único em toda a API.

Se você já tem um formato, adicione extensões como retryable e next_action.

É seguro colocar texto em next_action?

Sim, quando o serviço gera o texto a partir de um conjunto fixo de templates. Nunca repita conteúdo fornecido pelo usuário nesse campo: o agente pode interpretá-lo como instrução, criando um caminho para injeção de prompt.

O artigo sobre testar APIs contra entrada não confiável aborda esse risco.

Erros de validação devem usar 400 ou 422?

Use:

  • 400 quando a requisição estiver malformada, como JSON inválido;
  • 422 quando a requisição for analisada, mas violar regras de negócio.

Os agentes se beneficiam da distinção porque as correções são diferentes. Se sua API já usa um único código para ambos os casos, documente o comportamento antes de considerar uma alteração.

Quanto detalhe é demais?

Pare quando o chamador tiver informações suficientes para agir. Nome do campo, regra e um exemplo geralmente bastam.

Identificadores internos, texto de consulta e stack frames ultrapassam esse limite.

As mensagens de erro contam para a janela de contexto?

Sim. Um erro verboso repetido em várias retentativas se acumula rapidamente. Mantenha as respostas abaixo de algumas centenas de tokens.

As recomendações para reduzir respostas de API para agentes também se aplicam às falhas.

Como impeço um agente de tentar novamente um erro não retentável?

Defina:

{
  "retryable": false,
  "next_action": "Do not retry until the request body is corrected."
}
Enter fullscreen mode Exit fullscreen mode

Além disso, imponha a regra no wrapper da ferramenta. O julgamento do modelo não deve ser a única salvaguarda. Usar duas camadas de proteção é o comportamento correto.

Top comments (0)