Agentes de IA y Llamadas a API de Larga Duración: Polling vs Webhooks

Los agentes interpretan 202 Accepted como completado y reportan éxito en trabajos que nunca terminaron. Aprenda el contrato asíncrono que siguen los agentes y cómo probar la ruta de tiempo de espera.

Ashley Innocent

Ashley Innocent

26 August 2026

Agentes de IA y Llamadas a API de Larga Duración: Polling vs Webhooks

Apidog para empresas

Despliegue local

SSO & RBAC

Conforme con SOC 2

Explorar Apidog Enterprise

El agente llama a tu endpoint de transcodificación de video. El endpoint devuelve 202 Accepted y un ID de trabajo. El agente, que no tiene idea de lo que 202 significa en tu sistema, informa que la transcodificación está completa y pasa al siguiente paso, que lee un archivo que aún no existe.

Las operaciones de larga duración rompen a los agentes de una manera específica. Una llamada síncrona tiene un contrato obvio: envías, esperas, obtienes una respuesta. Una asíncrona divide eso en un inicio y un final, y la brecha entre ellos es donde los agentes se confunden. Declaran el éxito temprano, consultan mil veces en un bucle cerrado, o se quedan bloqueados durante seis minutos manteniendo abierta una ronda de conversación.

Esta guía cubre cómo diseñar el contrato asíncrono para que un agente pueda seguirlo, cuándo consultar y cuándo transferir, cómo escribir las herramientas para que el modelo se comporte, y cómo probar la ruta completa incluyendo los casos lentos y fallidos. Nuestra publicación sobre recuperación de errores del agente de IA cubre el lado de los fallos de las llamadas a la API; esta cubre las que tienen éxito lentamente.

Apidog encaja en el punto donde necesitas probar que el agente maneja un trabajo que tarda cuatro minutos y luego falla, lo cual no es algo que quieras descubrir en producción.

Por qué los agentes manejan mal la asincronía

Tres hábitos causan la mayor parte del problema.

Los modelos tratan un 2xx como completado. Un 202 dice que la solicitud fue aceptada para procesamiento, y la especificación de semántica HTTP es explícita en que el procesamiento puede no haber finalizado. Los modelos entrenados en tráfico ordinario de solicitud/respuesta tienden a leer cualquier 2xx como una finalización a menos que la respuesta diga lo contrario con palabras.

Los bucles son caros. Si un agente consulta dentro de su bucle de razonamiento, cada verificación cuesta un turno del modelo más los tokens de la conversación anterior. Consultar cada dos segundos para un trabajo de cuatro minutos son 120 turnos, y la ejecución agota el contexto o el presupuesto. Nuestra publicación sobre mantener las respuestas de las herramientas fuera de la ventana de contexto explica por qué esto se acumula más rápido de lo que la gente espera.

Los agentes pierden el rastro de los trabajos. Una herramienta que inicia un trabajo y devuelve un ID de trabajo ha creado un estado que el agente debe llevar consigo. Si el ID cae en medio de una conversación larga, puede ser compactado y el agente olvida que tiene un trabajo en curso.

Diseña la respuesta para que el modelo no pueda malinterpretarla

La solución más efectiva es la redacción, no la arquitectura. Cualquiera que sea tu código de estado, haz que el cuerpo diga claramente qué sucedió y qué hacer a continuación.

{
  "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"
}

Eso suena un poco exagerado para un consumidor humano de API. Está dirigido a un modelo, y los modelos siguen instrucciones explícitas en el cuerpo de una respuesta de manera mucho más fiable de lo que infieren significado de un código de estado. Tres detalles hacen el trabajo: la palabra "no completado", la herramienta siguiente nombrada y una espera mínima.

El AIP-151 de Google sobre operaciones de larga duración describe una forma de recurso limpia para esto, con un único objeto Operation que contiene los campos done, error y response. Copiar esa estructura te da una superficie consistente en cada endpoint lento, lo cual es importante porque un agente que aprende un patrón de sondeo puede entonces manejarlos todos.

Mantén la respuesta de estado igualmente directa:

{
  "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."
}

Y al completarse, devuelve el resultado en línea cuando sea pequeño, para que el agente no necesite una tercera llamada:

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

Consulta fuera del modelo, no dentro de él

La elección de implementación más importante: pon la espera en tu envoltorio de herramienta, no en el bucle de razonamiento del 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.",
    }

Desde el lado del modelo, esta es una llamada a una herramienta que toma un tiempo y devuelve una respuesta final. Sin bucle de sondeo en contexto, sin ID de trabajo olvidados, sin 120 turnos. El retroceso mantiene el recuento de solicitudes en un nivel razonable, y el límite evita que un trabajo atascado mantenga la ejecución indefinidamente. El artículo de Amazon sobre tiempos de espera, reintentos y retroceso con jitter es la referencia que vale la pena leer antes de ajustar esos números.

