Health checks de agentes IA: liveness, readiness y pruebas sintéticas

Separa liveness y readiness de agentes IA, detecta resultados caducados y diagnostica fallos con un ejemplo Python ejecutable.
En esta página
  1. Idea en 30 segundos
  2. Problema principal
  3. Como funciona
  4. Liveness, readiness y pruebas sintéticas responden preguntas distintas
  5. Ejemplo ejecutable de readiness
  6. Metricas tipicas de production para health checks
  7. Como leer la health-layer
  8. Cuando usar
  9. Ejemplo de implementacion
  10. Investigation
  11. Errores tipicos
  12. Solo hay ping/check de disponibilidad del servicio
  13. No hay checks de dependencias externas
  14. No hay desglose por workflow y release
  15. No hay alertas por degradacion de health score
  16. Labels de alta cardinalidad
  17. Autoevaluacion
  18. FAQ
  19. Paginas relacionadas

Idea en 30 segundos

Health checks para agentes de IA muestran si el sistema realmente esta listo para trafico justo ahora.

Ayudan a detectar degradacion antes del incidente: tools lentas, timeouts, problemas de queue o fallos en capa LLM.

Sin health checks, muchas veces solo se ve el fallo final, no las senales tempranas previas.

Problema principal

El sistema puede estar formalmente "up", pero en la practica ya inestable.

API responde, runs inician, pero algunas etapas ya se degradan: sube timeout rate, baja success rate, crece queue time. Sin health checks, esto suele verse solo tras fallo en cascada.

Ahora veamos como leer estas senales y encontrar que componente entra primero en degradacion.

En production esto suele verse asi:

  • desde fuera todo "funciona", pero p95 latency ya sale del SLO;
  • una tool devuelve timeout con mayor frecuencia, pero la causa aun no es clara;
  • synthetic run empieza a fallar antes que el trafico principal;
  • el equipo reacciona solo tras caida parcial.

Por eso la health-layer conviene monitorearla por separado, no solo con metricas generales de run.

Como funciona

Health checks normalmente se construyen en dos niveles:

  • component checks (tool_available, llm_reachable, queue_ok, db_ok);
  • end-to-end checks (synthetic_run_ok, critical_workflow_ok).

Estas senales responden "el sistema esta sano justo ahora". Logs y tracing son necesarios para explicar por que cayo una verificacion concreta.

Un proceso accesible no garantiza que el workflow esté listo. Los health checks pueden detectar un fallo antes que los usuarios, según su frecuencia y los escenarios cubiertos. No garantizan detectarlo antes que las métricas de errores o latencia.

Liveness, readiness y pruebas sintéticas responden preguntas distintas

  • Liveness: ¿debe reiniciarse el proceso?
  • Readiness: ¿puede aceptar tareas nuevas?
  • Prueba sintética: ¿funciona un escenario de extremo a extremo?

Kubernetes documenta las probes de liveness, readiness y startup.

Un agente de soporte puede funcionar en modo limitado si falla una búsqueda opcional. Si no está disponible el almacén de políticas necesario para autorizar acciones, debe rechazar las nuevas tareas afectadas. Un fallo crítico pesa más que nueve comprobaciones opcionales correctas: el promedio health_score no debe autorizar tráfico por sí solo.

No llames a un LLM externo en cada probe de liveness. Añade costes y una caída del proveedor puede causar reinicios inútiles en todas las réplicas. Recoge los resultados de dependencias por separado con timeouts; readiness lee una instantánea reciente. Las pruebas sintéticas necesitan datos propios, un presupuesto y herramientas sin efectos secundarios (cambios de estado) en producción.

Ejemplo ejecutable de readiness

Guarda el código como readiness_demo.py y ejecuta python3 readiness_demo.py con Python 3.10+. Prueba la lógica de admisión, no probes de red. Todos los tiempos usan el mismo reloj monotónico del proceso; 30 segundos es un ejemplo que debes ajustar al intervalo de comprobación.

PYTHON
from dataclasses import dataclass


@dataclass(frozen=True)
class Check:
    ok: bool
    observed_at: float


def ready(checks, required, now, max_age=30.0):
    if not required or max_age <= 0:
        return False
    for name in required:
        check = checks.get(name)
        if check is None or not check.ok:
            return False
        if not 0 <= now - check.observed_at <= max_age:
            return False
    return True


required = ("queue", "policy_store")
checks = {
    "queue": Check(True, 100.0),
    "policy_store": Check(True, 100.0),
    "optional_search": Check(False, 100.0),
}
assert ready(checks, required, now=110.0)
assert not ready(checks, required, now=131.0)
assert not ready({}, required, now=110.0)
checks["policy_store"] = Check(False, 100.0)
assert not ready(checks, required, now=110.0)
print("readiness cases: 4 passed")

