aller au contenu
FRFlo
Table des matières

Un agent IA ne se résume pas à un modèle qui reçoit un prompt et renvoie du texte. Dès qu’il doit rester disponible, répondre depuis une messagerie, accéder à des outils et conserver un environnement d’exécution, il devient un système distribué.

Dans mon infrastructure, Hermes Agent joue le rôle de gateway et d’orchestrateur. Il reçoit les messages Discord, construit les conversations et appelle un modèle compatible OpenAI. Les capacités externes ne sont pas ajoutées directement à Hermes : elles passent par Executor, une couche d’intégration auto-hébergée qui expose un endpoint MCP.

Cette séparation est le point important de l’architecture. Hermes raisonne et dialogue ; Executor centralise l’accès aux outils ; les serveurs MCP fournissent des capacités spécialisées. Le tout tourne dans une stack Docker, avec des services isolés des accès extérieurs directs.

Les trois couches du système

flowchart LR
    User["Utilisateur Discord"] -->|message| Hermes["Hermes Agent\ngateway run"]
    Hermes -->|appel du modèle| Models["Modèle IA"]
    Hermes -->|MCP HTTP + OAuth| Executor["Executor\nMCP toolkit Hermes"]
    Executor --> GTFS["GTFS-MCP"]
    Executor --> Moodle["Moodle-MCP"]
    Executor --> Storage["Executor /data\nvolume persistant"]

Hermes : la couche agent

Hermes est déployé avec l’image nousresearch/hermes-agent:latest. Le conteneur démarre avec gateway run, et non comme une simple session interactive. Cette différence est fondamentale : le processus reste actif, écoute les événements Discord et maintient les sessions de conversation.

Sa configuration est montée depuis config/hermes/config.yaml dans /opt/data/config.yaml. Elle décrit notamment :

  • le fournisseur de modèle ;
  • le répertoire de travail /workspace ;
  • l’interface Discord et ses règles d’autorisation ;
  • le routage des messages système vers un canal/thread dédié (system_channel) ;
  • l’endpoint du navigateur persistant Google Chrome (browser.cdp_url: "http://chrome:9222") ;
  • le serveur MCP distant auquel Hermes doit se connecter.

Le gateway Discord est volontairement restreint. La configuration autorise un utilisateur et un canal précis, désactive les mentions générales et n’exige pas de mentionner le bot dans ce canal. L’intégration s’appuie sur le plugin natif hermes-discord-py (plugins.enabled: [discord-frflo]), qui assure une synchronisation temps réel robuste :

  • Alignement d’identité de plateforme (Platform("discord-frflo")) : au lieu d’une identité générique Platform.DISCORD, l’adaptateur s’initialise et s’enregistre sous le nom exact de la plateforme configurée (discord-frflo). Cela garantit l’alignement strict des clés de session, du routage et des vérifications pre-dispatch (résolvant tout conflit d’acheminement des commandes et des tâches d’arrière-plan).
  • Canal épuré et masquage du raisonnement (show_reasoning: false) : la trace de pensée (chain-of-thought) du modèle est masquée globalement et sur Discord. Pour préserver un fil de discussion net et éviter les clics accidentels ou les artefacts d’interface, les boutons interactifs d’affichage du raisonnement ont été retirés de l’adaptateur.
  • Désactivation du bruit d’exécution (tool_progress: 'off') : l’affichage verbeux de l’avancement des outils intermédiaires est désactivé (tool_progress: 'off', tool_preview_length: 0), permettant à Hermes d’enchaîner ses appels d’outils en silence avant de livrer directement sa réponse finale et vérifiée.
  • Fiabilité et intégrité du flux : gestion de la persistance WebSocket, contrôle des battements de cœur (heartbeats) et de latence, routage étanche des journaux vers le canal système (system_channel), suppression systématique des aperçus d’URL sortants (suppress_embeds), édition robuste des messages partiels (msg.edit) et découpage intelligent des réponses volumineuses préservant la syntaxe Markdown.

En complément des interactions interactives, Hermes intègre un moteur de tâches planifiées (cron) autonome sauvegardé sur un volume persistant dédié (hermes-cron) et une mémoire persistante. Il orchestre notamment des routines à deux temps : un job d’analyse prédictive en soirée (croisant emploi du temps Naurio et transports GTFS) qui engendre dynamiquement une alerte one-shot déclenchée à la minute exacte le matin.

