Qu'a fait votre agent cette nuit ? Brancher Hermes sur OpenTelemetry


Vous vous réveillez, vous ouvrez le portable, et la tâche « juste un petit ajustement de config » laissée tourner toute la nuit a coûté quelques dollars de plus que d’habitude. /status vous donne le total de tokens et rien d’autre. Ce que vous voulez vraiment savoir, c’est quel appel de modèle a traîné, quel tool a réessayé trois fois, et quelle approbation est restée deux minutes à attendre un clic. Hermes ne fournit pas cette chronologie appel par appel, mais le plugin communautaire hermes-otel comble exactement ce manque : il branche Hermes sur OpenTelemetry et transforme un tour en une trace que vous dépliez couche par couche.

Les deux surfaces d’observabilité intégrées ne couvrent pas « cet appel précis »

Hermes en embarque déjà deux. La première est le monitoring du gateway (monitoring.export.otlp), qui surveille la santé du gateway et du cron et reste volontairement vide de contenu : aucun prompt, aucun appel de tool, aucun token. La seconde est le plugin Langfuse fourni d’origine, qui enregistre des traces par exécution mais ne les envoie qu’à Langfuse.

hermes-otel est la troisième surface, au niveau du tour, et elle cohabite avec les deux autres. Elle s’accroche aux hooks de cycle de vie de l’hôte et installe son propre TracerProvider, sans jamais toucher aux objets provider privés que construit l’exportateur du cœur. Les deux jeux de données ne s’écrasent pas.

Installation : une commande, un redémarrage du gateway

Le plugin figure dans le catalogue curaté (niveau communautaire, épinglé à 1.18.2) :

hermes plugins install hermes_otel
hermes plugins enable hermes_otel

À partir de Hermes 0.21, l’installation place les trois dépendances opentelemetry-* du plugin dans son propre virtualenv et les réapplique après chaque hermes update. Redémarrez ensuite tout gateway en cours d’exécution.

Version antérieure à 0.21, ou installation avec --no-deps ? Ajoutez les dépendances vous-même :

<hermes venv>/bin/pip install -r ~/.hermes/plugins/hermes_otel/requirements.txt

À quoi ressemble un tour quand on le déplie

Le plugin projette les événements de l’hôte sur des spans, à peu près ainsi :

agent / cron                       <- le span racine du tour complet
├── skill.{name}                   <- un skill chargé, ouvert jusqu'à la fin du tour
└── llm.{model}                    <- un run_conversation
    ├── api.{model}                <- un aller-retour HTTP, avec tokens et finish reason
    │   ├── tool.{name}            <- chaque appel de tool : args, résultat, issue
    │   └── approval.{pattern}     <- attente d'un humain ou du smart guardian
    └── subagent.{role}            <- delegate_task, la trace de l'enfant s'imbrique ici

Les spans api.* portent un attribut hermes.query_source qui indique si l’appel vient de la boucle de l’agent elle-même (main), d’un enfant délégué (subagent), ou a été fait pour le compte de l’agent (auxiliary — titrage, compression, agrégation Mixture-of-Agents), avec hermes.auxiliary.task qui nomme la tâche. Les spans de tool portent hermes.tool.outcome : completed, error, timeout, blocked ou cancelled. Les attributs sont émis à la fois selon les conventions OpenInference et OTel GenAI, donc Phoenix comme Langfuse fonctionnent sans mapping maison.

Configuration : un fichier YAML, ou une variable d’environnement

Pour un seul backend, aucun fichier de config n’est nécessaire :

export OTEL_PHOENIX_ENDPOINT=http://localhost:6006/v1/traces

Pour en déclarer plusieurs, créez ~/.hermes/hermes_otel.yaml :

project_name: hermes-agent
backends:
  - type: phoenix
    endpoint: http://localhost:6006/v1/traces      # traces uniquement
  - type: lgtm
    endpoint: http://localhost:4318/v1/traces      # traces + métriques + logs
    metrics: true
  - type: honeycomb
    api_key: ${HONEYCOMB_API_KEY}                  # ${VAR} est étendu au chargement
capture_logs: true

Les backends sont alimentés en parallèle, chacun avec sa propre file d’export. Le répertoire docker-compose/ du dépôt contient une stack prête à l’emploi pour chaque backend auto-hébergeable : lancer Phoenix ou Jaeger en local est le chemin le plus court vers une première trace.

Confidentialité : contenu complet par défaut, passez donc en preview

Lisez cette partie en premier. La valeur par défaut content_capture: full envoie le prompt et la réponse complets de chaque appel de modèle, ainsi que les arguments et la sortie des tools tronqués à 1200 caractères, au backend que vous avez configuré.

Valeur Ce qui quitte le processus
full Prompt et réponse complets, contenu des tools tronqué à 1200 caractères
preview Uniquement des aperçus tronqués
off Aucun contenu, seulement la structure

Sans backend configuré, le plugin conserve un stockage SQLite local sous $HERMES_HOME pour son tableau de bord et n’envoie rien. Avec plusieurs profils, rappelez-vous que chaque profil a sa propre installation, ses réglages et sa télémétrie, et qu’un gateway multiplexé trace chaque profil qu’il sert sous son propre nom.

Interroger depuis le chat, sans toucher au backend

Le plugin embarque un skill, hermes_otel:observability, doté d’un outil de requête en terminal. Une fois installé, vous pouvez simplement demander « pourquoi mon dernier tour a-t-il été lent ? » ou « combien a coûté cette session ? » : il lit le stockage local en direct, sans backend :

python3 ~/.hermes/plugins/hermes_otel/skills/observability/scripts/otel.py trace last

La sortie est une cascade lisible avec la durée par span, les tokens, les cache hits et le finish reason. Les sous-commandes incluent status, traces, span, sessions, stats, metrics, logs et un sql en lecture seule, toutes avec --json, --since et --source.

Quand mieux vaut s’en passer

  • Si vous voulez seulement savoir si le gateway est vivant, le monitoring intégré est plus léger et volontairement vide de contenu.
  • Si Langfuse est votre unique backend, le plugin Langfuse fourni d’origine fait déjà le travail.
  • Sur une petite machine qui enchaîne les micro-tâches, les spans par tour ont un coût. Commencez par preview plus de l’échantillonnage pendant une semaine avant de figer le réglage.

Deux limites, en toute honnêteté : relier les spans du serveur MCP à la trace de l’agent est implémenté côté plugin mais attend encore le support de l’hôte, et le plugin échoue en mode ouvert — ses exportateurs tournent sur leurs propres threads, ce n’est donc pas un journal d’audit. Si vous avez besoin de preuves qui survivent à un crash, gardez le stockage local ou exportez vers un backend que vous contrôlez.

Si vous hésitez encore sur les plugins, commencez par le fonctionnement du catalogue de plugins — hermes plugins search vous indique sur quel commit un plugin est épinglé. Pour parcourir ces spans dans un espace de travail navigateur, Hermes Workspace s’y marie très bien, et le détail des commandes se trouve dans la référence hermes plugins.