Votre gateway figé à 3 h du matin ? Réglez le watchdog de boucle de Hermes

Lundi matin, vous ouvrez votre ordinateur portable et découvrez que les tâches cron de la nuit ne sont jamais passées — pas d’erreurs, juste aucune trace d’exécution. Vous vous connectez en SSH : le processus du gateway est bien vivant, le port est ouvert, et pourtant chaque message que vous envoyez se perd dans le silence, pendant que le journal reste figé sur sa dernière ligne. C’est bien plus frustrant qu’un crash : quand un processus meurt, systemd le redémarre en quelques secondes, mais un processus vivant mais bloqué semble parfaitement sain aux yeux de votre supervision.
Hermes Agent embarque une réponse intégrée à exactement ce scénario : le loop watchdog. Un thread OS dédié surveille la boucle d’événements du gateway et, lorsqu’il détecte que la boucle est figée, tue délibérément le processus avec un exit code de redémarrage de service, pour que votre superviseur le ressuscite. Et dans le PR #92317, fusionné le 22 août 2026, l’équipe a enfin branché les paramètres de réglage de ce mécanisme sur le vrai chargeur de configuration — auparavant, vous pouviez les écrire toute la journée sans que rien ne change.
Les crashs sont faciles à régler, les blocages non
Deux mots d’abord. Un crash signifie que le processus se termine : le port se ferme, la supervision s’en aperçoit instantanément, et systemd/launchd KeepAlive le remet aussitôt sur pied. Un blocage (wedge), c’est différent — le processus reste vivant, mais la boucle d’événements asyncio (le « cœur » d’un programme asynchrone, là où chaque tâche fait la queue pour s’exécuter) est bloquée par un appel qui ne rend jamais la main.
Voilà le piège : chaque chemin de récupération construit sur la boucle d’événements — nouvelles tentatives après timeout, réécritures d’état, journalisation d’erreurs — a besoin que la boucle tourne pour se déclencher. Plus vous avez besoin de récupération, moins vous pouvez en avoir. Le processus ne meurt pas, donc aucune alarme ne sonne, et un gateway à moitié mort reste là, à attendre qu’un humain se manifeste.
Comment le watchdog surveille la boucle d’événements
L’approche de Hermes (source : gateway/shutdown_watchdog.py) contourne entièrement la boucle : un simple thread démon au niveau OS monte la garde depuis l’extérieur, en trois étapes :
- Sonde (probe) — toutes les
loop_watchdog_probe_interval_ssecondes (30 par défaut), le watchdog injecte une sonde dans la boucle viacall_soon_threadsafe. Cet appel est thread-safe, donc il atterrit même quand la boucle est occupée. - Strike — le watchdog attend ensuite jusqu’à
loop_watchdog_probe_timeout_ssecondes (10 par défaut) que la sonde soit traitée. Boucle saine → sonde traitée instantanément, le compteur de strikes repart à zéro. Boucle figée → la sonde n’est jamais traitée, un strike. - Sortie forcée — après
loop_watchdog_max_strikeséchecs consécutifs (3 par défaut), le watchdog vide les traces de pile de tous les threads viafaulthandler(une preuve inestimable pour les analyses ultérieures), consignereason=loop_liveness_watchdogdans le registre du cycle de vie, et force la sortie avec l’exit code 75 — le code dédié au redémarrage de service, qui dit à systemd/launchd de relancer le gateway.
Avec les valeurs par défaut, environ 90 à 120 secondes de blocage soutenu de la boucle déclenchent la récupération automatique. Comparé à la découverte de la panne le lendemain matin, ce temps de réaction est réellement utile.
Les trois nouveaux réglages — enfin branchés
Avant le #92317, ce mécanisme avait un défaut gênant : l’interrupteur gateway.loop_watchdog et ses paramètres existaient depuis un moment dans les valeurs par défaut de la configuration, mais le chargeur de configuration ne lisait jamais aucun d’entre eux — quoi que vous écriviez, le watchdog tournait avec des valeurs codées en dur, et vous ne pouviez même pas le désactiver. Le PR connecte ces clés au vrai chemin de chargement (la valeur de niveau supérieur gagne, avec repli sur les clés imbriquées) et ajoute une validation bornée : NaN, Infinity et les valeurs démesurées retombent désormais proprement sur les valeurs par défaut au lieu de faire planter le chargement de la configuration.
Quatre réglages sont désormais ajustables sous la section gateway de config.yaml :
| Clé | Défaut | Signification |
|---|---|---|
gateway.loop_watchdog |
true |
Interrupteur principal ; passer à false pour tout désactiver |
gateway.loop_watchdog_probe_interval_s |
30.0 |
Secondes entre deux sondes de vivacité |
gateway.loop_watchdog_probe_timeout_s |
10.0 |
Délai avant qu’une sonde non traitée compte comme un échec |
gateway.loop_watchdog_max_strikes |
3 |
Échecs consécutifs avant la sortie forcée |
Réglage depuis la CLI
Pas besoin d’éditer le YAML à la main — hermes config s’en charge :
# Check the current value
hermes config get gateway.loop_watchdog_max_strikes
# Your machine is heavily loaded and occasionally stalls:
# give a probe more time before counting a miss
hermes config set gateway.loop_watchdog_probe_timeout_s 20
# Allow 5 consecutive misses before acting
# (~2-3 minutes of sustained block before recovery)
hermes config set gateway.loop_watchdog_max_strikes 5
# Prefer no watchdog at all?
hermes config set gateway.loop_watchdog false
# Changed your mind — back to defaults
hermes config unset gateway.loop_watchdog
Redémarrez le gateway après avoir modifié la configuration. Notez que hermes config unset supprime complètement la clé, qui retombe alors sur la valeur par défaut intégrée — plus propre que de redéfinir manuellement true.
Quand assouplir, quand tenir la ligne
Assouplissez quand : vous dialoguez avec des providers de modèles distants ou lents dont certaines requêtes se bloquent parfois pendant un moment, ou votre machine est tellement chargée que la boucle d’événements cale pendant plusieurs secondes d’affilée. Une sonde manquée de temps en temps n’est pas un interblocage, et une probe_timeout_s ou un max_strikes plus grand réduit les mises à mort injustifiées.
N’assouplissez pas pour masquer un vrai blocage. Tout l’intérêt du watchdog, c’est une récupération rapide ; passer max_strikes de 3 à 8 allonge la récupération de 2 à 3 fois, et chaque minute supplémentaire qu’un gateway bloqué reste là, c’est plus de tâches cron qui s’accumulent et de messages non livrés. L’équipe corrige aussi à la racine la classe des faux positifs (déplacement de l’écriture du heartbeat propre au watchdog hors de la boucle, avec une sonde à deux témoins — le PR #90502 est toujours en cours de revue), c’est pourquoi le réglage par défaut reste strict.
Et si « les tâches qui calent en plein milieu » est votre douleur récurrente, c’est une autre couche : les garde-fous heartbeat et objectifs au niveau de la session s’en occupent.
Un fichier heartbeat pour votre propre supervision
Si vous utilisez une supervision externe (Uptime Kuma, Prometheus, ou un simple contrôle cron d’une ligne), le gateway réécrit aussi atomiquement un fichier heartbeat à <HERMES_HOME>/state/gateway.heartbeat selon une cadence régulière. Il sert à deux choses : il permet à la supervision externe de distinguer « processus vivant » de « boucle qui fonctionne », et il fait office d’instantané de télémétrie glissant avant la mort — après une mort non propre, le dernier heartbeat est ce qui se rapproche le plus d’une photo de la scène de crime.
Vérifiez la fraîcheur avec une seule commande :
# Linux
stat -c %Y ~/.hermes/state/gateway.heartbeat
# macOS
stat -f %m ~/.hermes/state/gateway.heartbeat
Comparez cet horodatage à l’heure actuelle. S’il n’a pas bougé depuis une minute ou deux, la boucle a probablement cessé de faire du vrai travail — le watchdog est probablement sur le point d’agir, et vous venez d’avoir un préavis.
Statut de publication
Ces réglages vivent actuellement sur main (PR #92317, fusionné le 22 août 2026) et ne figurent encore dans aucun tag de release — la dernière v0.20.5 (tag 2026.8.19) embarque toujours l’ancien comportement fixe. Si vous voulez les essayer, attendez la prochaine release et lancez hermes update, ou suivez la page des releases GitHub — nos notes de version v0.20.5 résument cette release.
Le gateway est le cœur de chaque tâche cron, bot de messagerie et session distante de Hermes (nouveau venu ? voir notre guide d’automatisation cron et la référence de la commande hermes-gateway). Donnez-lui une configuration de watchdog adaptée à votre environnement, et au moins vous saurez que le système se relèvera tout seul à 3 h du matin — au lieu d’attendre que vous le découvriez à 9 h.