Resultado esperado: readiness cases: 4 passed. Los casos cubren una dependencia opcional, resultados caducados, resultados ausentes y una dependencia crítica fallida. El colector real debe registrar los fallos y no actualizar la hora del último éxito tras un timeout. Las tareas ya iniciadas requieren una política separada de finalización o recuperación.

Tras una señal de fallo, continúa con la depuración del run afectado.

Metricas tipicas de production para health checks

MetricaQue muestraPara que sirve
health_check_pass_rateproporcion de health checks exitososevaluacion rapida de estado del sistema
health_check_latency_p95tiempo de ejecucion de verificacionessenal temprana de degradacion de dependencias
tool_check_latency_p95latency de checks de tools concretaslocalizar tool-layer lenta
synthetic_run_success_rateexito de synthetic runcontrol de escenario real E2E
degraded_component_countcuantos componentes estan degradadosestimar alcance del problema
timeout_rateproporcion de timeout en checks y runsdisparador temprano de inestabilidad
queue_time_p95cuanto espera un run en queuesenal de falta de capacity
health_scoreindice agregado de salud (0..1)senal simple para alertas y status page

health_score normalmente se calcula a nivel dashboard/health service como agregado de varios checks, no como una metrica "magica" unica.

Para que metrica de health sea util, normalmente se segmenta por release, region, workflow y tipo de componente.

Importante: no agregues campos de alta cardinalidad (run_id, request_id, user_id) en labels, o el storage de metricas se sobrecarga rapido.

Como leer la health-layer

Que se verifica -> que se degrada -> que componente rompe el workflow. Son tres niveles que siempre hay que mirar juntos.

Es clave mirar tendencias en el tiempo y diferencias entre releases, no una sola caida puntual de un check.

Ahora revisa combinaciones de senales:

  • health_check_pass_rate down + timeout_rate up -> sistema entra en modo inestable;
  • synthetic_run_success_rate down + degraded_component_count up -> el problema ya impacta workflow critico;
  • queue_time_p95 up + run_count up -> falta capacity, hace falta escalar;
  • health_score down + error_rate up -> riesgo alto de incidente a corto plazo;
  • tool_check_latency_p95 up + synthetic_run_success_rate down -> bottleneck en tool-layer externa.

Cuando usar

Un set completo de health checks no siempre es necesario.

Para un prototipo temprano, puede bastar una verificacion basica de disponibilidad de API.

Pero health checks detalladas se vuelven criticas cuando:

  • el sistema ya esta en production;
  • hay SLO/SLA de estabilidad;
  • el agente depende de varias tools externas o queues;
  • se necesitan alertas tempranas antes de fallo masivo.

Ejemplo de implementacion

Abajo hay un ciclo simplificado de health checks estilo Prometheus. Muestra enfoque base: checks de componentes, synthetic run y health score agregado.

Es una base de instrumentación: instala prometheus-client y proporciona tus propias comprobaciones con timeouts. El promedio sirve para una vista general, no para decidir automáticamente la readiness.

PYTHON
import time
from dataclasses import dataclass
from prometheus_client import Counter, Gauge, Histogram

# perf_counter() se usa para mediciones monotonicas y precisas de latency
HEALTH_CHECK_TOTAL = Counter(
    "agent_health_check_total",
    "Total health checks by check name and status",
    ["check", "status", "release"],
)

HEALTH_CHECK_LATENCY_MS = Histogram(
    "agent_health_check_latency_ms",
    "Health check latency in milliseconds",
    ["check", "release"],
    buckets=(10, 20, 50, 100, 250, 500, 1000, 2000),
)

SYNTHETIC_RUN_TOTAL = Counter(
    "agent_synthetic_run_total",
    "Total synthetic runs by status",
    ["status", "release"],
)

DEGRADED_COMPONENT_TOTAL = Counter(
    "agent_degraded_component_total",
    "Total degraded component detections",
    ["component", "reason", "release"],
)

HEALTH_SCORE = Gauge(
    "agent_health_score",
    "Aggregated health score in range 0..1",
    ["release"],
)


@dataclass
class CheckResult:
    ok: bool
    reason: str = "ok"