Le modèle : une dépendance séparée

Hermes n’héberge pas le modèle. La configuration pointe vers :

model:
provider: custom
default: gemini-3.8-flash-high
base_url: https://<cliproxyapi host>/v1
api_key: not-needed
api_mode: chat_completions
fallback_providers:
- provider: custom
model: gpt-5.6-luna
base_url: https://<cliproxyapi host>/v1
api_key: not-needed
auxiliary:
vision:
provider: custom
model: gpt-5.6-luna
base_url: https://<cliproxyapi host>/v1
api_key: not-needed

Le service cli-proxy-api tourne lui aussi dans Docker, avec une configuration versionnée séparée. Il sert de façade compatible avec plusieurs fournisseurs et évite de coupler Hermes à une implémentation particulière de modèle.

Executor : le plan d’intégration

Executor est déployé avec ghcr.io/rhyssullivan/executor-selfhost:latest. Il centralise les intégrations et expose une surface MCP consommable par Hermes.

Son état est conservé dans le volume Docker executor, monté sur /data. La persistance est importante : les intégrations, la configuration et l’état du service ne doivent pas disparaître à chaque recréation du conteneur.

Hermes ne référence pas l’interface générale d’Executor, mais un toolkit MCP dédié :

mcp_servers:
executor:
url: https://<executor-host>/mcp/toolkits/hermes
auth: oauth

Le nom executor devient donc une frontière de capacités dans Hermes. L’agent ne connaît pas nécessairement l’implémentation de chaque outil en aval ; il découvre une surface MCP publiée par Executor.

MCP comme frontière de capacités

Le Model Context Protocol standardise la façon dont un client découvre et appelle des outils externes. Dans ce setup, Hermes est le client MCP et Executor est le serveur MCP visible par Hermes.

Hermes distingue les serveurs locaux et les serveurs MCP distants. Mon intégration Executor utilise un endpoint distant authentifié avec auth: oauth.

Cette organisation apporte une séparation utile :

  1. Hermes conserve la logique de conversation et de décision.
  2. Executor présente une surface d’outils cohérente à l’agent.
  3. Les serveurs spécialisés peuvent rester des services indépendants.
  4. Les identifiants et politiques d’intégration peuvent être centralisés dans Executor.

La configuration Hermes ne déclare explicitement qu’Executor. C’est donc Executor qui présente à Hermes la liste unifiée des intégrations et des outils disponibles.

Les MCP exposés à Hermes

Voici les intégrations actuellement exposées par le toolkit Hermes d’Executor :

Intégration Identifiant Executor Rôle dans le setup
Naurio naurio_mcp Outils d’emploi du temps et de planification.
GTFS gtfs Itinéraires et horaires de transports en commun.
Open-Meteo open-meteo Prévisions météorologiques et données historiques.
Google Maps google-maps Géocodage, lieux, itinéraires, trafic et fuseaux horaires.
Moodle moodle Accès à l’espace d’apprentissage Moodle.
oCIS & Qdrant RAG ocis_qdrant_indexer Recherche sémantique vectorielle sur la base documentaire ownCloud.
ownCloud Infinite Scale (oCIS) ocis_mcp_server Navigation, gestion des partages, métadonnées et espaces de travail ownCloud (80+ outils).
Dockhand dockhand_api Gestion et orchestration de la stack Docker du VPS.
Arkeep arkeep_api Gestion, vérification et déclenchement des sauvegardes de volumes Docker.
Cloudflare cloudflare_mcp Gestion des services réseau et des règles de sécurité Cloudflare.
Cloudflare Browser Rendering cloudflare_browser_rendering Rendu de pages web dynamiques, extraction et crawling headless sur l’Edge.
Google Chrome google_chrome_mcp Navigation Chrome complète avec DevTools Protocol et session visuelle VNC.
OpenLegi (Légifrance, RNE, BODACC, BOFiP) openlegi_* Accès aux codes de lois, jurisprudence, registre des entreprises et doctrine fiscale.
Données publiques (Data.gouv) data_gouv_mcp Requêtage et analyse des jeux de données ouverts français.
Pocket ID pocket_id_api Interaction avec le fournisseur d’identité OIDC/SSO.
GitHub github_graphql Consultation et gestion des dépôts, issues et pull requests.
Pelican Application / Admin pelican_api_application Administration du panel Pelican et de ses nodes.
Pelican Client pelican_api_client Contrôle des serveurs hébergés par Pelican.
Spotify spotify Lecture en cours, recherche et gestion des playlists.
Firecrawl firecrawl_mcp Scraping, extraction de contenu et crawl de sites.
GrepApp grep_mcp Recherche de code dans les dépôts publics.
Excalidraw excalidraw_app_demo Création de schémas, maquettes et tableaux blancs.
Context7 context7_mcp Recherche de contexte et de documentation pour les outils utilisés.