Dos reglas hacen esto seguro. Siempre pon un límite a la espera, y siempre devuelve el ID del trabajo al agotar el tiempo para que el agente o un humano puedan verificarlo más tarde. Nunca devuelvas un resultado ambiguo: succeeded (éxito), failed (fallido) y timed_out (tiempo agotado) son tres resultados diferentes y el modelo debería ver tres palabras distintas.

Para trabajos que se miden en horas en lugar de minutos, el sondeo dentro del envoltorio de la herramienta deja de tener sentido. Entonces la forma correcta son dos herramientas, una para iniciar y otra para verificar, además de un registro duradero de trabajos en curso fuera de la conversación para que nada se pierda por compactación. Almacena el job_id, la tarea a la que pertenece y la hora de inicio, y haz que el agente lea esa lista al principio de cada ejecución.

Cuando los webhooks son la mejor respuesta

El sondeo es simple y funciona en todas partes. Las devoluciones de llamada (callbacks) son más eficientes y requieren más trabajo para ejecutarse. La compensación está bien cubierta en nuestra comparación webhooks vs sondeo, y la versión específica del agente es más limitada.

Usa el sondeo cuando el trabajo toma de segundos a minutos, cuando el agente está esperando el resultado para continuar, o cuando no puedes alojar un endpoint público. La mayoría de las cargas de trabajo de los agentes se encuentran aquí.

Usa webhooks cuando los trabajos toman horas, cuando el agente inicia un trabajo y avanza, o cuando muchos trabajos se ejecutan concurrentemente y sondear cada uno es un desperdicio. El costo es real: necesitas un receptor público, verificación de firma, manejo de reintentos y una forma de despertar al agente cuando llega la devolución de llamada. Nuestras guías sobre diseño de webhooks fiables y verificación de firma de webhooks cubren esa base.

Existe una opción intermedia que vale la pena conocer. La transmisión del progreso del trabajo a través de eventos enviados por el servidor (SSE) te da semántica push sin un endpoint público, ya que el cliente mantiene la conexión. Es adecuada para agentes interactivos donde un humano está observando, y nuestra guía para transmitir respuestas de API con SSE cubre la implementación.

Cualquiera que elijas, la ruta de finalización debe ser idempotente. Los webhooks reintentan, los sondeos compiten, y un agente que ve "éxito" dos veces no debería iniciar el paso siguiente dos veces. Nuestra publicación sobre idempotencia para agentes de IA cubre las claves que hacen esto seguro.

Prueba la ruta lenta, no solo la rápida

Los errores asíncronos se ocultan porque los entornos de prueba son rápidos. Un trabajo que tarda cuatro minutos en producción termina en 200 milisegundos contra un *stub* local, por lo que el agente nunca experimenta el estado que realmente encontrará.

Vale la pena construir deliberadamente cuatro escenarios.

El trabajo genuinamente lento. Simula el endpoint de estado para que devuelva processing (procesando) para las primeras varias llamadas y succeeded (éxito) después de eso. Esto demuestra que el envoltorio sondea, retrocede y finalmente devuelve. En Apidog puedes impulsar esto con un *mock* que varía según el recuento de solicitudes o por un parámetro de control, para que la misma prueba se ejecute de la misma manera cada vez.

El trabajo que falla tarde. Devuelve processing tres veces, luego failed (fallido) con un cuerpo de error. El agente debe informar el fallo en lugar de tratar una consulta completada como un trabajo completado. Este es el caso que produce una pérdida silenciosa de datos cuando es incorrecto.

El tiempo de espera agotado. Mantén el *mock* devolviendo processing (procesando) más allá del límite del envoltorio y afirma que la herramienta devuelve timed_out (tiempo agotado) con el ID del trabajo intacto, no una excepción y no un éxito falso.

La finalización duplicada. Entrega el éxito dos veces, mediante reintento de webhook o por una consulta que compite, y afirma que el paso siguiente se ejecuta solo una vez.

Guarda los cuatro como escenarios para que se ejecuten en CI. No cuesta nada volver a ejecutarlos y detectan la regresión donde alguien acorta un tiempo de espera o ignora un error. El enfoque más amplio se encuentra en nuestra guía de pruebas de contrato de API.

Tres trabajos que exponen el problema

Generación de informes. Un agente financiero solicita una exportación trimestral. Tarda 90 segundos. Con una herramienta ingenua, el agente obtiene un ID de trabajo, anuncia que el informe está listo y luego entrega un enlace de descarga roto al usuario. Con un envoltorio de bloqueo, espera 90 segundos y devuelve la URL real. Misma API, resultados opuestos, y la única diferencia es dónde ocurre la espera.

