O agente chama o endpoint de transcodificação de vídeo. O endpoint retorna 202 Accepted e um ID de trabalho. Como o agente não sabe que 202 significa em seu sistema, ele informa que a transcodificação terminou e passa à próxima etapa, que tenta ler 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 simples: enviar, esperar e receber. Uma chamada assíncrona divide esse fluxo em início e fim — e a lacuna entre os dois é onde os agentes se confundem.
Eles podem:
- Declarar sucesso cedo demais.
- Pesquisar o status em um loop apertado.
- Ficar bloqueados por vários minutos mantendo uma conversa aberta.
- Perder o ID do trabalho durante uma conversa longa.
Este guia mostra como projetar contratos assíncronos que os agentes consigam seguir, quando usar polling ou webhooks, como implementar as ferramentas e como testar cenários lentos e falhos. Para o tratamento de falhas de chamadas de API, consulte Recuperação de erros de agente de IA.
O Apidog é útil para provar que o agente consegue lidar com um trabalho que leva quatro minutos e depois falha — antes que esse problema apareça em produção.
Por que os agentes lidam mal com operações assíncronas
Modelos tratam qualquer 2xx como conclusão
Um 202 informa que a requisição foi aceita para processamento, não que ela terminou. A especificação de semântica HTTP é explícita sobre isso.
Ainda assim, modelos treinados principalmente em tráfego de requisição e resposta tendem a interpretar qualquer 2xx como sucesso final, a menos que o corpo explique claramente o contrário.
Loops de polling são caros
Se o agente pesquisa o status durante o próprio loop de raciocínio, cada verificação custa um turno de modelo e os tokens do histórico.
Pesquisar a cada dois segundos durante quatro minutos gera 120 turnos. A execução pode esgotar o contexto ou o orçamento. Veja como manter as respostas da ferramenta fora da janela de contexto para entender por que esse custo cresce rapidamente.
IDs de trabalho podem ser perdidos
Uma ferramenta que inicia um trabalho e retorna um ID cria um estado que o agente precisa carregar adiante. Em uma conversa longa, esse ID pode ser compactado junto com o histórico, fazendo o agente esquecer que existe um trabalho em andamento.
Projete respostas que o modelo não possa interpretar mal
A correção mais eficaz costuma ser a redação da resposta, não uma mudança de arquitetura. Independentemente do código HTTP usado, o corpo deve dizer claramente o que aconteceu e qual é o próximo passo:
{
"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"
}
Esse formato pode parecer detalhado demais para uma pessoa, mas é direcionado a um modelo. Três elementos são especialmente importantes:
- Dizer explicitamente que o trabalho não terminou.
- Nomear a próxima ferramenta.
- Informar um tempo mínimo antes da próxima consulta.
O AIP-151 do Google sobre operações de longa duração define uma estrutura consistente para isso, usando um único objeto Operation com os campos done, error e response. Adotar esse padrão em todos os endpoints lentos facilita o aprendizado do agente.
A resposta de status também deve ser 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."
}
Quando o resultado for pequeno, retorne-o na própria resposta final. Assim, o agente não precisa fazer uma terceira chamada:
{
"job_id": "job_7f21c",
"status": "succeeded",
"done": true,
"result": { "output_url": "https://cdn.example.com/out/7f21c.mp4", "duration_seconds": 372 }
}
Faça o polling fora do modelo
A decisão de implementação mais importante é colocar a espera no wrapper da 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 ponto de vista do modelo, essa implementação é uma única chamada de ferramenta que demora e retorna uma resposta final. Ela elimina:
- Loops de polling no contexto.
- IDs de trabalho esquecidos.
- Centenas de turnos adicionais.
- Requisições desnecessárias.
O backoff mantém a quantidade de requisições sob controle, e o limite de tempo impede que um trabalho travado bloqueie a execução indefinidamente. Consulte o guia da Amazon sobre timeouts, retries e backoff com jitter antes de ajustar esses valores.
Duas regras são essenciais:
- Sempre defina um limite máximo de espera.
- Sempre retorne o ID do trabalho em caso de timeout.
Nunca retorne um resultado ambíguo. succeeded, failed e timed_out são estados diferentes, e o modelo precisa enxergar três valores distintos.
Para trabalhos que duram horas, o polling dentro do wrapper deixa de ser adequado. Nesse caso, use duas ferramentas:
- Uma para iniciar o trabalho.
- Outra para consultar o status.
Mantenha também um registro durável fora da conversa com:
-
job_id. - A tarefa relacionada.
- O horário de início.
- O estado atual.
O agente pode ler essa lista no começo de cada execução, mesmo depois de uma compactação de contexto.
Quando usar webhooks
Polling é simples e funciona em praticamente qualquer ambiente. Callbacks são mais eficientes, mas exigem mais infraestrutura. A comparação entre as duas abordagens está em webhooks vs. polling.
Use polling quando:
- O trabalho leva segundos ou alguns minutos.
- O agente precisa do resultado para continuar.
- Você não pode hospedar um endpoint público.
Use webhooks quando:
- O trabalho leva horas.
- O agente inicia o trabalho e continua outras atividades.
- Muitos trabalhos são executados ao mesmo tempo e consultar cada um seria ineficiente.
Webhooks exigem um receptor público, verificação de assinatura, tratamento de retentativas e uma forma de acordar o agente quando o callback chegar. Consulte:
Uma opção intermediária é usar eventos enviados pelo servidor, ou server-sent events (SSE). Eles oferecem semântica de push sem exigir um endpoint público, pois o cliente mantém a conexão aberta. Essa abordagem funciona bem quando há uma pessoa acompanhando um agente interativo. Veja como transmitir respostas de API com SSE.
Independentemente da escolha, o caminho de conclusão deve ser idempotente. Webhooks podem ser reenviados, polls podem ocorrer em paralelo e o agente pode receber o mesmo sucesso duas vezes. A etapa seguinte nunca deve ser executada duas vezes. Consulte idempotência para agentes de IA.
Teste o caminho lento
Bugs assíncronos ficam escondidos quando os testes são rápidos. Um trabalho que leva quatro minutos em produção pode terminar em 200 milissegundos contra um stub local. Nesse caso, o agente nunca experimenta o estado real.
Crie deliberadamente estes quatro cenários:
1. Trabalho genuinamente lento
Faça o endpoint retornar processing nas primeiras chamadas e succeeded depois disso. Verifique se o wrapper:
- Continua pesquisando.
- Aplica backoff.
- Retorna o resultado final.
No Apidog, use um mock que varie por contagem de requisições ou por um parâmetro de controle. Assim, o teste é determinístico.
2. Falha tardia
Retorne processing três vezes e depois failed com um corpo de erro. O agente deve relatar a falha, não tratar uma consulta concluída como um trabalho bem-sucedido.
3. Timeout
Mantenha o mock retornando processing além do limite do wrapper. Afirme que a ferramenta retorna timed_out com o ID do trabalho intacto — não uma exceção nem um sucesso falso.
4. Conclusão duplicada
Entregue o sucesso duas vezes, simulando uma retentativa de webhook ou um polling concorrente. Verifique se a etapa seguinte é executada apenas uma vez.
Salve os quatro cenários e execute-os na CI. Eles são baratos de repetir e detectam regressões em timeouts e tratamento de erros. Para uma abordagem mais ampla, consulte teste de contrato de API.
Três trabalhos que revelam o problema
Geração de relatórios
Uma exportação trimestral pode levar 90 segundos. Com uma ferramenta ingênua, o agente recebe um ID, anuncia que o relatório está pronto e entrega um link quebrado.
Com um wrapper bloqueante, ele espera e retorna a URL real. A API é a mesma; o ponto da espera é que muda o resultado.
Importações em massa
Uma importação de 20.000 registros pode durar oito minutos e falhar parcialmente na linha 14.000. Um status done: true não significa que todos os registros foram aceitos.
Retorne resultados parciais explicitamente, com contagens, e faça o agente analisá-los antes de prosseguir.
Modelos e pipelines de build
Uma execução de treinamento ou build de CI pode levar 40 minutos. Nesse caso, polling dentro do wrapper mantém um turno aberto por tempo demais.
Inicie o trabalho, registre o ID em armazenamento durável, finalize o turno e use uma verificação agendada ou um callback para retomar o acompanhamento. O guia sobre transferência de agente e passagem de contexto aborda como transportar esse estado entre execuções.
Modele resultados parciais
Trabalhos longos frequentemente terminam entre sucesso e fracasso. Um modelo com apenas dois estados força o agente a omitir informações importantes.
Expresse um terceiro estado:
{
"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."
}
As contagens ficam disponíveis imediatamente para que o agente possa decidir sem outra chamada. Já as linhas com erro ficam atrás de uma URL paginada, evitando inserir 140 objetos no contexto sem necessidade.
Dê um responsável ao trabalho interrompido
Um timeout deve retornar o ID do trabalho e informar que ele continua em execução. Esse retorno só é útil se chegar a alguém que possa verificá-lo.
Se o agente for um serviço próprio, encaminhe o item para a fila monitorada pela equipe. Se ele rodar em um ambiente de execução de código baseado em tarefas, use o mecanismo de acompanhamento da própria plataforma.
No Sharkly, por exemplo, uma execução bloqueada permanece na tarefa com seu estado e resultado, enquanto a Caixa de Entrada separa itens que exigem resposta ou revisão humana de atualizações comuns.
A ferramenta específica não é o ponto principal. “Ainda está em execução; verifique mais tarde” precisa ter um responsável. Caso contrário, transforma-se em “ninguém verificou”.
Checklist
- Todo endpoint lento retorna um ID de trabalho, uma URL de status e uma mensagem simples informando que o trabalho não terminou.
- As respostas de status contêm um booleano
done. - O polling fica no wrapper da ferramenta, com backoff e limite máximo.
- Timeouts retornam o ID do trabalho.
- Sucesso, falha e timeout são valores distintos.
- Trabalhos com duração de vários minutos são registrados fora da conversa.
- O tratamento da conclusão é idempotente, tanto para polling quanto para callbacks.
- Os cenários lento, falha tardia, timeout e duplicação são executados na CI.
Com uma resposta bem redigida e um wrapper adequado, operações longas deixam de ser um caso especial para o agente. Ele chama uma ferramenta, espera e recebe uma resposta inequívoca — exatamente o contrato que consegue gerenciar melhor.
Use o Apidog para criar mocks de trabalhos lentos e executar esses testes de forma determinística.
Perguntas frequentes
A API deve retornar 202 ou 200 ao iniciar uma operação assíncrona?
202 Accepted é o código mais honesto: ele informa aos clientes padrão que o processamento ainda não terminou. Para agentes, não dependa apenas do código. Use também um corpo explícito, pois é isso que o modelo interpreta com mais confiabilidade.
Quanto tempo o wrapper deve esperar?
Defina o limite um pouco acima do pior caso realista do endpoint — normalmente entre dois e dez minutos. Além disso, o wrapper bloqueia um turno por tempo demais; uma ferramenta de verificação posterior costuma ser melhor.
Qual intervalo de polling devo usar?
Comece com poll_after_seconds, se o servidor fornecer esse valor. Depois, aplique backoff de aproximadamente 1.5, com limite próximo de 60 segundos.
Polling fixo a cada segundo desperdiça requisições e pode atingir limites de taxa. Consulte o guia sobre erros de limite de taxa.
O agente pode fazer algo útil enquanto espera?
Somente se o orquestrador suportar chamadas de ferramentas concorrentes. Nesse caso, inicie o trabalho, execute a atividade independente e consulte o status depois.
Quando a concorrência não está disponível, um wrapper bloqueante é mais simples e menos propenso a erros do que um agendador implementado manualmente.
Como impedir que o agente declare sucesso cedo demais?
Diga isso claramente no corpo da resposta, exponha um campo booleano done e faça com que apenas a ferramenta de conclusão retorne um resultado.
Se a resposta inicial não contém um resultado, o modelo não tem um resultado válido para relatar.
Webhooks funcionam em agentes executados em um laptop?
Não diretamente, porque um laptop normalmente não possui um endpoint público. Para desenvolvimento, use um túnel, como explicado no guia sobre testar APIs localhost com serviços de webhook, ou mantenha o polling até o agente estar em um ambiente acessível.


Top comments (0)