def run_health_checks(checks, run_synthetic, release="2026-03-22"):
    total = len(checks)
    failed = 0

    for check_name, check_fn in checks.items():
        started_at = time.perf_counter()
        status = "error"  # default for any failure path
        try:
            result = check_fn()  # -> CheckResult
            status = "ok" if result.ok else "fail"
            if not result.ok:
                failed += 1
                DEGRADED_COMPONENT_TOTAL.labels(
                    component=check_name,
                    reason=result.reason,
                    release=release,
                ).inc()
        except Exception as error:
            failed += 1
            DEGRADED_COMPONENT_TOTAL.labels(
                component=check_name,
                reason=type(error).__name__,
                release=release,
            ).inc()
        finally:
            HEALTH_CHECK_TOTAL.labels(check=check_name, status=status, release=release).inc()
            HEALTH_CHECK_LATENCY_MS.labels(check=check_name, release=release).observe(
                (time.perf_counter() - started_at) * 1000
            )

    try:
        synthetic_ok = run_synthetic()
        SYNTHETIC_RUN_TOTAL.labels(
            status="ok" if synthetic_ok else "fail",
            release=release,
        ).inc()
        if not synthetic_ok:
            failed += 1
    except Exception:
        SYNTHETIC_RUN_TOTAL.labels(status="error", release=release).inc()
        failed += 1

    # 1.0 = todos los checks pasaron, 0.0 = todo fallo
    # +1 por synthetic run
    denominator = total + 1
    health_score = max(0.0, 1.0 - (failed / max(1, denominator)))
    HEALTH_SCORE.labels(release=release).set(health_score)
    return health_score

Las siguientes métricas son valores ilustrativos, no mediciones de un sistema de producción:

Segmentpass_ratesynthetic_successhealth_scoreEstado
core workflow98.7%99.1%0.97ok
research workflow93.2%89.4%0.82warning
tool-heavy workflow87.1%80.6%0.74critical: incident risk

Investigation

Cuando dispara una alerta de health:

  1. encontrar check o workflow con degradacion;
  2. revisar runs problematicos en tracing;
  3. revisar en logs timeout, stop_reason y tool failures;
  4. encontrar root cause (tool, LLM, queue, routing, release regression).

Errores tipicos

Incluso cuando ya hay health checks, muchas veces no ayudan por errores tipicos.

Solo hay ping/check de disponibilidad del servicio

Disponibilidad de API no garantiza que workflow agente funcione realmente. Sin synthetic run, es facil perder degradacion oculta.

No hay checks de dependencias externas

Si tools, queues o BD no entran en health checks, el sistema puede verse "sano" hasta el primer incidente. En ese escenario, cuesta localizar rapido fallo de herramienta.

No hay desglose por workflow y release

Sin eso, cuesta entender que release o escenario empeoro el estado del sistema.

No hay alertas por degradacion de health score

Sin alertas, health checks se vuelven telemetria pasiva. Asi se pierden senales tempranas de caos multi-agente.

Labels de alta cardinalidad

Agregar run_id, request_id o session_id en labels sobrecarga rapido el backend de metricas. Es mejor mantener esos datos en logs y tracing.

Autoevaluacion

Abajo tienes checklist corto de health checks base antes de release.

Progreso: 0/9

⚠ Falta observability base

Será difícil depurar el sistema en production. Empieza con run_id, structured logs y tracing de tool calls.

FAQ

P: En que se diferencian health checks de metricas normales?
R: Metricas muestran tendencia; health checks dan respuesta rapida "sistema listo o no ahora" para escenarios concretos.

P: Cual es el minimo de health checks para empezar?
R: Empieza con llm_reachable, tool_available, queue_ok y un synthetic run para workflow critico.

P: Por que synthetic run es tan importante?
R: Porque valida camino completo de ejecucion, no componente aislado. Es lo que mejor refleja estado real en production.

P: Si baja health score pero API aun responde, ya es incidente?
R: Es degradacion temprana. Hay que iniciar investigation de inmediato antes de fallo masivo.

Paginas relacionadas

Siguiente sobre el tema:

⏱️ 9 min de lectura • Actualizado 3 de octubre de 2026Dificultad: ★★★
Integrado: control en producciónOnceOnly
Make agent behavior observable with run timelines
Correlate actions and policy decisions using run events, timelines, logs, and metrics:
  • Presupuestos (pasos / topes de gasto)
  • Permisos de herramientas (allowlist / blocklist)
  • Kill switch y parada por incidente
  • Idempotencia y dedupe
  • Audit logs y trazabilidad
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.
Observability Hooks (Python SDK)
from onceonly import OnceOnly

client = OnceOnly(api_key="once_live_...")
run_id = "run_obs_042"

client.post_event(run_id=run_id, type="step_start", step="retrieve_context")
client.post_event(run_id=run_id, type="step_end", step="retrieve_context", status="ok")

timeline = client.get_run_timeline(run_id, limit=200, offset=0)
metrics = client.gov.agent_metrics("support-agent", period="day")

Autor

Nick — ingeniero que construye infraestructura para agentes de IA en producción.

Enfoque: patrones de agentes, modos de fallo, control del runtime y fiabilidad del sistema.

🔗 GitHub: https://github.com/mykolademyanov


Nota editorial

Esta documentación está asistida por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

Los ejemplos son educativos y pueden utilizar herramientas y datos simulados. Antes de usarlos en producción, verifica la fiabilidad, la seguridad y la recuperación en tu propio entorno.