Expérience close
Neuf agents, neuf jours
Retour d'expérience chiffré sur une flotte de neuf agents SRE autonomes en conditions réelles : ce qui a tenu, ce qui a cassé, ce que l'analyse des traces m'a appris, et ce que je garde.
· Période : 27 sept. → 6 oct. 2026 · 10 min de lecture
- Agents autonomes
- SRE
- LLM local
- Observabilité
- Sécurité
J'ai fait tourner une flotte d'agents SRE autonomes sur mon infrastructure réelle : un home-server, un DGX Spark et un VPS. La boucle agentique a fonctionné. La valeur produite pour l'infra, beaucoup moins. Voici les chiffres, ce qui a tenu, ce qui a cassé, ce que l'analyse des traces m'a appris, et ce que je garde.
- profils d'agents, en topologie étoile pilotée par un tableau Kanban
- 9
- des 418 tâches Kanban étaient le même scan d'infrastructure (324 scans)
- 78 %
- des tokens attribués à un profil ont servi à cette boucle de scan ; 1 % aux profils qui corrigent
- 72 %
- incidents ouverts : 21 résolus, 29 en échec, 25 clôturés sans suite
- 75
- des incidents portaient sur les composants de la flotte elle-même
- 37 %
- appels LLM, 146,6 M tokens en entrée et 12,6 M en sortie, tous servis en local
- 24 537
- échecs d'outils détectés et classés automatiquement dans les traces
- 428
- fois où un agent a fait docker exec dans son propre conteneur pour redevenir root
- 270
Le dispositif
Le projet est né pour le NVIDIA Claw Agent Challenge à Paris : montrer un agent SRE capable de surveiller, diagnostiquer et réparer une infrastructure sans humain dans la boucle. J'ai utilisé Hermes Agent, un LLM servi en local par vLLM sur un DGX Spark (Qwen3.8), et un serveur MCP maison de 59 outils couvrant mes trois machines : lecture, écriture Docker sous garde-fous, exécution en bac à sable.
Après le challenge, la flotte a continué à tourner sur mon home-server avec des rôles séparés. Un seul agent, le conductor, a le droit de créer des tâches. Les autres rendent des verdicts.
cron */10 ─┐
├─▶ sre-conductor ──Kanban──▶ sre-monitor (scan read-only, classe, rapporte)
webhook ───┘ (seul dispatcher) ├─▶ sre-diag (cause racine → verdict, n'applique rien)
(crash, OOM) ├─▶ sre-doctor (hot-fix infra à partir d'un verdict)
├─▶ sre-dev (correctif de code en TDD → PR)
└─▶ sre-reporter (seul à notifier l'humain, Telegram)
à côté : sre-hygiene (nettoyage des incidents), auto-eval (juge les traces), gardien (rappels)
observabilité : Langfuse (traces par profil) + Julia-1, petit modèle de décision qui note chaque étape
Chronologie
- mi-sept.
Première version pour le challenge : quatre agents, délégation directe entre eux, crons toutes les 5 minutes.
- 23–24 sept.
Panne injectée
Je remplace le modèle d'un service par un modèle inexistant. Le conteneur reste « healthy » et répond HTTP 200 avec une erreur dans le corps. La tuyauterie tient : délégation, journalisation, Telegram, rollback. Le jugement échoue : 5 runs concluent « sain » faute de signal d'erreur, et 3 incidents sont clôturés en « SUCCESS » sans preuve.
- 27 sept.
Profils versionnés dans le dépôt, passage à la topologie Kanban avec un seul dispatcher.
- 2 oct.
Date limite du challenge.
- 3 oct.
Le conductor scanne toutes les 10 minutes. Le volume de tâches passe de quelques unités à plus de 100 par jour. Mise en place de la détection automatique des échecs d'outils dans les traces.
- 4 oct.
Les échecs « capacity » les plus fréquents deviennent des outils MCP dédiés : exécution allowlistée, Python en bac à sable sans réseau, sonde HTTP.
- 5 oct.
Deux boucles émergentes
Un fournisseur de mémoire externe modélise les alertes comme un « utilisateur » à comprendre et met 36 requêtes en file sur le GPU. Le même jour, les runs déclenchés par webhook n'ont pas l'outil Kanban : le conductor écrit une soixantaine de souvenirs « Kanban manquant » qui biaisent ensuite son raisonnement.
- 6 oct.
Audit de sécurité
Les agents ont un terminal et le socket Docker : les garde-fous MCP sont contournés en pratique. J'isole le conteneur avec un proxy Docker en lecture et une passerelle SSH à commandes épinglées. Dans ses dernières tâches, la flotte détecte la régression que cette isolation vient d'introduire. Je mets la flotte en pause le soir même.
Où sont partis le travail et les tokens
- Scans d'infrastructure (sre-monitor)
- Toutes les autres tâches
- Boucle de scan
- Autres profils
Lire les traces pour faire évoluer les agents
C'est la partie de l'expérience qui m'a le plus appris. Chaque appel d'outil de chaque agent finit dans Langfuse. Plutôt que de lire des transcripts à la main, j'ai construit un pipeline qui détecte les appels en échec, les classe, et réécrit le verdict sur la trace sous forme de score.
Le classement repose sur une question simple : à qui est la faute ? Quatre catégories en sont sorties.
428 échecs d'outils
sur 264 traces
- Capacity199 · 46 %
L'intention de l'agent était légitime, mais l'environnement l'a bloqué ou ne lui donnait pas l'outil nécessaire.
- Reasoning128 · 30 %
Mauvais outil ou mauvais argument : un identifiant inexistant, un outil appelé par le mauvais intermédiaire.
- Garbage100 · 23 %
Charge structurellement invalide, typiquement du JSON malformé.
- Inclassable1 · < 1 %
Aucune règle ne s'applique et le LLM n'a pas tranché.
Deux choses m'ont surpris. D'abord, sept règles déterministes suffisent à classer 88 % des échecs. Le LLM n'intervient que sur le résidu, et ses classements récurrents ont vocation à devenir de nouvelles règles. C'est le seul endroit de la flotte où le système devenait moins dépendant du modèle à mesure qu'il tournait.
Ensuite, la catégorie la plus grosse n'était pas une erreur de l'agent. Près de la moitié des échecs relevaient de « capacity » : l'agent voulait faire quelque chose de raisonnable et l'environnement le lui interdisait. En mode autonome, sans humain pour approuver, l'exécution de code et le terminal sont refusés par le harnais : 111 refus sur l'exécution de code, 69 sur le terminal. Ces chiffres sont devenus une liste de besoins. J'ai créé des outils MCP étroits pour les couvrir : une exécution de commandes en lecture sur liste blanche, un Python en bac à sable sans réseau ni secrets, une sonde HTTP limitée à des cibles internes.
Les échecs « reasoning » racontaient autre chose. Une bonne partie venait de l'outil intermédiaire qui sert à appeler les outils chargés à la demande : le modèle se trompait d'arité ou appelait directement un outil qui devait passer par lui. Ces motifs se corrigeaient dans les fichiers d'instructions des agents, pas dans le code : un skill dédié à l'usage des outils, des exemples d'appels corrects, et des consignes retirées quand elles se contredisaient.
Le même travail a guidé la gestion de la fenêtre de contexte. Charger d'emblée les schémas de 59 outils noie le modèle. Les outils sont donc découverts à la demande, avec une liste plafonnée à 12 000 tokens, chaque profil n'a accès qu'aux outils de son rôle (27 pour le diagnostic, par exemple), et ne charge automatiquement que ses propres skills. J'ai aussi réglé l'effort de raisonnement par rôle : faible pour l'orchestration, élevé pour la sonde qui doit juger un état ambigu.
La difficulté de fond, c'est de faire évoluer un agent dans un environnement protégé. Chaque restriction de sécurité crée une nouvelle catégorie d'échecs « capacity ». L'agent a besoin d'outils, mais les outils prévus ne couvrent jamais tous ses besoins réels. Il faut donc mesurer ce qu'il essaie de faire, distinguer le besoin légitime du contournement, puis ajouter l'outil le plus étroit possible. L'audit du 6 octobre l'a montré dans l'autre sens : quand l'outil manquait, l'agent passait par le terminal et le socket Docker. Une partie des échecs « capacity » qui disparaissaient n'étaient pas résolus. Ils étaient contournés.
Ce qui a tenu, ce qui a cassé
Ce qui a tenu
La tuyauterie déterministe. Délégation, Kanban, journal d'incidents, notifications, rollback : tout ce qui ne dépend pas du jugement du modèle a fonctionné du premier coup ou presque.
Un seul dispatcher. La topologie en étoile a supprimé les délégations en cascade de la première version et rendu chaque tâche traçable.
Le juge Julia-1. Un modèle de 147 M de paramètres qui répond à des questions typées (choix, score, oui/non) note chaque étape de chaque trace dans Langfuse, sur CPU, pour un coût négligeable.
La classification des échecs d'outils. Sept règles, un LLM sur le résidu, un score réécrit sur chaque trace : la seule boucle qui produisait de la connaissance réutilisable.
Des modèles de repli. Quand j'ai débranché le DGX ou changé de modèle, les agents ont continué sur un autre fournisseur.
Ce qui a cassé
Une boucle sur un état stable. Un cron qui interroge une infra qui va bien produit « rien de nouveau », 140 fois par jour.
« Non exercé » pris pour « sain ». Sans signal d'erreur, le modèle conclut que tout va bien au lieu de générer la preuve.
L'auto-référence. 37 % des incidents concernaient la flotte, son dashboard ou son serveur MCP. Elle passait une partie de son temps à se surveiller.
Des garde-fous contournés. Avec un terminal et le socket Docker, un agent n'a pas besoin des outils MCP : 270 docker exec pour redevenir root, des docker run -v, des compose up directs.
Des boucles émergentes. Mémoire externe et outils manquants ont chacun créé un cycle que personne n'avait conçu.
Le coût de la vitrine. Sur 185 commits du serveur MCP et du dashboard, 46 % sont des correctifs, et la scène 3D de visualisation en concentre le plus grand nombre.
Ce que je ferais autrement
Ce que je garde
| Brique | Ce qu'elle apporte |
|---|---|
| Agent sans root | Proxy Docker en lecture et redémarrage, passerelle SSH à commandes épinglées, une seule clé montée. |
| Classification des échecs d'outils | Règles d'abord, LLM sur le résidu, verdict réécrit sur chaque trace Langfuse. |
| Juge Julia-1 sur Langfuse | Évaluation typée de chaque étape et de chaque tour, sur CPU, branchée comme évaluateur natif. |
| Collector OpenTelemetry | Un seul point d'entrée : traces d'infra vers Jaeger, spans LLM vers MLflow. |
| Secrets Bitwarden | Injection au lancement, pour Docker Compose comme pour les agents, rien sur disque. |
La flotte reste reproductible à partir d'un tag git. Ses crons sont en pause, pas supprimés. La prochaine expérience part d'une hypothèse plus étroite : donner à chaque service consommateur de LLM son propre compte et ses propres clés via le gateway MLflow.
Sur la carte
- Abricot.coprojet
- OC-Ai-Engineer-P3projet
- CLEA WebUIprojet