Cette liste donne une image plus juste de ce que signifie « donner des outils à Hermes ». L’agent ne reçoit pas seulement un accès à un outil unique : il dispose d’un catalogue transversal couvrant la vie quotidienne, l’infrastructure, le développement, la documentation et la création de schémas.

La séparation reste nette : Hermes choisit quand utiliser un outil et présente le résultat dans la conversation ; Executor porte les intégrations et les autorisations nécessaires ; chaque service conserve sa logique métier.

Isolation et sécurité

Les composants sont isolés des accès extérieurs directs. Les services internes ne sont pas exposés comme des ports publics ; lorsqu’un accès externe est nécessaire, il passe par les mécanismes de contrôle et d’authentification prévus par l’infrastructure.

Cette séparation limite la surface d’attaque et évite que chaque serveur MCP doive devenir lui-même une application publique. Hermes ne voit que les outils qu’Executor lui expose, tandis que les services spécialisés restent derrière cette frontière.

Le chemin d’une requête

Une interaction typique suit cette chaîne conceptuelle :

  1. Un message arrive dans le canal Discord autorisé.
  2. Le gateway Hermes vérifie l’utilisateur et le canal.
  3. Hermes restaure la session correspondante et prépare le contexte.
  4. Le modèle analyse la demande et peut choisir un outil.
  5. Hermes envoie alors l’appel à la surface MCP Executor.
  6. Executor applique sa logique d’intégration et transmet éventuellement l’appel à un serveur spécialisé.
  7. Le résultat revient dans la boucle Hermes, qui peut poursuivre son raisonnement.
  8. La réponse finale est envoyée à Discord.

Le modèle ne devient donc pas directement un administrateur du VPS. Il opère dans un ensemble de surfaces d’outils exposées par Hermes et Executor. La qualité du système dépend autant de cette sélection de capacités que du modèle lui-même.

Ce que cette architecture rend possible

Le bénéfice principal est le découplage. Un nouvel outil peut être ajouté en aval sans transformer Hermes en monolithe. Un autre client MCP pourrait également consommer Executor, tandis que Hermes reste le point d’accès conversationnel orienté Discord.

La centralisation crée aussi un endroit naturel pour traiter les intégrations, leurs secrets et leur politique d’exposition. À l’inverse, elle ajoute une dépendance supplémentaire : si Executor est indisponible, les outils qu’il publie le sont également, même si Hermes et le modèle fonctionnent encore.

Enfin, l’usage d’images latest rend le déploiement simple mais moins reproductible. Pour documenter ou reproduire exactement cette installation, il faudrait conserver les versions ou digests des images utilisées, ainsi que la version d’Hermes et les versions des serveurs MCP. Le fichier actuel décrit donc une architecture opérationnelle, pas un lockfile complet de l’infrastructure.

En résumé

Hermes Agent est ici la couche d’interaction et d’orchestration : il reçoit les messages Discord, gère les sessions et pilote le modèle. Executor est la passerelle MCP qui expose à Hermes une surface d’outils centralisée. Naurio, GTFS, Open-Meteo, Google Maps, Moodle, le RAG documentaire oCIS/Qdrant, ownCloud Infinite Scale MCP, Dockhand, Arkeep, Cloudflare, Cloudflare Browser Rendering, Google Chrome persistant (DevTools/VNC/CDP), OpenLegi, Data.gouv, Pocket ID, GitHub, Pelican, Spotify, Firecrawl, GrepApp, Excalidraw et Context7 composent le catalogue d’outils transversal disponible au runtime.

La valeur du setup ne vient pas d’un composant unique, mais des frontières entre eux. L’agent peut raisonner sans connaître chaque intégration ; les outils peuvent évoluer sans réécrire le gateway ; et l’infrastructure conserve le contrôle de la persistance et de l’exposition.