Health checks des agents IA : liveness, readiness et tests synthétiques

Séparer liveness et readiness des agents IA, détecter les résultats périmés et diagnostiquer les pannes avec un exemple Python exécutable.
Sur cette page
  1. Idee en 30 secondes
  2. Probleme principal
  3. Comment ca fonctionne
  4. Liveness, readiness et tests synthétiques répondent à des questions différentes
  5. Exemple de readiness exécutable
  6. Metriques production typiques pour health checks
  7. Comment lire la health-layer
  8. Quand l'utiliser
  9. Exemple d'implementation
  10. Investigation
  11. Erreurs typiques
  12. Il y a seulement ping/check de disponibilite
  13. Pas de checks des dependances externes
  14. Pas de segmentation par workflow et release
  15. Pas d'alertes sur degradation du health score
  16. Labels a forte cardinalite
  17. Auto-verification
  18. FAQ
  19. Pages liees

Idee en 30 secondes

Les health checks pour agents IA montrent si le systeme est reellement pret pour le trafic maintenant.

Ils aident a detecter la degradation avant incident : tools lentes, timeouts, problemes de queue ou pannes dans la couche LLM.

Sans health checks, on voit souvent seulement la panne, pas les signaux precoces qui la precedent.

Probleme principal

Le systeme peut etre formellement "up", mais en pratique deja instable.

L'API repond, les runs demarrent, mais certaines etapes degradent deja : timeout rate augmente, success rate baisse, queue time monte. Sans health checks, cela devient visible seulement apres echec en cascade.

Ensuite, voyons comment lire ces signaux et trouver quel composant entre en degradation en premier.

En production, cela ressemble souvent a :

  • de l'exterieur tout semble "fonctionner", mais p95 latency sort deja du SLO ;
  • un tool renvoie timeout plus souvent, mais la cause reste floue ;
  • synthetic run commence a echouer avant le trafic principal ;
  • l'equipe reagit seulement apres panne partielle.

C'est pourquoi la health-layer doit etre surveillee separement, et pas seulement via les metriques globales de run.

Comment ca fonctionne

Les health checks sont generalement construits sur deux niveaux :

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

Ces signaux repondent a "le systeme est-il sain maintenant". Logs et tracing servent a expliquer pourquoi un check concret a echoue.

Un processus accessible ne garantit pas un workflow prêt. Les health checks peuvent détecter une panne avant les utilisateurs, selon leur fréquence et les scénarios couverts. Ils ne garantissent pas une détection antérieure aux métriques d’erreur ou de latence.

Liveness, readiness et tests synthétiques répondent à des questions différentes

  • Liveness : faut-il redémarrer le processus ?
  • Readiness : peut-il accepter de nouvelles tâches ?
  • Test synthétique : un scénario de bout en bout fonctionne-t-il ?

Kubernetes documente les probes de liveness, readiness et startup.

Un agent de support peut fonctionner en mode limité si une recherche facultative est indisponible. Si le stockage des politiques nécessaire pour autoriser les actions est indisponible, il doit refuser les nouvelles tâches concernées. Un échec critique compte plus que neuf succès facultatifs : un health_score moyen ne doit pas, à lui seul, autoriser le trafic.

N’appelle pas un LLM externe à chaque probe de liveness. Cela coûte de l’argent et une panne du fournisseur peut provoquer des redémarrages inutiles de toutes les répliques. Collecte séparément les résultats des dépendances avec des timeouts ; la readiness lit un état récent. Donne aux tests synthétiques des données dédiées, un budget et des outils sans effets secondaires (changements d’état) en production.

Exemple de readiness exécutable

Enregistre ce code dans readiness_demo.py et lance python3 readiness_demo.py avec Python 3.10+. Il teste la logique d’admission, pas des probes réseau. Tous les temps utilisent la même horloge monotone du processus ; 30 secondes est un exemple à adapter à l’intervalle des contrôles.

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")

Résultat attendu : readiness cases: 4 passed. Les cas couvrent une dépendance facultative, des résultats périmés, des résultats absents et une dépendance critique en échec. Le collecteur réel doit enregistrer les échecs et ne pas actualiser l’heure du dernier succès après un timeout. Les tâches déjà en cours nécessitent une politique distincte de terminaison ou de reprise.

Après un signal d’échec, passe au débogage du run concerné.

Metriques production typiques pour health checks

MetriqueCe qu'elle montrePourquoi elle est utile
health_check_pass_ratepart des health checks reussisevaluation rapide de l'etat systeme
health_check_latency_p95temps d'execution des checkssignal precoce de degradation des dependances
tool_check_latency_p95latency des checks de tools concreteslocalisation d'une couche tools lente
synthetic_run_success_ratetaux de succes des synthetic runscontrole du vrai scenario E2E
degraded_component_countnombre de composants en degradationestimation de l'ampleur du probleme
timeout_ratepart de timeout dans checks et runsdeclencheur precoce d'instabilite
queue_time_p95combien de temps un run attend en queuesignal de manque de capacity
health_scoreindex agrege de sante (0..1)signal simple pour alertes et status page

health_score est generalement calcule au niveau dashboard/health-service comme agregat de plusieurs checks, pas comme metrique "magique" unique.

Pour que les metriques de health soient utiles, on segmente souvent par release, region, workflow et type de composant.

Important : ne pas ajouter de champs a forte cardinalite (run_id, request_id, user_id) dans les labels, sinon le stockage metriques se surcharge vite.

Comment lire la health-layer

Ce qui est verifie -> ce qui degrade -> quel composant casse le workflow. Ces trois niveaux doivent toujours etre lus ensemble.

Il faut regarder les tendances dans le temps et les differences entre releases, pas une chute isolee d'un seul check.

Puis regarder les combinaisons de signaux :

  • health_check_pass_rate down + timeout_rate up -> le systeme entre en mode instable ;
  • synthetic_run_success_rate down + degraded_component_count up -> le probleme impacte deja le workflow critique ;
  • queue_time_p95 up + run_count up -> manque de capacity, scaling necessaire ;
  • health_score down + error_rate up -> risque eleve d'incident a court terme ;
  • tool_check_latency_p95 up + synthetic_run_success_rate down -> bottleneck dans la couche tools externe.

Quand l'utiliser

Un ensemble complet de health checks n'est pas toujours necessaire.

Pour un prototype initial, un check basique de disponibilite API peut suffire.

Mais des health checks detaillees deviennent critiques quand :

  • le systeme est deja en production ;
  • il y a des SLO/SLA de stabilite ;
  • l'agent depend de plusieurs tools externes ou de queues ;
  • il faut des alertes precoces avant qu'un echec massif commence.

Exemple d'implementation

Ci-dessous un cycle simplifie de health checks en style Prometheus. L'exemple montre l'approche de base : checks de composants, synthetic run et health score agrege.

Ce code est une base d’instrumentation : installe prometheus-client et fournis tes propres contrôles avec timeouts. Le score agrégé sert à la vue d’ensemble, pas à décider automatiquement de la readiness.

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

# perf_counter() est utilise pour des mesures monotones et precises 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 = tous les checks passent, 0.0 = tout echoue
    # +1 pour 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

Les métriques suivantes sont illustratives, pas des mesures d’un système de production :

Segmentpass_ratesynthetic_successhealth_scoreStatut
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

Quand une alerte health se declenche :

  1. trouver le check ou workflow en degradation ;
  2. regarder les runs problematiques dans le tracing ;
  3. verifier timeout, stop_reason et tool failures dans les logs ;
  4. trouver la root cause (tool, LLM, queue, routing, release regression).

Erreurs typiques

Meme quand les health checks sont ajoutees, elles apportent souvent peu de valeur a cause des erreurs ci-dessous.

Il y a seulement ping/check de disponibilite

Disponibilite API ne garantit pas que le workflow agentique fonctionne reellement. Sans synthetic run, on manque facilement la degradation cachee.

Pas de checks des dependances externes

Si tools, queues ou base ne sont pas incluses dans les health checks, le systeme peut sembler "sain" jusqu'au premier incident. Dans ce cas, localiser rapidement echec d'outil devient difficile.

Pas de segmentation par workflow et release

Sans cela, il est difficile de comprendre quelle release ou quel scenario a degrade l'etat systeme.

Pas d'alertes sur degradation du health score

Sans alertes, les health checks deviennent de la telemetrie passive. Cela fait manquer les signaux precoces de chaos multi-agent.

Labels a forte cardinalite

Ajouter run_id, request_id ou session_id dans les labels surcharge vite le backend metriques. Il vaut mieux garder ces donnees dans logs et tracing.

Auto-verification

Ci-dessous une checklist courte des health checks de base avant release.

Progression: 0/9

⚠ L'observability de base manque

Le système sera difficile à déboguer en production. Commencez par run_id, structured logs et tracing des tool calls.

FAQ

Q : En quoi les health checks different des metriques classiques ?
R : Les metriques montrent des tendances, alors que les health checks donnent une reponse rapide "systeme pret ou non maintenant" pour des scenarios concrets.

Q : Quel minimum de health checks faut-il au demarrage ?
R : Commencer avec llm_reachable, tool_available, queue_ok et un synthetic run pour workflow critique.

Q : Pourquoi synthetic run est si important ?
R : Il valide le chemin d'execution complet, pas un composant isole. C'est le meilleur reflet de l'etat reel en production.

Q : Si health score baisse mais API repond encore, est-ce deja un incident ?
R : C'est une degradation precoce. Il faut lancer l'investigation tout de suite avant un echec massif.

Pages liees

Suite du sujet :

⏱️ 10 min de lecture • Mis à jour 3 octobre 2026Difficulté: ★★★
Intégré : contrôle en productionOnceOnly
Make agent behavior observable with run timelines
Correlate actions and policy decisions using run events, timelines, logs, and metrics:
  • Budgets (steps / plafonds de coût)
  • Permissions outils (allowlist / blocklist)
  • Kill switch & arrêt incident
  • Idempotence & déduplication
  • Audit logs & traçabilité
Mention intégrée : OnceOnly est une couche de contrôle pour des systèmes d’agents en prod.
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")

Auteur

Nick — ingénieur qui construit une infrastructure pour des agents IA en production.

Focus : patterns d’agents, modes de défaillance, contrôle du runtime et fiabilité des systèmes.

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


Note éditoriale

Cette documentation est assistée par l’IA, avec une responsabilité éditoriale humaine pour l’exactitude, la clarté et la pertinence en production.

Les exemples sont pédagogiques et peuvent utiliser des outils et des données simulés. Avant une utilisation en production, vérifiez la fiabilité, la sécurité et la reprise dans votre propre environnement.