Importaciones masivas. Un agente de operaciones carga 20,000 registros. La importación se ejecuta durante ocho minutos y falla parcialmente en la fila 14,000. Este es el caso que castiga una verificación de éxito ingenua: el trabajo finalizó, por lo que un estado de done es verdadero, pero el resultado lleva una lista de filas rechazadas. Devuelve resultados parciales explícitamente, con recuentos, y haz que el agente los lea antes de continuar.

Pipelines de modelos y compilación. Un agente activa una ejecución de entrenamiento o una compilación CI que tarda 40 minutos. El sondeo dentro del envoltorio es la forma incorrecta aquí; la ejecución mantendría un turno abierto demasiado tiempo. Inicia el trabajo, registra el ID en almacenamiento duradero, termina el turno y deja que una verificación programada o una devolución de llamada active el seguimiento. Nuestra publicación sobre transferencia de control y paso de contexto entre múltiples agentes cubre el movimiento de ese estado entre ejecuciones sin perderlo.

Da forma a los resultados parciales

Los trabajos largos a menudo terminan en algún punto entre el éxito y el fracaso, y un modelo de dos estados te obliga a mentir al respecto. Haz explícito el tercer 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."
}

Dos cosas importan en esa carga útil. Los recuentos están en línea, por lo que el agente puede decidir sin otra llamada. Las filas que fallan están detrás de una URL con un límite, por lo que 140 objetos de error no llegan al contexto sin ser invitados.

Alguien tiene que ver el trabajo que se estancó

La ruta de tiempo de espera agotado termina con un ID de trabajo y un mensaje que dice que el trabajo aún se está ejecutando. Ese es el valor de retorno correcto, y solo es útil si llega a una persona.

Cuando el agente es tu propio servicio, dirígelo a la cola que tu equipo ya monitorea. Cuando el agente es un tiempo de ejecución de codificación que trabaja a través de tareas asignadas, la plataforma que lo ejecuta generalmente tiene un lugar para que esto aterrice. En Sharkly, una ejecución que termina bloqueada permanece en su Tarea con su estado de ejecución y resultado, y la Bandeja de entrada separa los elementos que necesitan una respuesta o revisión humana de las actualizaciones ordinarias. El punto no es la herramienta específica. Es que "todavía en ejecución, verificar más tarde" necesita un propietario, o se convierte en "nadie verificó".

Una breve lista de verificación

Consigue la redacción de la respuesta y el envoltorio correctos y las operaciones de larga duración dejarán de ser un caso especial para el agente. Llama a una herramienta, espera y obtiene una respuesta, que es el contrato que mejor maneja. Descarga Apidog para construir los mocks de trabajos lentos junto con las pruebas.

Preguntas frecuentes

¿Debería la API devolver 202 o 200 para un inicio asíncrono? 202 Accepted es el código honesto y señala a los clientes estándar que el procesamiento no ha terminado. No confíes solo en él para los agentes, ya que el cuerpo es lo que el modelo lee de manera más fiable. Usa ambos.

¿Cuánto tiempo debe esperar el envoltorio de la herramienta antes de darse por vencido? Establece el límite ligeramente por encima del peor caso realista del endpoint, comúnmente de dos a diez minutos. Más allá de eso, el envoltorio está bloqueando un turno de conversación durante demasiado tiempo, y una herramienta de "verificar más tarde" es una mejor forma.

¿Qué intervalo de sondeo debo usar? Comienza con la sugerencia poll_after_seconds del propio servidor si la proporciona, luego retrocede por un factor de aproximadamente 1.5 con un límite de alrededor de 60 segundos. El sondeo fijo de un segundo desperdicia solicitudes y puede activar límites de velocidad, como se cubre en nuestra guía de exceso de límite de velocidad.

¿Puede el agente hacer algo útil mientras espera? Solo si tu orquestador admite llamadas concurrentes a herramientas. Si lo hace, inicia el trabajo, realiza el trabajo independiente y luego verifica el estado. Si no lo hace, el envoltorio de bloqueo es más simple y menos propenso a errores que un programador hecho a mano.

¿Cómo evito que el agente declare el éxito prematuramente? Dilo con palabras en el cuerpo de la respuesta, expón un campo booleano done y haz que la herramienta de finalización sea el único lugar donde aparece un resultado. Si la respuesta de inicio no contiene ningún resultado, no hay nada que el modelo pueda informar como un resultado.

¿Funcionan los webhooks para agentes que se ejecutan en un portátil? No directamente, ya que no hay un endpoint público. Usa un túnel para el desarrollo, como en nuestra guía para probar APIs de localhost con servicios de webhook, o limítate al sondeo hasta que el agente se ejecute en un lugar direccionable.

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs