Agentes de IA e Chamadas de API de Longa Duração: Polling vs Webhooks

Agentes interpretam 202 Accepted como concluído e reportam sucesso em tarefas que nunca terminaram. Aprenda o contrato assíncrono que os agentes seguem e como testar o caminho de timeout.

Ashley Innocent

Ashley Innocent

26 agosto 2026

Agentes de IA e Chamadas de API de Longa Duração: Polling vs Webhooks

Apidog para empresas

Implantação local

SSO & RBAC

Conforme SOC 2

Explorar Apidog Enterprise

O agente chama o endpoint de transcodificação de vídeo. O endpoint retorna 202 Accepted e um ID de trabalho. O agente, que não tem ideia do que 202 significa em seu sistema, relata que a transcodificação está completa e passa para a próxima etapa, que lê um arquivo que ainda não existe.

Operações de longa duração quebram agentes de uma forma específica. Uma chamada síncrona tem um contrato óbvio: você envia, espera, recebe uma resposta. Uma assíncrona divide isso em um início e um fim, e a lacuna entre eles é onde os agentes se confundem. Eles declaram sucesso precocemente, eles pesquisam mil vezes em um loop apertado, ou ficam bloqueados por seis minutos mantendo uma conversa aberta.

Este guia aborda como projetar o contrato assíncrono para que um agente possa segui-lo, quando pesquisar (poll) e quando entregar, como escrever as ferramentas para que o modelo se comporte e como testar todo o caminho, incluindo os casos lentos e falhos. Nossa publicação sobre recuperação de erros de agente de IA cobre o lado da falha das chamadas de API; esta cobre as que são bem-sucedidas lentamente.

Apidog se encaixa no ponto em que você precisa provar que o agente lida com um trabalho que leva quatro minutos e depois falha, o que não é algo que você queira descobrir em produção.

Por que os agentes lidam mal com operações assíncronas

Três hábitos causam a maior parte do problema.

Modelos tratam um 2xx como concluído. Um 202 diz que a requisição foi aceita para processamento, e a especificação de semântica HTTP é explícita ao dizer que o processamento pode não ter sido concluído. Modelos treinados em tráfego de requisição/resposta comum tendem a ler qualquer 2xx como conclusão, a menos que a resposta diga o contrário em palavras.

Loops são caros. Se um agente pesquisa dentro de seu loop de raciocínio, cada verificação custa um turno de modelo mais os tokens da conversa anterior. Pesquisar a cada dois segundos por um trabalho de quatro minutos são 120 turnos, e a execução esgota o contexto ou o orçamento. Nossa publicação sobre manter as respostas da ferramenta fora da janela de contexto explica por que isso se acumula mais rápido do que as pessoas esperam.

Agentes perdem o controle dos trabalhos. Uma ferramenta que inicia um trabalho e retorna um ID de trabalho criou um estado que o agente deve levar adiante. Se o ID cair no meio de uma longa conversa, ele pode ser compactado, e o agente esquece que tem um trabalho em andamento.

Projete a resposta para que o modelo não possa interpretá-la mal

A correção mais eficaz é a redação, não a arquitetura. Seja qual for o seu código de status, faça o corpo dizer claramente o que aconteceu e o que fazer a seguir.

{
  "status": "processing",
  "job_id": "job_7f21c",
  "message": "The transcode has STARTED and is NOT complete. Do not report success. Check status with getJobStatus(job_id) after at least 30 seconds.",
  "poll_after_seconds": 30,
  "estimated_duration_seconds": 240,
  "status_url": "/v1/jobs/job_7f21c"
}

Isso parece pesado para um consumidor humano de API. É direcionado a um modelo, e os modelos seguem instruções explícitas no corpo de uma resposta de forma muito mais confiável do que inferem significado de um código de status. Três detalhes fazem o trabalho: a palavra "não completo", a ferramenta seguinte nomeada e um tempo mínimo de espera.

O AIP-151 do Google sobre operações de longa duração descreve uma forma de recurso limpa para isso, com um único objeto Operation contendo os campos done, error e response. Copiar essa estrutura oferece uma superfície consistente em todos os endpoints lentos, o que é importante porque um agente que aprende um padrão de pesquisa pode então lidar com todos eles.

Mantenha a resposta de status igualmente direta:

{
  "job_id": "job_7f21c",
  "status": "processing",
  "done": false,
  "progress_percent": 45,
  "elapsed_seconds": 108,
  "poll_after_seconds": 45,
  "message": "Still processing. Do not proceed to the next step."
}

E na conclusão, retorne o resultado em linha quando for pequeno, para que o agente não precise de uma terceira chamada:

{
  "job_id": "job_7f21c",
  "status": "succeeded",
  "done": true,
  "result": { "output_url": "https://cdn.example.com/out/7f21c.mp4", "duration_seconds": 372 }
}

Pesquise fora do modelo, não dentro dele

A escolha de implementação mais importante: coloque a espera no seu wrapper de ferramenta, não no loop de raciocínio do agente.

import time

def start_and_await_transcode(client, source_url, max_wait=600):
    job = client.post("/v1/transcode", json={"source_url": source_url}).json()
    job_id = job["job_id"]
    delay = job.get("poll_after_seconds", 5)
    waited = 0

    while waited < max_wait:
        time.sleep(delay)
        waited += delay
        status = client.get(f"/v1/jobs/{job_id}").json()

        if status.get("done"):
            if status["status"] == "succeeded":
                return {"status": "succeeded", "result": status["result"]}
            return {"status": "failed", "error": status.get("error")}

        delay = min(int(delay * 1.5), 60)

    return {
        "status": "timed_out",
        "job_id": job_id,
        "message": f"Still running after {max_wait}s. Job {job_id} continues in the background.",
    }

Do lado do modelo, esta é uma única chamada de ferramenta que leva um tempo e retorna uma resposta final. Sem loop de pesquisa no contexto, sem IDs de trabalho esquecidos, sem 120 turnos. O backoff mantém a contagem de requisições razoável, e o limite impede que um trabalho travado bloqueie a execução para sempre. O artigo da Amazon sobre timeouts, retries e backoff com jitter é a referência que vale a pena ler antes de ajustar esses números.

Duas regras tornam isso seguro. Sempre limite o tempo de espera e sempre retorne o ID do trabalho em caso de timeout para que o agente ou um humano possa verificar mais tarde. Nunca retorne um resultado ambíguo: succeeded, failed e timed_out são três resultados diferentes e o modelo deve ver três palavras diferentes.

Para trabalhos medidos em horas, e não em minutos, o polling dentro do wrapper deixa de fazer sentido. A forma correta então são duas ferramentas, uma para iniciar e outra para verificar, além de um registro durável de trabalhos em andamento fora da conversa para que nada seja perdido por compactação. Armazene o job_id, a tarefa a que pertence e a hora de início, e faça o agente ler essa lista no início de cada execução.

Quando webhooks são a melhor resposta

O polling é simples e funciona em qualquer lugar. Os callbacks são mais eficientes e dão mais trabalho para serem executados. A troca é bem abordada em nossa comparação de webhooks vs polling, e a versão específica do agente é mais restrita.

Use o polling quando o trabalho leva de segundos a minutos, quando o agente está aguardando o resultado para continuar, ou quando você não pode hospedar um endpoint público. A maioria das cargas de trabalho de agentes se encaixa aqui.

Use webhooks quando os trabalhos levam horas, quando o agente dispara o trabalho e segue em frente, ou quando muitos trabalhos são executados concorrentemente e pesquisar cada um é um desperdício. O custo é real: você precisa de um receptor público, verificação de assinatura, tratamento de retentativas e uma maneira de "acordar" o agente quando o callback chega. Nossos guias sobre como projetar webhooks confiáveis e verificação de assinatura de webhook cobrem essa base.

Uma opção intermediária vale a pena conhecer. Transmitir o progresso do trabalho por meio de eventos enviados pelo servidor (server-sent events) oferece semântica de push sem um endpoint público, já que o cliente mantém a conexão. É adequado para agentes interativos onde um humano está observando, e nosso guia para transmitir respostas de API com SSE cobre a implementação.

Qualquer que seja a sua escolha, o caminho de conclusão deve ser idempotente. Webhooks tentam novamente, polls competem, e um agente que vê "sucedido" duas vezes não deve iniciar a etapa subsequente duas vezes. Nossa postagem sobre idempotência para agentes de IA aborda as chaves que tornam isso seguro.

Teste o caminho lento, não apenas o rápido

Bugs assíncronos se escondem porque os ambientes de teste são rápidos. Um trabalho que leva quatro minutos em produção termina em 200 milissegundos contra um stub local, então o agente nunca experimenta o estado que realmente encontrará.

Quatro cenários valem a pena serem construídos deliberadamente.

O trabalho genuinamente lento. Simule o endpoint de status para que ele retorne processing nas primeiras chamadas e succeeded depois disso. Isso prova que o wrapper pesquisa, faz backoff e eventualmente retorna. No Apidog, você pode fazer isso com um mock que varia por contagem de requisições ou por um parâmetro de controle, para que o mesmo teste seja executado da mesma forma sempre.

O trabalho que falha tardiamente. Retorne processing três vezes, depois failed com um corpo de erro. O agente deve relatar a falha em vez de tratar uma pesquisa concluída como um trabalho concluído. Este é o caso que produz perda silenciosa de dados quando está errado.

O tempo limite. Mantenha o mock retornando processing além do limite do wrapper e afirme que a ferramenta retorna timed_out com o ID do trabalho intacto, não uma exceção e não um sucesso falso.

A conclusão duplicada. Entregue o sucesso duas vezes, por retentativa de webhook ou por uma pesquisa concorrente, e afirme que a etapa seguinte é executada uma única vez.

Salve todos os quatro como cenários para que sejam executados na CI. Eles não custam nada para serem reexecutados e pegam a regressão onde alguém encurta um timeout ou engole um erro. A abordagem mais ampla está em nosso guia de teste de contrato de API.

Três trabalhos que expõem o problema

Geração de relatórios. Um agente financeiro solicita uma exportação trimestral. Leva 90 segundos. Com uma ferramenta ingênua, o agente obtém um ID de trabalho, anuncia que o relatório está pronto e, em seguida, entrega um link de download quebrado ao usuário. Com um wrapper de bloqueio, ele espera 90 segundos e retorna a URL real. Mesma API, resultados opostos, e a única diferença é onde a espera acontece.

Importações em massa. Um agente de operações carrega 20.000 registros. A importação dura oito minutos e falha parcialmente na linha 14.000. Este é o caso que pune uma verificação ingênua de sucesso: o trabalho terminou, então um status de done é verdadeiro, mas o resultado contém uma lista de linhas rejeitadas. Retorne resultados parciais explicitamente, com contagens, e faça o agente lê-los antes de prosseguir.

Modelos e pipelines de build. Um agente aciona uma execução de treinamento ou um build de CI que leva 40 minutos. O polling dentro do wrapper é a forma errada aqui; a execução manteria um turno aberto por muito tempo. Inicie o trabalho, registre o ID em armazenamento durável, finalize o turno e deixe que uma verificação agendada ou um callback "acorde" o acompanhamento. Nossa postagem sobre transferência de agente e passagem de contexto abrange a movimentação desse estado entre execuções sem perdê-lo.

Dê forma aos resultados parciais

Trabalhos longos geralmente terminam em algum lugar entre o sucesso e o fracasso, e um modelo de dois estados o força a mentir sobre isso. Torne o terceiro estado explícito:

{
  "job_id": "job_a11f",
  "status": "completed_with_errors",
  "done": true,
  "summary": { "processed": 20000, "succeeded": 19860, "failed": 140 },
  "errors_url": "/v1/jobs/job_a11f/errors?limit=50",
  "message": "Import finished. 140 rows failed and were not written. Review errors before reporting success."
}

Duas coisas importam nessa carga útil. As contagens estão em linha, para que o agente possa decidir sem outra chamada. As linhas que falharam estão atrás de uma URL com um limite, para que 140 objetos de erro não apareçam no contexto sem serem convidados.

Alguém tem que ver o trabalho que parou

O caminho de timeout termina com um ID de trabalho e uma mensagem dizendo que o trabalho ainda está em execução. Este é o valor de retorno correto, e só é útil se chegar a uma pessoa.

Quando o agente é seu próprio serviço, direcione-o para qualquer fila que sua equipe já monitora. Quando o agente é um ambiente de execução de código trabalhando em tarefas atribuídas, a plataforma que o executa geralmente tem um lugar para isso. No Sharkly, uma execução que termina bloqueada permanece em sua Tarefa com seu estado de execução e resultado, e a Caixa de Entrada separa os itens que precisam de uma resposta ou revisão humana de atualizações comuns. O ponto não é a ferramenta específica. É que "ainda em execução, verifique mais tarde" precisa de um proprietário, ou se torna "ninguém verificou".

Uma breve lista de verificação

Acerte a redação da resposta e o wrapper, e operações de longa duração deixam de ser um caso especial para o agente. Ele chama uma ferramenta, espera e obtém uma resposta, que é o contrato que ele melhor gerencia. Baixe o Apidog para construir os mocks de trabalhos lentos junto com os testes.

Perguntas frequentes

A API deve retornar 202 ou 200 para um início assíncrono? 202 Accepted é o código honesto e sinaliza aos clientes padrão que o processamento não foi concluído. Não confie apenas nele para agentes, pois o corpo é o que o modelo lê de forma mais confiável. Use ambos.

Quanto tempo o wrapper da ferramenta deve esperar antes de desistir? Defina o limite um pouco acima do pior caso realista do endpoint, comumente de dois a dez minutos. Além disso, o wrapper está bloqueando um turno de conversa por muito tempo, e uma ferramenta de "verificar mais tarde" é uma forma melhor.

Qual intervalo de polling devo usar? Comece pela dica poll_after_seconds do próprio servidor, se houver, e depois faça um backoff de um fator de cerca de 1.5 com um limite em torno de 60 segundos. O polling fixo de um segundo desperdiça requisições e pode atingir limites de taxa, conforme abordado em nosso guia de limite de taxa excedido.

O agente pode fazer algo útil enquanto espera? Apenas se o seu orquestrador suportar chamadas de ferramentas concorrentes. Onde isso acontece, inicie o trabalho, faça o trabalho independente e depois verifique o status. Onde não acontece, o wrapper de bloqueio é mais simples e menos propenso a erros do que um agendador feito à mão.

Como impeço o agente de declarar sucesso precocemente? Diga em palavras no corpo da resposta, exponha um campo booleano done e faça da ferramenta de conclusão o único lugar onde um resultado aparece. Se a resposta de início não contém nenhum resultado, não há nada para o modelo relatar como um resultado.

Webhooks funcionam para agentes rodando em um laptop? Não diretamente, pois não há um endpoint público. Use um túnel para desenvolvimento, como em nosso guia para testar APIs localhost com serviços de webhook, ou mantenha o polling até que o agente esteja rodando em algum lugar acessível.

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs