Basculer le menu
Changer de menu des préférences
Basculer le menu personnel
Non connecté(e)
Votre adresse IP sera visible au public si vous faites des modifications.

« Hermes » : différence entre les versions

De Le Wiki de Lug
 
(75 versions intermédiaires par le même utilisateur non affichées)
Ligne 2 : Ligne 2 :
= Prérequis =
= Prérequis =
(Hermes seul)
(Hermes seul)
* Une machine ou VM dédiée (LXC compatible)
* Une machine ou VM dédiée (LXC compatible / VM recommandée)
Pour un Hermes de base (sans LLM local) :
Pour un Hermes de base (sans LLM local) :
* RAM :
* RAM :
Ligne 16 : Ligne 16 :
* GPU :
* GPU :
** Inutile
** Inutile
= Installation Hermes (service dédié) =
= Installation Hermes (service dédié) =


Ligne 107 : Ligne 108 :
Cela permet d’éviter que Hermes tente de se connecter trop tôt à certains services encore en cours de démarrage.
Cela permet d’éviter que Hermes tente de se connecter trop tôt à certains services encore en cours de démarrage.
}}
}}
=== Activer le Dashboard ===
=== Activer le backend Hermes ===
Le Dashboard expose l’interface d’administration de Hermes ainsi que les API utilisées notamment pour les sessions, skills, configuration, jobs et certaines fonctions MCP.
Le backend Hermes expose les API utilisées notamment par le Dashboard, [[Hermes#Hermes_Workspace|Hermes Worspace]], [https://hermes-ai.net/desktop/ Hermes Desktop], les sessions, skills, configuration, jobs et certaines fonctions MCP.
 
La commande <code>hermes serve</code> permet de lancer ce backend en mode headless, sans ouvrir l'interface web du Dashboard.


Avant de modifier la configuration, on génère le hash du mot de passe qui sera utilisé pour l’authentification :
Avant de modifier la configuration, on génère le hash du mot de passe qui sera utilisé pour l’authentification :


  # python3 -c 'import hashlib,secrets,base64; p="<font color = blue>MON_MOT_DE_PASSE</font>"; s=secrets.token_bytes(16); d=hashlib.scrypt(p.encode(),salt=s,n=16384,r=8,p=1,dklen=32); print(f"scrypt$16384$8$1${base64.b64encode(s).decode()}${base64.b64encode(d).decode()}")'
  # python3 -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('<font color = blue>MON_MOT_DE_PASSE</font>'))"


La commande retourne une valeur de la forme :
La commande retourne une valeur de la forme :
  scrypt$16384$8$1$...
  scrypt$16384$8$1$...


On génère ensuite le secret utilisé pour signer les sessions du Dashboard :
On génère ensuite le secret utilisé pour signer les sessions :


  # openssl rand -hex 32
  # openssl rand -hex 32
Ligne 134 : Ligne 137 :
   | niveau = grave
   | niveau = grave
   | icône = grave
   | icône = grave
   | texte = Le Dashboard donne accès à des fonctions d’administration sensibles de Hermes. Dès qu’il écoute sur une interface réseau autre que <code>127.0.0.1</code>, Hermes impose une authentification. Utiliser un mot de passe fort et limiter également l’accès au port <code>9119</code> via firewall ou réseau privé.
   | texte = Le backend Hermes donne accès à des fonctions sensibles. Dès qu’il écoute sur une interface réseau autre que <code>127.0.0.1</code>, Hermes active la protection par authentification. Utiliser un mot de passe fort et limiter également l’accès au port <code>9119</code> via firewall ou réseau privé.
}}
}}


On crée un service systemd pour lancer automatiquement le Dashboard après Hermes Gateway :
On crée un service systemd pour lancer automatiquement le backend après Hermes Gateway :


  # vi /etc/systemd/system/hermes-dashboard.service
  # vi /etc/systemd/system/hermes-backend.service


Ajouter :
Ajouter :


  [Unit]
  [Unit]
  Description=Hermes Dashboard
  Description=Hermes Backend
  Requires=hermes-gateway.service
  Requires=hermes-gateway.service
  After=hermes-gateway.service
  After=hermes-gateway.service
Ligne 152 : Ligne 155 :
  User=root
  User=root
  Environment=HOME=/root
  Environment=HOME=/root
EnvironmentFile=/root/.hermes/.env
  WorkingDirectory=/root
  WorkingDirectory=/root
   
   
  ExecStartPre=/bin/sh -c 'until /usr/bin/curl -fsS <nowiki>http://</nowiki><font color = blue>IP_GATEWAY_HERMES</font>:<font color = green>8642</font>/health >/dev/null; do sleep 1; done'
  ExecStartPre=/bin/sh -c 'until /usr/bin/curl -fsS <nowiki>http://</nowiki><font color = blue>IP_GATEWAY_HERMES</font>:<font color = green>8642</font>/health >/dev/null; do sleep 1; done'
   
   
  ExecStart=/root/.local/bin/hermes dashboard --host <font color = blue>IP_DASHBOARD</font> --port <font color = green>9119</font> --no-open
  ExecStart=/root/.local/bin/hermes serve --host <font color = blue>IP_BACKEND_HERMES</font> --port <font color = green>9119</font>
   
   
  Restart=on-failure
  Restart=on-failure
Ligne 168 : Ligne 172 :
| icône = information
| icône = information
| texte = Remplacer <code><font color = blue>IP_GATEWAY_HERMES</font></code> par l’adresse à laquelle le Gateway Hermes est accessible.<br>
| texte = Remplacer <code><font color = blue>IP_GATEWAY_HERMES</font></code> par l’adresse à laquelle le Gateway Hermes est accessible.<br>
Remplacer <code><font color = blue>IP_DASHBOARD</font></code> par l’adresse locale de l’interface réseau sur laquelle le Dashboard doit écouter. Utiliser une adresse précise plutôt que <code>0.0.0.0</code> permet de limiter son exposition au réseau souhaité.
Remplacer <code><font color = blue>IP_BACKEND_HERMES</font></code> par l’adresse locale de l’interface réseau sur laquelle le backend doit écouter. Utiliser une adresse précise plutôt que <code>0.0.0.0</code> permet de limiter son exposition au réseau souhaité.
}}
}}
 
On active le plugin pour authentifier le dashboard :
# hermes plugins enable dashboard_auth/basic
On recharge la configuration systemd :
On recharge la configuration systemd :
  # systemctl daemon-reload
  # systemctl daemon-reload


On active le service au démarrage :
On active le service au démarrage :
  # systemctl enable hermes-dashboard.service
  # systemctl enable hermes-backend.service


On le démarre :
On le démarre :
  # systemctl start hermes-dashboard.service
  # systemctl start hermes-backend.service


Pour vérifier son état :
Pour vérifier son état :
  # systemctl status hermes-dashboard.service
  # systemctl status hermes-backend.service


Pour suivre les logs :
Pour suivre les logs :
  # journalctl -u hermes-dashboard.service -f
  # journalctl -u hermes-backend.service -f


Pour vérifier que le Dashboard écoute :
Pour vérifier que le backend écoute :
  # ss -lntp | grep 9119
  # ss -lntp | grep 9119


Pour tester son API :
Pour tester son API :
  # curl http://IP_HERMES:9119/api/status
  # curl <nowiki>http://</nowiki>IP_HERMES:9119/api/status
 
Pour ouvrir l'interface web d'administration, on peut toujours utiliser :
# hermes dashboard --host <font color = blue>IP_DASHBOARD</font> --port <font color = green>9119</font> --no-open


=== Utiliser une CA privée avec Hermes ===
=== Utiliser une CA privée avec Hermes ===
Ligne 279 : Ligne 287 :
Enfin, on redémarre le service :
Enfin, on redémarre le service :
  # systemctl restart hermes-gateway.service
  # systemctl restart hermes-gateway.service
=== Installer les dépendances pour la voix du CLI ===
{{Méta bandeau
  | niveau = information
  | icône = loupe
  | texte  =
Cette étape est utile uniquement si le CLI est exécuté localement sur le serveur Hermes Agent, avec un microphone et des haut-parleurs accessibles.
}}
# cd ~/.hermes/hermes-agent
# uv pip install --python ./venv/bin/python -e ".[voice]"
# apt update
# apt install libportaudio2
==== LXC ====
Voir [[Audio_Passthrough|audio passthrough]]


=== Signal ===
=== Signal ===
Ligne 295 : Ligne 316 :
Pour les pièces jointes natives (images, audio TTS, vidéos), installer signal-cli sur le même serveur qu'Hermes via [[Hermes#Native|l'installation native]].
Pour les pièces jointes natives (images, audio TTS, vidéos), installer signal-cli sur le même serveur qu'Hermes via [[Hermes#Native|l'installation native]].
  }}
  }}
<div class="mw-collapsible mw-collapsed" style="text-align:left;">
'''Procédure d'installation via <code>Docker</code> (non recommandée)'''
<div class="mw-collapsible-content">
  # mkdir -p /opt/signal-cli
  # mkdir -p /opt/signal-cli
  # vi /opt/signal-cli/docker-compose.yml
  # vi /opt/signal-cli/docker-compose.yml
Ligne 335 : Ligne 359 :
** Taper votre message : Hermes répond dans cette même conversation
** Taper votre message : Hermes répond dans cette même conversation
** (Optionnel) taper <code>/sethome</code> pour définir le canal par défaut pour les notifications et les tâches planifiées.
** (Optionnel) taper <code>/sethome</code> pour définir le canal par défaut pour les notifications et les tâches planifiées.
</div>
</div>


==== Native ====
==== Native ====
Ligne 341 : Ligne 367 :
  # apt update && apt upgrade
  # apt update && apt upgrade
  # apt install tar qrencode openjdk-<font color = green>25</font>-jre-headless
  # apt install tar qrencode openjdk-<font color = green>25</font>-jre-headless
On s'assure que Java 25 est la version utilisée par défaut :
# update-alternatives --set java /usr/lib/jvm/java-25-openjdk-amd64/bin/java
# java -version
{{Méta bandeau
{{Méta bandeau
   | niveau = information
   | niveau = information
   | icône = information
   | icône = information
   | texte  =   Si l'installation échoue, vérifier la compatibilité Java : <code>signal-cli --version</code>. En cas d'erreur <code>UnsupportedClassVersionError</code>, installer la version Java requise via <code>apt install openjdk-<font color = blue>XX</font>-jre-headless</code>.
   | texte  = Si <code>signal-cli --version</code> retourne une erreur <code>UnsupportedClassVersionError</code>, vérifier la version de Java actuellement utilisée avec <code>java -version</code>. Installer si nécessaire la version requise avec <code>apt install openjdk-XX-jre-headless</code>, puis la sélectionner avec <code>update-alternatives --config java</code>.
  }}
  }}
On Installe le programme :
On Installe le programme :
Ligne 365 : Ligne 394 :
   
   
  [Service]
  [Service]
Environment="JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64"
  ExecStart=/usr/local/bin/signal-cli --account +<font color = blue>VOTRE_NUMERO</font> daemon --http 127.0.0.1:8080
  ExecStart=/usr/local/bin/signal-cli --account +<font color = blue>VOTRE_NUMERO</font> daemon --http 127.0.0.1:8080
  Restart=always
  Restart=always
Ligne 387 : Ligne 417 :
** Taper votre message : Hermes répond dans cette même conversation
** Taper votre message : Hermes répond dans cette même conversation
** (Optionnel) taper <code>/sethome</code> pour définir le canal par défaut pour les notifications et les tâches planifiées.
** (Optionnel) taper <code>/sethome</code> pour définir le canal par défaut pour les notifications et les tâches planifiées.
==== Mise à jour automatique ====
==== Mise à jour automatique ====
{{Méta bandeau
{{Méta bandeau
Ligne 558 : Ligne 589 :


* <code>vision</code> : analyse d’images (modèle multimodal avec vision)
* <code>vision</code> : analyse d’images (modèle multimodal avec vision)
* <code>web_extract</code> : extraction et synthèse de contenu web (LLM texte)
* <code>compression</code> : compression et résumé du contexte (LLM texte avec un contexte suffisamment long)
* <code>compression</code> : compression et résumé du contexte (LLM texte avec un contexte suffisamment long)
* <code>skills_hub</code> : recherche et sélection de compétences (LLM texte léger)
* <code>skills_hub</code> : recherche et sélection de compétences (LLM texte léger)
Ligne 565 : Ligne 595 :
* <code>title_generation</code> : génération de titres de conversations (petit LLM texte rapide)
* <code>title_generation</code> : génération de titres de conversations (petit LLM texte rapide)
* <code>tts_audio_tags</code> : génération de balises expressives pour la synthèse vocale (LLM texte léger)
* <code>tts_audio_tags</code> : génération de balises expressives pour la synthèse vocale (LLM texte léger)
* <code>triage_specifier</code> : classification et orientation des demandes (LLM texte léger)
* <code>triage_specifier</code> : génération d’une spécification détaillée à partir d’une tâche placée dans le Triage du Kanban (LLM texte léger)
* <code>kanban_decomposer</code> : décomposition d’un projet en tâches Kanban (LLM texte avec de bonnes capacités de planification)
* <code>kanban_decomposer</code> : décomposition d’un projet en tâches Kanban (LLM texte avec de bonnes capacités de planification)
* <code>profile_describer</code> : génération d’une description de profil (LLM texte)
* <code>profile_describer</code> : génération d’une description de profil (LLM texte)
Ligne 573 : Ligne 603 :
* <code>moa_reference</code> : production d’une réponse de référence pour le MoA (LLM texte performant)
* <code>moa_reference</code> : production d’une réponse de référence pour le MoA (LLM texte performant)
* <code>moa_aggregator</code> : agrégation et synthèse des réponses MoA (LLM texte performant avec un contexte long)
* <code>moa_aggregator</code> : agrégation et synthèse des réponses MoA (LLM texte performant avec un contexte long)
* <code>session_search</code> : analyse et sélection des résultats de recherche dans les sessions (LLM texte léger)
* <code>memory_query_rewrite</code> : reformulation des requêtes destinées aux systèmes de mémoire afin d'améliorer la pertinence de la recherche (LLM texte léger)
* <code>flush_memories</code> : sélection et consolidation des souvenirs pertinents (LLM texte léger et fiable)
* <code>goal_judge</code> : évaluation de l'état d'un objectif actif afin de déterminer s'il est terminé, doit continuer ou doit attendre (LLM texte léger et fiable)
 
{{Méta bandeau
| niveau = information
| icône = loupe
| texte =
Dans les versions récentes de Hermes, <code>web_extract</code> n'utilise plus de modèle auxiliaire, <code>session_search</code> effectue directement ses recherches dans les sessions sans appel LLM et <code>flush_memories</code> a été supprimé. Ces trois anciennes entrées n'ont donc plus à être configurées dans <code>auxiliary</code>.
}}


==== Note sur vision et les modèles principaux multimodaux ====
==== Note sur vision et les modèles principaux multimodaux ====
Ligne 590 : Ligne 627 :
* <code>vision</code> : fortement recommandé avec un modèle multimodal séparé (environ 3 à 4 milliards de paramètres ; par exemple Qwen2.5-VL-3B-Instruct, Qwen3-VL-4B-Instruct ou Gemma 3 4B). Un modèle quantifié en Q4 est généralement suffisant pour l’analyse d’images courante. '''Pour de l’OCR/transcription de texte dense''', augmenter <code>--image-min-tokens</code>/<code>--image-max-tokens</code> (ex. 2048/8192) et ajouter un <code>--repeat-penalty 1.1</code> pour éviter les boucles de génération sur du texte peu lisible à basse résolution.
* <code>vision</code> : fortement recommandé avec un modèle multimodal séparé (environ 3 à 4 milliards de paramètres ; par exemple Qwen2.5-VL-3B-Instruct, Qwen3-VL-4B-Instruct ou Gemma 3 4B). Un modèle quantifié en Q4 est généralement suffisant pour l’analyse d’images courante. '''Pour de l’OCR/transcription de texte dense''', augmenter <code>--image-min-tokens</code>/<code>--image-max-tokens</code> (ex. 2048/8192) et ajouter un <code>--repeat-penalty 1.1</code> pour éviter les boucles de génération sur du texte peu lisible à basse résolution.
* <code>title_generation</code> : recommandé avec un petit modèle texte très rapide (environ 0,5 à 3 milliards de paramètres ; par exemple Qwen3 0.6B, 1.7B ou un modèle instruct équivalent). Tâche simple, ne nécessite pas un modèle puissant.
* <code>title_generation</code> : recommandé avec un petit modèle texte très rapide (environ 0,5 à 3 milliards de paramètres ; par exemple Qwen3 0.6B, 1.7B ou un modèle instruct équivalent). Tâche simple, ne nécessite pas un modèle puissant.
* <code>web_extract</code> : recommandé avec un modèle texte rapide disposant d’un contexte suffisamment long (environ 3 à 8 milliards de paramètres ; par exemple Qwen3 4B Instruct ou Gemma 3 4B). Privilégier un modèle capable de traiter au moins 32 000 jetons.
* <code>compression</code> : recommandé avec un modèle texte disposant d’un contexte long et de bonnes capacités de résumé. Un petit modèle rapide peut convenir si sa qualité et sa fenêtre de contexte sont suffisantes.
* <code>compression</code> : recommandé avec un modèle texte intermédiaire doté d’un contexte long et de bonnes capacités de résumé (environ 7 à 14 milliards de paramètres). Un modèle de 3-4B peut fonctionner mais risque de perdre davantage d’informations sur de longues conversations — '''à éviter si le modèle principal tourne avec un gros contexte (100K+)''', préférer laisser cette tâche sur le principal dans ce cas plutôt qu’un 4B sous-dimensionné.
* <code>mcp</code> : petit modèle texte fiable en sélection/appel d’outils (environ 3 à 8 milliards de paramètres). Éviter les modèles extrêmement petits si plusieurs outils ont des descriptions proches.
* <code>mcp</code> : petit modèle texte fiable en sélection/appel d’outils (environ 3 à 8 milliards de paramètres ; ex. Qwen3 4B Instruct). Éviter les modèles extrêmement petits si plusieurs outils ont des descriptions proches.
* <code>approval</code> : petit modèle texte fiable (environ 3 à 8 milliards de paramètres). Qualité et respect des consignes plus importants que la vitesse brute — valide des actions sensibles, ne pas sous-dimensionner.
* <code>approval</code> : petit modèle texte fiable (environ 3 à 8 milliards de paramètres). Qualité et respect des consignes plus importants que la vitesse brute — valide des actions sensibles, ne pas sous-dimensionner.
* <code>skills_hub</code>, <code>triage_specifier</code>, <code>tts_audio_tags</code>, <code>session_search</code>, <code>monitor</code> : petit modèle texte rapide suffisant (environ 1 à 4 milliards de paramètres).
* <code>skills_hub</code>, <code>triage_specifier</code>, <code>tts_audio_tags</code>, <code>monitor</code> et <code>memory_query_rewrite</code> : petit modèle texte rapide suffisant (environ 1 à 4 milliards de paramètres).
* <code>flush_memories</code> : petit modèle fiable capable de distinguer l’important du temporaire (environ 3 à 8 milliards de paramètres).
* <code>goal_judge</code> : petit modèle texte rapide mais fiable (environ 1 à 4 milliards de paramètres). La sortie est courte et structurée ; un modèle rapide convient bien s'il respecte correctement les consignes et le format demandé.
* <code>profile_describer</code> : petit ou moyen modèle texte (environ 3 à 8 milliards de paramètres).
* <code>profile_describer</code> : petit ou moyen modèle texte (environ 3 à 8 milliards de paramètres).
* <code>kanban_decomposer</code>, <code>background_review</code> : modèle texte intermédiaire avec de bonnes capacités de planification/analyse (environ 7 à 14 milliards de paramètres).
* <code>kanban_decomposer</code>, <code>background_review</code> : modèle texte intermédiaire avec de bonnes capacités de planification/analyse (environ 7 à 14 milliards de paramètres).
Ligne 605 : Ligne 641 :


* <code>vision</code> : modèle multimodal de 3 à 4 milliards de paramètres
* <code>vision</code> : modèle multimodal de 3 à 4 milliards de paramètres
* <code>title_generation</code> : modèle texte de 0,5 à 3 milliards de paramètres
* <code>title_generation</code> : petit modèle texte très rapide
* <code>web_extract</code> : modèle texte de 3 à 8 milliards de paramètres avec contexte long
* <code>triage_specifier</code> : petit modèle texte rapide
* <code>compression</code> : modèle texte de 7 à 14 milliards de paramètres avec de bonnes capacités de résumé
* <code>memory_query_rewrite</code> : petit modèle texte rapide
* <code>goal_judge</code> : petit modèle texte rapide et fiable
* <code>compression</code> : modèle disposant d’un contexte long et de bonnes capacités de résumé


Les autres fonctions peuvent rester sur <code>provider: auto</code> tant qu’elles ne provoquent pas de lenteurs, de délais d’attente ou une consommation excessive de ressources.
Les autres fonctions peuvent rester sur <code>provider: auto</code> tant qu’elles ne provoquent pas de lenteurs, de délais d’attente ou une consommation excessive de ressources.


==== Contexte recommandé par fonction ====
==== Contexte et reasoning recommandés par fonction ====


Le sur-dimensionnement du contexte (<code>-c</code>) est l’un des plus gros gaspillages de VRAM sur les petits modèles auxiliaires — le mettre par défaut à une valeur « confortable » unique pour toutes les routes coûte cher sans bénéfice.
Le sur-dimensionnement du contexte (<code>-c</code>) est l’un des plus gros gaspillages de VRAM sur les petits modèles auxiliaires. Il est donc préférable d’adapter le contexte à chaque fonction plutôt que d’utiliser une valeur unique « confortable » pour toutes les routes.
 
Hermes permet également de contrôler le niveau de reasoning indépendamment pour chaque tâche auxiliaire avec :
 
<pre>
auxiliary:
  vision:
    reasoning_effort: "none"
 
  compression:
    reasoning_effort: "low"
</pre>
 
Les valeurs disponibles sont :
 
<code>none</code>, <code>minimal</code>, <code>low</code>, <code>medium</code>, <code>high</code>, <code>xhigh</code>, <code>max</code> et <code>ultra</code>.
 
Une valeur vide ou absente laisse le provider choisir son comportement par défaut.
 
{{Méta bandeau
| niveau = information
| icône = loupe
| texte =
Certains modèles, comme LFM2.5-2.6B, ne permettent pas de sélectionner le niveau de raisonnement. Dans ce cas, laisser le champ vide.
}}


{| class="wikitable"
{| class="wikitable"
! Fonction !! Contexte suggéré !! Remarque
! Fonction !! Contexte suggéré !! Reasoning suggéré !! Remarque
|-
|-
| <code>title_generation</code> || 2-4K || Ne voit que les 1-2 premiers messages
| <code>title_generation</code> || 2-4K || <code>none</code> || Ne voit généralement que les premiers messages. Aucun raisonnement complexe nécessaire.
|-
|-
| <code>tts_audio_tags</code> || 2-4K || Passage de texte court à annoter
| <code>tts_audio_tags</code> || 2-4K || <code>none</code> || Annotation d’un passage de texte court. Le thinking apporte très peu.
|-
|-
| <code>triage_specifier</code> || 4K || Juste la requête à classifier
| <code>triage_specifier</code> || 4-8K || <code>none</code> / <code>minimal</code> || Développe une tâche courte du Triage Kanban en spécification plus détaillée. Un reasoning profond est généralement inutile.
|-
|-
| <code>skills_hub</code> || 4-8K || Liste de compétences + requête
| <code>memory_query_rewrite</code> || 4-8K || <code>none</code> / <code>minimal</code> || Reformulation courte destinée à améliorer la recherche mémoire ; aucun reasoning profond n'est normalement nécessaire.
|-
|-
| <code>session_search</code> || 8K || Résultats de recherche à trier
| <code>goal_judge</code> || 4-8K || <code>none</code> / <code>minimal</code> || Évalue l'objectif et la dernière réponse pour produire un verdict structuré. La fiabilité du respect du format est plus importante que la profondeur du reasoning.
|-
|-
| <code>monitor</code> || 8K || Selon la taille des logs surveillés
| <code>skills_hub</code> || 4-8K || <code>none</code> / <code>minimal</code> || Recherche ou sélection de compétences. Un reasoning profond est inutile dans la majorité des cas.
|-
|-
| <code>flush_memories</code> || 8K || Candidats mémoire à évaluer
| <code>monitor</code> || 8K || <code>none</code> / <code>minimal</code> || Selon la taille des données surveillées. Le plus souvent détection/classification.
|-
|-
| <code>profile_describer</code> || 8K || Historique modéré
| <code>profile_describer</code> || 8K || <code>low</code> || Synthèse d’un historique modéré.
|-
|-
| <code>approval</code> || 8-16K || Action proposée + contexte suffisant
| <code>approval</code> || 8-16K || <code>low</code> / <code>medium</code> || Analyse d’une action proposée et de son contexte. Conserver davantage de reasoning pour les décisions sensibles.
|-
|-
| <code>mcp</code> || 8-16K || Selon le nombre d’outils déclarés
| <code>mcp</code> || 8-16K || <code>low</code> || Dépend principalement du nombre et de la taille des définitions d’outils. Un peu de reasoning aide à sélectionner correctement l’outil.
|-
|-
| <code>kanban_decomposer</code> || 8-16K || Description de projet
| <code>kanban_decomposer</code> || 8-16K || <code>low</code> / <code>medium</code> || Décomposition d’un projet en tâches ; le reasoning peut réellement améliorer la structure.
|-
|-
| <code>vision</code> || 16K || Ajuster si usage OCR intensif (image seule jusqu’à 8K tokens)
| <code>vision</code> || 16K || <code>none</code> || Recommandé pour description/OCR simple : le modèle principal peut effectuer le raisonnement ensuite. Passer à <code>low</code> pour diagrammes ou analyse visuelle complexe. Ajuster le contexte si usage OCR intensif.
|-
|-
| <code>curator</code> || 16-32K || Peut agréger plusieurs sources
| <code>curator</code> || 16-32K || <code>low</code> || Peut agréger plusieurs sources et doit sélectionner les éléments pertinents.
|-
|-
| <code>background_review</code> || 16-32K || Selon ce qui est passé en revue
| <code>background_review</code> || 16-32K || <code>low</code> / hérité || Si un modèle auxiliaire distinct est utilisé, <code>low</code> est généralement suffisant. Un review fork utilisant le même modèle que l’agent hérite du reasoning du modèle principal.
|-
|-
| <code>web_extract</code> || 32K || Recommandation explicite
| <code>moa_reference</code> || 32K ou plus || Par preset || Le reasoning ne se configure pas via <code>auxiliary.moa_reference.reasoning_effort</code>. Il se règle individuellement dans <code>moa.presets.&lt;nom&gt;.reference_models[].reasoning_effort</code>.
|-
|-
| <code>moa_reference</code> || 32K || Réponses de référence potentiellement longues
| <code>compression</code> || 64-128K typiquement || <code>low</code> || Nécessite une fenêtre de contexte suffisamment grande pour recevoir le contenu à compresser.
|-
|-
| <code>compression</code> || 32-64K || Dépend de la longueur laissée s’accumuler avant déclenchement
| <code>moa_aggregator</code> || 64K minimum, souvent davantage || Par preset, généralement <code>medium</code> / <code>high</code> || Agrège plusieurs réponses longues et bénéficie réellement du reasoning. Le réglage se fait dans <code>moa.presets.&lt;nom&gt;.aggregator.reasoning_effort</code>.
|-
| <code>moa_aggregator</code> || 64K minimum || Exigence explicite (agrège plusieurs longues réponses)
|}
|}
===== Quand désactiver le reasoning =====
Le reasoning peut généralement être désactivé avec <code>reasoning_effort: "none"</code> lorsque la tâche consiste principalement à classer, annoter, extraire ou décrire des données sans prise de décision complexe.
Bonnes candidates :
* <code>title_generation</code>
* <code>tts_audio_tags</code>
* <code>triage_specifier</code>
* <code>memory_query_rewrite</code>
* <code>goal_judge</code>
* <code>vision</code> pour description ou OCR simple
* <code>monitor</code>
* <code>skills_hub</code>
Pour les tâches de synthèse ou de décision, <code>low</code> constitue généralement un meilleur compromis :
* <code>compression</code>
* <code>profile_describer</code>
* <code>curator</code>
* <code>mcp</code>
Les tâches de planification ou d’agrégation complexe peuvent conserver <code>medium</code> ou davantage :
* <code>kanban_decomposer</code>
* <code>approval</code> pour les décisions sensibles
* <code>moa_aggregator</code>
À noter que <code>reasoning_effort</code> est un raccourci Hermes. Si un <code>extra_body.reasoning</code> explicite est également défini pour la même tâche, celui-ci est prioritaire.
Pour le modèle principal, le réglage global reste :
<pre>
agent:
  reasoning_effort: "medium"
</pre>
et peut être modifié temporairement dans une session avec :
<pre>
/reasoning none
/reasoning low
/reasoning medium
/reasoning high
</pre>
Le réglage <code>/reasoning</code> concerne le modèle principal ; il ne remplace pas les réglages <code>reasoning_effort</code> propres aux tâches auxiliaires.
===== Cas particulier de la compression =====
Le contexte nécessaire pour <code>compression</code> dépend directement du volume de contexte transmis au modèle auxiliaire.
Le réglage général de la compression se fait dans :
<pre>
~/.hermes/config.yaml
</pre>
Par défaut :
<pre>
compression:
  enabled: true
  threshold: 0.50
</pre>
<code>threshold</code> représente une fraction de la fenêtre de contexte du modèle principal.
Par exemple, avec un modèle principal configuré à 262144 tokens :
<pre>
262144 × 0.50 = 131072 tokens
</pre>
La compression peut donc être déclenchée autour de 131K tokens.
Le modèle auxiliaire lui-même se configure séparément :
<pre>
auxiliary:
  compression:
    provider: auto
    model: NOM_DU_MODELE
    base_url: http://ADRESSE:PORT/v1
    api_key: CLE_API
    timeout: 120
</pre>
Le modèle utilisé pour la compression doit disposer d'une fenêtre de contexte suffisamment grande pour traiter les données que Hermes lui transmet.
Dans le cas d'un petit modèle local disposant malgré tout d'un contexte long, comme LFM2.5-2.6B configuré à 128K, celui-ci peut être particulièrement intéressant grâce à son coût réduit et à sa vitesse élevée de traitement du prompt.


==== Quantification (poids du modèle) ====
==== Quantification (poids du modèle) ====


* '''Tâches à faible enjeu''' (<code>title_generation</code>, <code>tts_audio_tags</code>, <code>skills_hub</code>, <code>monitor</code>, <code>session_search</code>, <code>triage_specifier</code>) : Q4_K_M suffit largement.
* '''Tâches à faible enjeu''' (<code>title_generation</code>, <code>tts_audio_tags</code>, <code>skills_hub</code>, <code>monitor</code>, <code>triage_specifier</code>, <code>memory_query_rewrite</code>, <code>goal_judge</code>) : Q4_K_M suffit largement.
* '''Tâches à enjeu réel''' (<code>approval</code>, <code>mcp</code>, <code>web_extract</code>, <code>compression</code>, <code>vision</code>) : privilégier Q5_K_M ou Q6_K — sur des modèles 4-8B, l’écart VRAM entre Q4 et Q6 reste faible (souvent &lt; 1,5 Go), autant prendre la marge.
* '''Tâches à enjeu réel''' (<code>approval</code>, <code>mcp</code>, <code>compression</code>, <code>vision</code>) : privilégier Q5_K_M ou Q6_K — sur des modèles 4-8B, l’écart VRAM entre Q4 et Q6 reste faible (souvent &lt; 1,5 Go), autant prendre la marge.


==== KV cache (contexte) ====
==== KV cache (contexte) ====


* '''En dessous de 16K de contexte''' : Pas de quantization ou q8_0/q8_0 en KV cache partout par défaut, sur toutes les routes auxiliaires — risque de dégradation négligeable, aucune raison de s'en priver même à faible contexte.
* '''En dessous de 16K de contexte''' : pas de quantification ou <code>q8_0</code>/<code>q8_0</code> en KV cache partout par défaut sur les routes auxiliaires — risque de dégradation négligeable.
* '''À partir de 32K''' (<code>web_extract</code>, <code>moa_reference</code>, <code>compression</code>) : <code>q8_0</code>/<code>q8_0</code> simple devient intéressant, gain notable pour un risque quasi nul.
* '''À partir de 32K''' (<code>moa_reference</code>, <code>compression</code>) : <code>q8_0</code>/<code>q8_0</code> devient intéressant, avec un gain notable de mémoire pour un risque faible.
* '''<code>moa_aggregator</code> à 64K+''' : seule route où une approche plus poussée type <code>q8_0</code>/<code>q6_0</code> + Hadamard (si le fork le supporte) se justifie, vu le volume réel de KV cache à comprimer.
* '''<code>moa_aggregator</code> à 64K+''' : seule route où une approche plus poussée type <code>q8_0</code>/<code>q6_0</code> + Hadamard (si le fork le supporte) peut se justifier, vu le volume réel de KV cache à comprimer.
 
=== Réglages de la voix ===
=== Réglages de la voix ===
==== Text-to-Speech (TTS) ====
==== Text-to-Speech (TTS) ====
Ligne 696 : Ligne 849 :
     voice: <font color="blue">default</font>
     voice: <font color="blue">default</font>
     base_url: http://<font color="blue">IP_CRISPASR</font>:<font color="blue">7981</font>/v1
     base_url: http://<font color="blue">IP_CRISPASR</font>:<font color="blue">7981</font>/v1
    <font color="green">consent_attestation: "I have the speaker's consent"</font>
  <font color="grey">...</font>
  <font color="grey">...</font>
 
{{Méta bandeau
| niveau = information
| icône = loupe
| texte =
<code><font color="green">consent_attestation: "I have the speaker's consent"</font></code> est nécessaire à l'utilisation du clonage de voix.
}}
Lorsque CrispASR est lancé avec une voix de référence fixe, la valeur <code>default</code> peut être utilisée pour l’option <code>voice</code>. La voix réellement utilisée dépend alors du fichier WAV et de sa transcription chargés par le serveur.
Lorsque CrispASR est lancé avec une voix de référence fixe, la valeur <code>default</code> peut être utilisée pour l’option <code>voice</code>. La voix réellement utilisée dépend alors du fichier WAV et de sa transcription chargés par le serveur.


Ligne 706 : Ligne 865 :
* Dans le fichier <code>~/.hermes/.env</code> :
* Dans le fichier <code>~/.hermes/.env</code> :
  <font color="grey">...</font>
  <font color="grey">...</font>
  VOICE_TOOLS_OPENAI_KEY=local
  VOICE_TOOLS_OPENAI_KEY=<font color="blue">local</font>
  <font color="grey">...</font>
  <font color="grey">...</font>


Ligne 713 : Ligne 872 :
   | icône = important
   | icône = important
   | texte =
   | texte =
La valeur <code>local</code> sert uniquement de clé fictive lorsque l’authentification n’est pas activée sur CrispASR. Hermes exige néanmoins qu’une clé soit définie pour utiliser le fournisseur OpenAI.
La valeur <code><font color="blue">local</font></code> sert uniquement de clé fictive lorsque l’authentification n’est pas activée sur CrispASR. Hermes exige néanmoins qu’une clé soit définie pour utiliser le fournisseur OpenAI.
}}
}}
====== Patch de Hermes pour autoriser les voix clonées ======
Actuellement, Hermes n'envoie pas l'attestation de consentement requise pour utiliser une voix clonée avec CrispTTS. Sans cette attestation, CrispTTS refuse la requête et Hermes bascule sur son moteur TTS de secours.
Voici un patch permettant d'ajouter cette attestation.
* On commence par sauvegarder le fichier actuel :
# cp ~/.hermes/hermes-agent/tools/tts_tool.py ~/.hermes/hermes-agent/tools/tts_tool.py.bak
* On édite ensuite le fichier :
# vi ~/.hermes/hermes-agent/tools/tts_tool.py
On remplace :
create_kwargs: Dict[str, Any] = {
    "model": model,
    "voice": voice,
    "input": text,
    "response_format": response_format,
    "extra_headers": {"x-idempotency-key": str(uuid.uuid4())},
}
par :
create_kwargs: Dict[str, Any] = {
    "model": model,
    "voice": voice,
    "input": text,
    "response_format": response_format,
    "extra_headers": {"x-idempotency-key": str(uuid.uuid4())},
    <font color = green>"extra_body": {
        "consent_attestation": "I have the speaker's consent"
    },</font>
}
* Un peu plus bas, on remplace :
if language:
    <font color = red>create_kwargs["extra_body"] = {"lang_code": language}</font>
par :
if language:
    <font color = green>create_kwargs["extra_body"]["lang_code"] = language</font>
Cette modification évite que le champ ''extra_body'' contenant l'attestation de consentement soit remplacé lorsqu'une langue est spécifiée.
* Enfin, on redémarre Hermes :
# systemctl restart hermes-gateway.service


==== Speech-to-Text (STT) ====
==== Speech-to-Text (STT) ====
Ligne 827 : Ligne 941 :


Après modification, on redémarre Hermes afin que la nouvelle configuration soit prise en compte.
Après modification, on redémarre Hermes afin que la nouvelle configuration soit prise en compte.
=== Changer le mode d'approbation des commandes ===
# vi ~/.hermes/config.yaml
approvals:
  mode: <font color="green">manual</font>
Remplacer <code>manual</code> par <code>smart</code> pour confier l’évaluation des commandes sensibles à un LLM. Les opérations jugées sûres sont automatiquement autorisées, les opérations dangereuses sont refusées et les cas incertains sont soumis à l’utilisateur. Par défaut, Hermes utilise le modèle principal pour cette évaluation, sauf si un modèle auxiliaire spécifique est configuré pour la tâche approval.


= Commandes =
= Commandes =
Ligne 859 : Ligne 980 :
* [https://github.com/0xNyk/awesome-hermes-agent Awesome Hermes Agent] : liste de différentes extensions classé par catégorie.
* [https://github.com/0xNyk/awesome-hermes-agent Awesome Hermes Agent] : liste de différentes extensions classé par catégorie.
* [https://hermesatlas.com/skills/ Hermes Agent skills] : top selon le site hermes atlas
* [https://hermesatlas.com/skills/ Hermes Agent skills] : top selon le site hermes atlas
== [https://github.com/fathah/hermes-desktop Hermes Agent Desktop] ==
Pour accéder a toutes les fonctionnalités sur une connexion distante il est préférable de se connecter via SSH, sur le serveur Hermes Agent :
# ssh-keygen -t ed25519 -f ~/.ssh/hermes_desktop
Ne PAS entrer de mot de passe :
Enter passphrase for "/root/.ssh/hermes_desktop" (empty for no passphrase):
Enter same passphrase again:
Ensuite, ajouter la clé publique aux clés autorisées du compte auquel Hermes Desktop doit se connecter :
# cat ~/.ssh/hermes_desktop.pub >> ~/.ssh/authorized_keys
# chmod 600 ~/.ssh/authorized_keys
=== Windows ===
On lance <code>PowerShell</code> et on récupère la clef :
> scp <font color = blue>utilisateur</font>@<font color = blue>IP_DU_SERVEUR</font>:~/.ssh/hermes_desktop "$env:USERPROFILE\.ssh\hermes_desktop"
Dans <code>Private Key Path</code> il faudra alors entrer :
C:\Users\<font color=blue>NOM_UTILISATEUR</font>\.ssh\hermes_desktop
== [https://github.com/nesquena/hermes-webui Hermes WebUI] ==
Sur le serveur Hermes Agent :
# apt update && apt upgrade
# apt install -y git python3 python3-venv
# cd /opt/
# git clone <nowiki>https://</nowiki>github.com/nesquena/hermes-webui.git
# cd hermes-webui
# ./ctl.sh start
On arrête temporairement le serveur :
# ./ctl.sh stop
On crée un service systemd pour qu'il se lance automatiquement :
# vi /etc/systemd/system/hermes-webui.service
[Unit]
Description=Hermes WebUI
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/opt/hermes-webui
Environment=HOME=/root
Environment=HERMES_HOME=/root/.hermes
Environment=HERMES_WEBUI_HOST=<font color = blue>127.0.0.1</font>
Environment=HERMES_WEBUI_PORT=<font color = green>8787</font>
EnvironmentFile=-/opt/hermes-webui/.env
ExecStart=/bin/bash /opt/hermes-webui/start.sh --foreground
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target
Par défaut l'interface n'est accessible que depuis le serveur, pour autoriser l'écoute sur une IP remplacer <code>HERMES_WEBUI_HOST=<font color = blue>127.0.0.1</font></code> par 0.0.0.0 ou une interface IP du serveur.
Ensuite on ajoute un mot de passe :
# vi /opt/hermes-webui/.env
HERMES_WEBUI_PASSWORD=<font color = blue>MOT_DE_PASSE</font>
# chmod 600 /opt/hermes-webui/.env
(Recommandé) On peut également conserver <code>127.0.0.1</code> et passer par un [[Caddy_ReverseProxy|proxy Caddy]].
On active le service systemd :
# systemctl daemon-reload
# systemctl enable --now hermes-webui.service
Pour pouvoir utiliser un reverse-proxy il faut ajouter :
# vi /opt/hermes-webui/.env
HERMES_WEBUI_PASSWORD=<font color = blue>MOT_DE_PASSE</font>
HERMES_WEBUI_SECURE=1
HERMES_WEBUI_ALLOWED_ORIGINS=<font color = blue><nowiki>https://</nowiki>hermes.exemple.net</font>
HERMES_WEBUI_TRUST_FORWARDED_HOST=1
HERMES_WEBUI_TRUST_FORWARDED_FOR=1
HERMES_WEBUI_TRUST_FORWARDED_PROTO=1
# systemctl restart hermes-webui
Exemple de vhost [[Nginx_ReverseProxy_LXC_Alpine_Linux|Nginx]] :
server {
    listen 80;
    listen [::]:80;
      server_name <font color = blue>hermes.exemple.net</font>;
    return 301 <nowiki>https://</nowiki>$host$request_uri;
}
server {
    server_name <font color = blue>hermes.exemple.net</font>;
    # protection
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000" always;
    client_max_body_size 256m;
    location / {
        proxy_set_header Host              $proxy_host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_http_version 1.1;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
        proxy_pass <nowiki>http<font color = red>s</font>://</nowiki><font color = blue>192.168.1.123</font>:<font color = green>8787</font>;
            proxy_ssl_verify off;
            proxy_ssl_server_name on;
            proxy_ssl_name 192.168.1.123;
        proxy_buffering off;
        proxy_cache off;
    }
    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<font color = blue>hermes.exemple.net</font>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<font color = blue>hermes.exemple.net</font>/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
}
=== Mise à jour ===
Mettre à jour Hermes Agent :
# hermes update
Mettre à jour Hermes WebUI :
# cd /opt/hermes-webui
# git pull --ff-only
# systemctl restart hermes-webui.service
=== Garder toutes les parties de réponse ouvertes (extension keep-expanded) ===
Par défaut, Hermes WebUI replie automatiquement les parties intermédiaires d'une réponse (le texte que l'IA écrit entre deux appels d'outils) dès qu'il y en a plus de 3 : un lien <code>Show N earlier updates</code> apparaît pour les retrouver. Ce comportement est codé en dur (<code>INTERIM_COLLAPSE_THRESHOLD=3</code> dans <code>static/messages.js</code>) et ne propose aucune option. Les modèles qui produisent beaucoup de petites notes de progression (Qwen par exemple) le rendent vite pénible, car le bloc de code final peut se retrouver dans une partie repliée.
Le WebUI supporte les extensions CSS (voir <code>docs/EXTENSIONS.md</code> dans le dépôt). Voici une petite extension maison qui désactive ce repli.
* Étape 1 — Créer le fichier CSS :
# mkdir -p ~/.hermes/webui/extensions
# vi ~/.hermes/webui/extensions/keep-expanded.css
Y coller :
/* Extension hermes-webui : garder depliee chaque segment de reponse
    (desactive le repli automatique des notes intermediaires). */
.interim-collapsed { display: revert !important; }
.interim-collapse-toggle { display: none !important; }
* Étape 2 — Déclarer l'extension au service : le service systemd lit déjà <code>EnvironmentFile=-/opt/hermes-webui/.env</code> (voir le service plus haut), on y ajoute donc deux lignes :
# vi /opt/hermes-webui/.env
HERMES_WEBUI_EXTENSION_DIR=/root/.hermes/webui/extensions
HERMES_WEBUI_EXTENSION_STYLESHEET_URLS=/extensions/keep-expanded.css
* Étape 3 — Redémarrer et vérifier :
# systemctl restart hermes-webui.service
# systemctl is-active hermes-webui.service
Puis recharger la page du WebUI dans le navigateur (F5) : toutes les parties des réponses restent ouvertes, et le lien <code>Show N earlier updates</code> a disparu.
{{Méta bandeau
  | niveau = information
  | icône = information
  | texte  = Redémarrer le service coupe brièvement la session WebUI en cours (les conversations sont sauvegardées, mais la réponse en cours de rédaction peut être perdue).
}}
'''Retour en arrière :''' supprimer les deux lignes de <code>/opt/hermes-webui/.env</code> (ou le fichier CSS), puis relancer le service — le repli automatique revient.
'''Résistance aux mises à jour :''' cette configuration survit aux mises à jour normales (<code>git pull</code> dans <code>/opt/hermes-webui</code> puis redémarrage) :
* <code>.env</code> est listé dans le <code>.gitignore</code> du projet : git ne le touche jamais ;
* le CSS vit dans <code>~/.hermes/webui/extensions/</code>, hors du dépôt git — c'est le répertoire officiellement recommandé pour les extensions.
Seuls une réinstallation complète (suppression + re-clone) ou un changement de nom des classes <code>interim-collapsed</code> / <code>interim-collapse-toggle</code> côté développeur pourraient casser l'extension. Si le repli revient tout seul après une mise à jour, il suffit de vérifier ces sélecteurs dans le CSS.
== [https://github.com/outsourc-e/hermes-workspace Hermes Workspace] ==
== [https://github.com/outsourc-e/hermes-workspace Hermes Workspace] ==
{{Méta bandeau
| niveau = information
| icône = important
| texte =
Le projet semble ne plus être maintenu ; il est recommandé d’utiliser [[Hermes#Hermes_WebUI|Hermes WebUI]].
}}
Un espace de travail complet : orchestrez vos agents, parcourez la mémoire, gérez les compétences et contrôlez l’ensemble depuis une interface unique.
Un espace de travail complet : orchestrez vos agents, parcourez la mémoire, gérez les compétences et contrôlez l’ensemble depuis une interface unique.
=== Docker ===
=== Docker ===
Ligne 865 : Ligne 1 174 :
| niveau = information
| niveau = information
| icône = loupe
| icône = loupe
| texte = Au moment d'écrire ce tutoriel l'accès au Dashboard et à la page MCP ne fonctionne plus avec la version officielle du conteneur, voir [[Hermes#Fix_Temporaire|cette procédure]] pour le patcher.
| texte = Au moment d'écrire ce tutoriel l'accès au Dashboard et à la page MCP ne fonctionne plus avec la version officielle du conteneur, voir [[Hermes#Fix_Temporaire|cette procédure]] pour le patcher. Sinon, utliser [https://github.com/LugMuad/hermes-workspace-voice mon fork].
}}
}}
  # mkdir -p /opt/hermes-workspace/hermes-workspace-data
  # mkdir -p /opt/hermes-workspace/hermes-workspace-data
Ligne 931 : Ligne 1 240 :
   
   
     client_max_body_size 256m;
     client_max_body_size 256m;
      location = /api/terminal-stream {
        proxy_pass http://<font color = blue>IP_SERVEUR_HERMES</font>:<font color = green>3000</font>;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
   
   
     location / {
     location / {
Ligne 969 : Ligne 1 299 :
  # cd hermes-workspace
  # cd hermes-workspace
  # git fetch origin pull/758/head:pr-758
  # git fetch origin pull/758/head:pr-758
  # git fetch origin pull/735/head:pr-735
  # git fetch origin pull/735/head:pr-759
  # git checkout -b hermes-fixed origin/main
  # git checkout -b hermes-fixed origin/main
  # git cherry-pick f7a50c5dcad8ba0f88e8d29e6f85c60b07681362
  # git cherry-pick f7a50c5dcad8ba0f88e8d29e6f85c60b07681362
Ligne 978 : Ligne 1 308 :


  # docker compose -f /opt/hermes-workspace/docker-compose.yml --project-directory /opt/hermes-workspace up -d
  # docker compose -f /opt/hermes-workspace/docker-compose.yml --project-directory /opt/hermes-workspace up -d
==== Réglage voix ====
==== Réglage voix ====
Pour le moment le plus simple est d'utiliser [https://github.com/LugMuad/hermes-workspace-voice mon fork].
==== Catalogue des Skills et serveurs MCP dans le conteneur ====
Pour le moment le plus simple est d'utiliser [https://github.com/LugMuad/hermes-workspace-voice mon fork].
<div class="mw-collapsible mw-collapsed" style="text-align:left;">
'''Modification de <code>Dockerfile</code> pour référence'''
<div class="mw-collapsible-content">
Le catalogue des Skills nécessite le script <code>skills-search.py</code> de Hermes Workspace ainsi que certains modules de Hermes Agent.
Pour les intégrer directement à l'image Docker :
# cd /opt/hermes-workspace
# vi Dockerfile
Ajouter les éléments suivant en bleu :
<font color = grey>...</font>
RUN apt-get update && apt-get install -y --no-install-recommends \
      ca-certificates curl tini python3 <font color = blue>python3-httpx python3-yaml</font> \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd -r workspace && useradd -r -g workspace -u 10010 -m workspace
<font color = blue>COPY scripts/skills-search.py /app/scripts/skills-search.py
RUN curl -L \
      <nowiki>https://</nowiki>codeload.github.com/NousResearch/hermes-agent/tar.gz/refs/heads/main \
      -o /tmp/hermes-agent.tar.gz \
    && mkdir -p /hermes-agent \
    && tar -xzf /tmp/hermes-agent.tar.gz -C /hermes-agent --strip-components=1 \
    && rm /tmp/hermes-agent.tar.gz
ENV PYTHONPATH=/hermes-agent</font>
<font color = grey>...</font>
Puis plus bas :
<font color = grey>...</font>
COPY --from=build --chown=workspace:workspace /app/dist ./dist
COPY --from=build --chown=workspace:workspace /app/node_modules ./node_modules
COPY --from=build --chown=workspace:workspace /app/package.json ./package.json
COPY --from=build --chown=workspace:workspace /app/server-entry.js ./server-entry.js
COPY --from=build --chown=workspace:workspace /app/skills ./skills
COPY --chown=workspace:workspace docker/entrypoint.sh /usr/local/bin/docker-entrypoint.sh
<font color = blue>COPY --chown=workspace:workspace assets/mcp-presets.seed.json ./assets/mcp-presets.seed.json</font>
<font color = grey>...</font>
Reconstruire ensuite l'image et recréer le conteneur :
# docker build -t hermes-workspace:fixed .
# docker compose up -d --force-recreate
On peut vérifier le fonctionnement avec :
# docker exec hermes-workspace python3 -c \
  'import httpx, yaml, tools.skills_hub; print("Skills Hub OK")'
Le résultat attendu est :
Skills Hub OK
</div>
</div>


== [https://github.com/robbyczgw-cla/hermes-web-search-plus Hermes Web Search Plus] avec [https://github.com/dondai44423/donsetch DonSeTch] ==
== [https://github.com/robbyczgw-cla/hermes-web-search-plus Hermes Web Search Plus] avec [https://github.com/dondai44423/donsetch DonSeTch] ==
Les opérations se déroulent directement sur l'hôte Hermes.
Les opérations se déroulent directement sur l'hôte Hermes.


La configuration présentée ici utilise DonSeTch avec SaerXNG et Firecrawl comme service secondaire afin de rester 100% local, il faut d’abord avoir [[SearXNG|installé une instance SearXNG]] et [[Firecrawl|installé une instance Firecrawl]].
La configuration présentée ici utilise DonSeTch avec SearXNG et Firecrawl comme services locaux. Il faut d’abord avoir [[SearXNG|installé une instance SearXNG]] et [[Firecrawl|installé une instance Firecrawl]].


* On installe le plugin :
* On installe et active le plugin :
  # cd ~/.hermes/plugins
  # hermes plugins install web-search-plus --enable
# git clone <nowiki>https://</nowiki>github.com/robbyczgw-cla/hermes-web-search-plus.git web-search-plus


On choisit le preset <code>self-hosted</code> afin de ne pas dépendre de services extérieurs ou payants :
Répondre <code>y</code> si l’installation des dépendances Node est proposée :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py setup --preset self-hosted
  Install them into the plugin's own node_modules now? [y/N]: y


Entrer l'adresse de son instance SearXNG :
Répondre <code>yes</code> si une autorisation est demandée :
  SearXNG key (SEARXNG_INSTANCE_URL, Enter to skip):
  This is a privileged capability: an override can intercept everything the agent routes
through that tool.
Grant it?


Il n'est pas obligatoire de renseigner une clé à l'invite suivante :
On initialise le plugin avec le preset <code>self-hosted</code> :
  Keenable key (KEENABLE_API_KEY, Enter to skip):
  # hermes web-search-plus setup --preset self-hosted


Si le service SearXNG est accessible via une adresse IP privée, il faut l'autoriser explicitement :
L’assistant propose ensuite d’activer Jev :
  # vi ~/.hermes/.env
  Enable optional Jev (TypeSafe) for search-type overlay, extract quality, and language fill? [y/N]:


<font color = grey>...
Jev est un service extérieur optionnel utilisé pour améliorer la détection du type de recherche, l’évaluation des extractions et la détection de la langue. Il n’est pas nécessaire pour une configuration locale avec SearXNG, DonSeTch et Firecrawl. Répondre :
SEARXNG_INSTANCE_URL=<nowiki>http://</nowiki><font color = blue>IP_PRIVEE_SEARXNG</font>:<font color = green>PORT</font></font>
  n
SEARXNG_ALLOW_PRIVATE=1
  <font color = grey>...</font>


Pour configurer Firecrawl, si une clef API est nécessaire il faut la renseigner dans le .env :
On renseigne l’adresse de l’instance SearXNG :
# hermes config set SEARXNG_INSTANCE_URL <nowiki>http://</nowiki><font color = blue>IP_PRIVEE_SEARXNG</font>:<font color = green>PORT</font>


  # vi ~/.hermes/.env
Si le service SearXNG est accessible via une adresse IP privée, il faut l’autoriser explicitement :
 
  # hermes config set SEARXNG_ALLOW_PRIVATE 1
<font color = grey>...</font>
FIRECRAWL_API_KEY=<font color = blue>FIRECRAWL_API_KEY</font>
<font color = grey>...</font>


Pour configurer l'instance Firecrawl, il faut modifier la configuration du plugin :
Le preset <code>self-hosted</code> peut utiliser Keenable comme service de secours extérieur sans clé API. Pour l’autoriser :
# hermes config set KEENABLE_ALLOW_PUBLIC 1


# vi ~/.hermes/plugins/web-search-plus/config.py
Cette commande doit être omise pour conserver une configuration strictement locale.
Et remplacer :
    "firecrawl": {
        "api_url": "<nowiki>https://</nowiki>api.firecrawl.dev/v2/search",
        "timeout": 30000,
        "sources": ["web"],
        "ignore_invalid_urls": False
    },
Par (exemple avec http simple) :
    "firecrawl": {
        "api_url": "<nowiki>http://</nowiki><font color = blue>IP_FIRECRAWL</font>:<font color = green>3002</font>/v2/search",
        "scrape_url": "<nowiki>http://</nowiki><font color = blue>IP_FIRECRAW</font>L:<font color = green>3002</font>/v2/scrape",
        "timeout": 30000,
        "sources": ["web"],
        "ignore_invalid_urls": False
    },
L'option suivante n'est nécessaire que si <code>web_extract_plus</code> doit être autorisé à extraire des pages situées sur un réseau privé ou un intranet :
    "extract": {
        # Target URLs supplied to extract_plus are blocked when they resolve to
        # private/internal networks. Operators can opt in for trusted intranet use.
        "allow_private_urls": <font color = blue>True</font>,
    },


On teste ensuite le fonctionnement du plugin :
Pour que Web Search Plus considère Firecrawl comme configuré, une valeur doit être renseignée dans <code>FIRECRAWL_API_KEY</code>. Si l’instance locale n’utilise aucune authentification, employer une valeur factice :
  # cd ~/.hermes/plugins/web-search-plus
  # hermes config set FIRECRAWL_API_KEY <font color = blue>local-firecrawl</font>
# python3 search.py --query "Hermes Agent latest release" --provider auto --quality-report


* On installe DonSeTch :
* On installe DonSeTch :
  # npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch
  # npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch


On installe Chromium :
Pour s'assurer que le service Hermes puisse trouver Node :
  # apt update && apt upgrade
# test -e /usr/local/bin/node || ln -s /root/.local/bin/node /usr/local/bin/node
  # apt install chromium
 
On installe Chromium et Xvfb pour les fonctions nécessitant un navigateur :
  # apt update
  # apt install chromium xvfb


On vérifie son fonctionnement :
On vérifie le fonctionnement de DonSeTch :
  # /root/.hermes/node/bin/donsetch --version
  # /root/.hermes/node/bin/donsetch --version
  # /root/.hermes/node/bin/donsetch doctor
  # /root/.hermes/node/bin/donsetch doctor


On ajoute le chemin du binaire DonSeTch à l'environnement d'Hermes :
On enregistre le chemin du binaire dans le profil Hermes :
  # vi ~/.hermes/.env
  # hermes config set DONSETCH_BIN /root/.hermes/node/bin/donsetch
 
...</font>
DONSETCH_BIN=/root/.hermes/node/bin/donsetch
<font color = grey>...</font>


Le preset <code>self-hosted</code> limite par défaut le routage automatique à SearXNG et Keenable. On réinitialise donc la configuration afin de repasser sur le profil <code>standard</code>, puis on autorise Web Search Plus à utiliser DonSeTch en priorité pour la recherche et Firecrawl pour l'extraction :
Le preset <code>self-hosted</code> limite le routage automatique à SearXNG et Keenable. Pour utiliser DonSeTch en priorité pour la recherche et Firecrawl pour l’extraction, on réinitialise la configuration avec le profil standard :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config reset --yes
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config reset --yes
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-routing on
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-routing on
# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-priority donsetch,searxng
# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-extract-priority donsetch,firecrawl


On désactive les autres fournisseurs :
On désactive les fournisseurs inutilisés :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable brave
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable brave
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable exa
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable exa
Ligne 1 080 : Ligne 1 437 :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable you
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config disable you


On réapplique les priorités de recherche et d'extraction :
On définit les priorités de recherche et d’extraction :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-priority donsetch,searxng
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-priority donsetch,searxng
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-extract-priority donsetch,firecrawl
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-extract-priority donsetch,firecrawl


On définit Keenable comme fournisseur de secours. Il s'agit d'un fournisseur extérieur disposant d'un accès gratuit limité sans clé API :
On autorise l’utilisation automatique de DonSeTch :
# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-fallback keenable
 
On autorise l'utilisation automatique de DonSeTch :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-auto-allow donsetch on
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-auto-allow donsetch on


Pour vérifier la configuration :
Si Keenable a été activé, on le définit comme fournisseur de secours :
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config show --json
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config set-fallback keenable


Enfin, on redémarre Hermes :
Cette commande doit également être omise pour conserver une configuration strictement locale.
# systemctl restart hermes-gateway.service


<div class="mw-collapsible mw-collapsed">
Le preset <code>self-hosted</code> ajoute l’autorisation d’utiliser le service public Keenable dans le fichier de routage du plugin. Ce réglage étant ignoré sous Hermes, il faut supprimer uniquement le bloc correspondant dans ce fichier.
'''Ancienne méthode pour remplacer les outils web natifs (non recommandée et partiellement fonctionnelle)'''
<div class="mw-collapsible-content">
* (Optionnel) On désactive les outils web natifs de Hermes pour utiliser uniquement ceux du plugin :
# vi ~/.hermes/config.yaml
Et on ajoute dans la section <code>agent:</code> :
<font color = grey>...
agent:
...
disabled_toolsets: [</font>web<font color = grey>]
...</font>
Enfin, on redémarre Hermes :
# systemctl restart hermes-gateway.service
</div>
</div>
=== Provider Web pour les outils natifs Hermes ===
<div class="mw-collapsible mw-collapsed">
'''Ancienne méthode pour référence, aujourd'hui intégrée nativement au plugin depuis la version 4.1.0'''
<div class="mw-collapsible-content">
Cette configuration permet de conserver les outils natifs <code>web_search</code> et <code>web_extract</code> de Hermes tout en remplaçant leur backend par Web Search Plus.


Le modèle continue donc d'utiliser les outils Web habituels de Hermes, mais les recherches et extractions sont transmises de manière transparente à Web Search Plus, qui applique ensuite sa propre configuration de routage vers DonSeTch, SearXNG, Firecrawl ou les autres providers activés.
On édite le fichier de routage :
# vi ~/.hermes/plugins/config.json


* Réactiver <code>web</code> dans la configuration d'Hermes si nécessaire (supprimer <code><font color = red>web</font></code>) :
On supprime uniquement le bloc suivant :
# vi ~/.hermes/config.yaml
    "keenable": {
        "allow_public": true
    },


<font color = grey>...
Il ne faut pas supprimer les autres occurrences de <code>keenable</code>, notamment celles qui le définissent comme fournisseur de secours.
agent:
...
disabled_toolsets: [</font><font color = red>web</font><font color = grey>]
...</font>


* Création d'un provider Web pour remplacer de façon transparente les backends utilisés par les outils natifs <code>web_search</code> et <code>web_extract</code> de Hermes :
On ajoute ou modifie ensuite l’objet <code>firecrawl</code> situé à la racine du fichier :
# mkdir -p ~/.hermes/plugins/web/wsp
  "firecrawl": {
    "api_url": "<nowiki>http://</nowiki><font color = blue>IP_FIRECRAWL</font>:<font color = green>3002</font>/v2/search",
    "scrape_url": "<nowiki>http://</nowiki><font color = blue>IP_FIRECRAWL</font>:<font color = green>3002</font>/v2/scrape"
  },


On crée le fichier de métadonnées du plugin :
L’option suivante n’est nécessaire que si <code>web_extract_plus</code> doit être autorisé à extraire des pages situées sur un réseau privé ou un intranet :
# vi ~/.hermes/plugins/web/wsp/plugin.yaml
  "extract": {
    "allow_private_urls": <font color = blue>true</font>
  },


Les objets doivent être séparés par une virgule afin de conserver un fichier JSON valide. Les valeurs booléennes JSON s’écrivent en minuscules : <code>true</code> et <code>false</code>.


Hermes peut réessayer automatiquement les appels Web qui échouent auprès de services externes anonymes comme Exa, Parallel, Firecrawl ou Keenable. Web Search Plus disposant déjà de son propre fournisseur de secours, on désactive ce second mécanisme :
# hermes config set web.keyless_rescue false


name: web-wsp
On vérifie la syntaxe du fichier de routage :
  version: 1.0.0
  # python3 -m json.tool ~/.hermes/plugins/config.json >/dev/null && echo "JSON valide"
description: "Web Search Plus backend for Hermes native web_search/web_extract tools."
author: local
kind: backend
provides_web_providers:
  - wsp


On crée ensuite le provider Web. Celui-ci sert d'adaptateur entre les outils natifs de Hermes et Web Search Plus :
On vérifie la configuration finale :
  # vi ~/.hermes/plugins/web/wsp/__init__.py
# hermes web-search-plus status
  # python3 ~/.hermes/plugins/web-search-plus/setup.py config show --json


<pre>
=== Provider Web pour les outils natifs Hermes ===
from __future__ import annotations
Web Search Plus peut servir directement de backend aux outils natifs <code>web_search</code> et <code>web_extract</code> de Hermes.


import json
On active Web Search Plus pour la recherche et l’extraction :
import subprocess
# hermes config set web.search_backend wsp
import sys
# hermes config set web.extract_backend wsp
from pathlib import Path
from typing import Any, Dict, List


from agent.web_search_provider import WebSearchProvider
Le backend <code>wsp</code> ne doit être sélectionné pour l’extraction que si au moins un fournisseur compatible est disponible, comme DonSeTch ou Firecrawl.


Les outils natifs nécessitent que le toolset <code>web</code> reste activé. Si celui-ci figure dans <code>agent.disabled_toolsets</code>, il faut le retirer.


WSP_DIR = Path.home() / ".hermes" / "plugins" / "web-search-plus"
On vérifie l’intégration :
WSP_SEARCH = WSP_DIR / "search.py"
# hermes web-search-plus fastpath
# hermes web-search-plus status


Enfin, on redémarre Hermes :
# systemctl restart hermes-gateway.service


class WSPWebSearchProvider(WebSearchProvider):
=== Test ===
On teste directement le fournisseur Firecrawl :
# cd ~/.hermes/plugins/web-search-plus
# FIRECRAWL_API_KEY=<font color = green>local-firecrawl</font> python3 search.py --query "Hermes Agent latest release" --provider firecrawl --quality-report


    @property
On lance ensuite Hermes :
    def name(self) -> str:
  # hermes
        return "wsp"
 
    @property
    def display_name(self) -> str:
        return "Web Search Plus"
 
    def is_available(self) -> bool:
        return WSP_SEARCH.exists()
 
    def supports_search(self) -> bool:
        return True
 
    def supports_extract(self) -> bool:
        return True
 
    def search(self, query: str, limit: int = 5) -> Dict[str, Any]:
        cmd = [
            sys.executable,
            str(WSP_SEARCH),
            "--query",
            query,
            "--provider",
            "auto",
            "--max-results",
            str(limit),
        ]
 
        try:
            proc = subprocess.run(
                cmd,
                cwd=str(WSP_DIR),
                capture_output=True,
                text=True,
                timeout=90,
                check=False,
            )
        except Exception as exc:
            return {
                "success": False,
                "error": f"Web Search Plus search failed: {exc}",
            }
 
        if proc.returncode != 0:
            return {
                "success": False,
                "error": proc.stderr.strip() or "Web Search Plus search failed",
            }
 
        try:
            data = json.loads(proc.stdout)
        except Exception as exc:
            return {
                "success": False,
                "error": f"Invalid Web Search Plus JSON: {exc}",
            }
 
        results = []
 
        for i, item in enumerate(data.get("results", [])[:limit], start=1):
            results.append(
                {
                    "title": str(item.get("title", "")),
                    "url": str(item.get("url", "")),
                    "description": str(
                        item.get("snippet")
                        or item.get("description")
                        or ""
                    ),
                    "position": i,
                }
            )
 
        return {
            "success": True,
            "data": {
                "web": results,
            },
        }
 
    def extract(
        self,
        urls: List[str],
        format: str = "markdown",
        **kwargs: Any,
    ) -> List[Dict[str, Any]]:
 
        cmd = [
            sys.executable,
            str(WSP_SEARCH),
            "--provider",
            "auto",
            "--extract-urls",
            *urls,
            "--format",
            format or "markdown",
        ]
 
        try:
            proc = subprocess.run(
                cmd,
                cwd=str(WSP_DIR),
                capture_output=True,
                text=True,
                timeout=90,
                check=False,
            )
        except Exception as exc:
            return [
                {
                    "url": url,
                    "title": "",
                    "content": "",
                    "error": f"Web Search Plus extraction failed: {exc}",
                }
                for url in urls
            ]
 
        if proc.returncode != 0:
            error = (
                proc.stderr.strip()
                or "Web Search Plus extraction failed"
            )
 
            return [
                {
                    "url": url,
                    "title": "",
                    "content": "",
                    "error": error,
                }
                for url in urls
            ]
 
        try:
            data = json.loads(proc.stdout)
        except Exception as exc:
            return [
                {
                    "url": url,
                    "title": "",
                    "content": "",
                    "error": f"Invalid Web Search Plus JSON: {exc}",
                }
                for url in urls
            ]
 
        if data.get("error") and not data.get("results"):
            return [
                {
                    "url": url,
                    "title": "",
                    "content": "",
                    "error": str(data["error"]),
                }
                for url in urls
            ]
 
        results = []
 
        for item in data.get("results", []):
            if not isinstance(item, dict):
                continue
 
            content = str(
                item.get("content")
                or item.get("markdown")
                or ""
            )
 
            results.append(
                {
                    "url": str(item.get("url", "")),
                    "title": str(item.get("title", "")),
                    "content": content,
                    "raw_content": str(
                        item.get("raw_content")
                        or content
                    ),
                    "metadata": {
                        "provider": data.get("provider"),
                        "fetcher": item.get("fetcher"),
                        "status": item.get("status"),
                        "quality": item.get("quality"),
                        "lang": item.get("lang"),
                        "source_type": item.get("source_type"),
                        "page_type": item.get("page_type"),
                    },
                }
            )
 
        return results
 
 
def register(ctx) -> None:
    ctx.register_web_search_provider(WSPWebSearchProvider())
</pre>
 
Le provider lance Web Search Plus dans un sous-processus distinct pour les recherches comme pour les extractions. Cela évite notamment les conflits entre les modules Python de Hermes et ceux de Web Search Plus.
 
On active ensuite le plugin :
# hermes plugins enable web-wsp
 
Répondre <code>yes</code> à la demande d'autorisation :
This is a privileged capability: an override can intercept everything the agent routes
through that tool.
Grant it?
 
Cette autorisation est nécessaire car le plugin fournit un backend susceptible d'intercepter les appels effectués par les outils Web natifs de Hermes.
 
On édite ensuite la configuration de Hermes pour utiliser le nouveau provider :
  # vi ~/.hermes/config.yaml
 
<font color = grey>...</font>
web:
  backend: <font color = blue>wsp</font>
  search_backend: <font color = blue>wsp</font>
  extract_backend: <font color = blue>wsp</font>
<font color = grey>...</font>


<code>backend</code> définit le backend Web général, tandis que <code>search_backend</code> et <code>extract_backend</code> permettent de sélectionner explicitement des backends différents pour la recherche et l'extraction.
On lui demande d’effectuer une recherche avec Web Search Plus :
Recherche la dernière version de Hermes Agent en utilisant web_search_plus et indique le fournisseur utilisé.


Avec cette configuration, les outils natifs <code>web_search</code> et <code>web_extract</code> de Hermes restent inchangés pour le modèle, mais leurs appels sont transmis de façon transparente à Web Search Plus.
On peut également demander un test explicite de DonSeTch :
 
  Recherche la documentation de Hermes Agent avec web_search_plus en imposant le fournisseur donsetch.
Enfin, on redémarre Hermes :
  # systemctl restart hermes-gateway.service
</div>
</div>
On édite la configuration de Hermes pour utiliser le nouveau provider :
# vi ~/.hermes/config.yaml
 
<font color = grey>...</font>
web:
  backend: <font color = blue>wsp</font>
  search_backend: <font color = blue>wsp</font>
  extract_backend: <font color = blue>wsp</font>
<font color = grey>...</font>
Enfin, on redémarre Hermes :
# systemctl restart hermes-gateway.service


=== Mise à jour ===
=== Mise à jour ===
* Hermes Web Search Plus
* Hermes Web Search Plus
  # hermes plugins update web-search-plus
  # hermes plugins update web-search-plus
Le scanner de sécurité de Hermes peut désactiver le plugin après la mise à jour si celui-ci obtient un verdict <code>dangerous</code>. Si le plugin est de nouveau désactivé, le réactiver manuellement :
 
Si l’installation des dépendances Node est de nouveau proposée, répondre <code>y</code>.
 
Le scanner de sécurité de Hermes peut désactiver le plugin après une mise à jour. Si nécessaire, le réactiver :
  # hermes plugins enable web-search-plus
  # hermes plugins enable web-search-plus
Répondre <code>yes</code> si demande d'autorisation :
 
This is a privileged capability: an override can intercept everything the agent routes
through that tool.
Grant it?
Puis redémarrer Hermes :
Puis redémarrer Hermes :
  # systemctl restart hermes-gateway.service
  # systemctl restart hermes-gateway.service
* DonSeTch :
 
* DonSeTch
On met à jour DonSeTch vers la dernière version disponible :
  # npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch@latest
  # npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch@latest
On vérifie ensuite la version et le bon fonctionnement de DonSeTch :
On vérifie ensuite la version et le bon fonctionnement de DonSeTch :
  # /root/.hermes/node/bin/donsetch --version
  # /root/.hermes/node/bin/donsetch --version
  # /root/.hermes/node/bin/donsetch doctor
  # /root/.hermes/node/bin/donsetch doctor
Sources :
* [https://github.com/robbyczgw-cla/hermes-web-search-plus/blob/main/docs/USER_GUIDE.md Guide de Web Search Plus]
* [https://github.com/robbyczgw-cla/hermes-web-search-plus/blob/main/docs/DONSETCH.md Intégration de DonSeTch]
* [https://github.com/robbyczgw-cla/hermes-web-search-plus/blob/main/docs/NATIVE_BACKEND.md Backend natif Hermes]


==[https://github.com/mnemosyne-oss/mnemosyne Mnemosyne]==
==[https://github.com/mnemosyne-oss/mnemosyne Mnemosyne]==
Ligne 1 630 : Ligne 1 748 :


  # systemctl restart hermes-gateway.service
  # systemctl restart hermes-gateway.service
=== Mise à jour ===
# source ~/.hermes/hermes-agent/venv/bin/activate
# python -m pip install --upgrade mnemosyne-hermes
# systemctl restart hermes-gateway.service
== [https://github.com/stephenschoettler/hermes-lcm Hermes-LCM] ==
Hermes-LCM est un plugin de gestion de contexte « lossless » pour Hermes Agent. Lorsqu’il est utilisé comme moteur de contexte, il remplace la compression ponctuelle du contexte actif par un système persistant basé sur SQLite et une structure hiérarchique en DAG.
On installe les fichiers du projet :
# git clone https://github.com/stephenschoettler/hermes-lcm \
  ~/.hermes/plugins/hermes-lcm
Puis on exécute le script d'installation afin d'exposer également le skill Hermes-LCM :
# cd ~/.hermes/plugins/hermes-lcm
# ./scripts/install.sh
On active la configuration de Hermes pour activer le plugin :
# vi ~/.hermes/config.yaml
Dans la sction <code>plugins:</code> ajouter :
<font color = grey>...
plugins:</font>
  enabled:
    - hermes-lcm
<font color = grey>...</font>
Puis rempalcer :
<font color = grey>...</font>
context:
  engine: compressor
<font color = grey>...</font>
Par :
<font color = grey>...</font>
context:
  engine: <font color = blue>lcm</font>
<font color = grey>...</font>
Enfin, on redémarre Hermes :
# systemctl restart hermes-gateway.service
=== Mise à jour ===
# cd ~/.hermes/plugins/hermes-lcm && git pull --ff-only


= MCP =
= MCP =

Dernière version du 28 octobre 2026 à 13:18

Hermes Atlas

Prérequis

(Hermes seul)

  • Une machine ou VM dédiée (LXC compatible / VM recommandée)

Pour un Hermes de base (sans LLM local) :

  • RAM :
    • 1 Go → minimum (test uniquement)
    • 2 Go → recommandé
    • 4 Go -> confortable
  • CPU / vCPU :
    • 1 vCPU → suffisant
    • 2 vCPU → recommandé
  • Espace disque :
    • 5 Go → minimum
    • 10 Go → confortable
  • GPU :
    • Inutile

Installation Hermes (service dédié)

Installation (Debian)

# apt update && apt upgrade
# apt install curl git
# curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
How would you like to set up Hermes?
> (○) Quick setup — provider, model & messaging (recommended)

  • Ollama local :
Select provider:
>(○) Custom endpoint (enter URL manually)
Custom OpenAI-compatible endpoint configuration:
API base URL [e.g. https://api.example.com/v1]: http://IP_OLLAMA:11434/v1
API key [optional]:
Detected model: mon_model
Use this model? [Y/n]: Y
Context length in tokens [leave blank for auto-detect]:
Display name [192.168.2.216:11434]: Ollama

  • Llama.cpp :
Select provider:
>(○) Custom endpoint (enter URL manually)
Custom OpenAI-compatible endpoint configuration:
API base URL [e.g. https://api.example.com/v1]: https://IP_OLLAMA:11434/v1

Remplir les options désirées puis fin de l'installation :

Launch hermes chat now? [Y/n]: y

Ensuite, inscrire Hermes comme service pour qu'il se lance automatiquement :


  • Sur une machine physique ou VM :
# hermes gateway install --system
  • Dans un LXC :
# hermes gateway install --system --run-as-user root

# systemctl enable hermes-gateway.service      
# systemctl start hermes-gateway.service

Configuration

Activer le serveur API

On édite le fichier :

# vi ~/.hermes/.env

Ajouter :

...
API_SERVER_ENABLED=true
API_SERVER_KEY="maclefsecrete"
# pour autoriser toutes les interfaces :
API_SERVER_HOST=0.0.0.0
# pour autoriser une seule interface :
# API_SERVER_HOST=IP_HERMES
API_SERVER_PORT=8642

On relance le service :

# systemctl restart hermes-gateway.service

Pour tester :

# curl http://IP_HERMES:8642/health

Doit renvoyer :

{"status":"ok"}

Pour vérifier le modèle exposé :

# curl http://IP_HERMES:8642/v1/models -H "Authorization: Bearer maclefsecrete"

Retarder le démarrage de Hermes au boot pour laisser le temps aux autres services de se lancer

# systemctl edit hermes-gateway.service

Ajouter dans la zone d’édition (partie supérieure du fichier) :

...
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStartPre=/bin/sh -c 'uptime=$(cut -d. -f1 /proc/uptime); [ "$uptime" -lt 120 ] && sleep 30 || true'

### Edits below this comment will be discarded
...

Activer le backend Hermes

Le backend Hermes expose les API utilisées notamment par le Dashboard, Hermes Worspace, Hermes Desktop, les sessions, skills, configuration, jobs et certaines fonctions MCP.

La commande hermes serve permet de lancer ce backend en mode headless, sans ouvrir l'interface web du Dashboard.

Avant de modifier la configuration, on génère le hash du mot de passe qui sera utilisé pour l’authentification :

# python3 -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('MON_MOT_DE_PASSE'))"

La commande retourne une valeur de la forme :

scrypt$16384$8$1$...

On génère ensuite le secret utilisé pour signer les sessions :

# openssl rand -hex 32

On édite alors le fichier :

# vi ~/.hermes/.env

Ajouter :

...
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASH="scrypt$16384$8$1$..."
HERMES_DASHBOARD_BASIC_AUTH_SECRET="secret_généré_par_openssl"
...

On crée un service systemd pour lancer automatiquement le backend après Hermes Gateway :

# vi /etc/systemd/system/hermes-backend.service

Ajouter :

[Unit]
Description=Hermes Backend
Requires=hermes-gateway.service
After=hermes-gateway.service

[Service]
Type=simple
User=root
Environment=HOME=/root
EnvironmentFile=/root/.hermes/.env
WorkingDirectory=/root

ExecStartPre=/bin/sh -c 'until /usr/bin/curl -fsS http://IP_GATEWAY_HERMES:8642/health >/dev/null; do sleep 1; done'

ExecStart=/root/.local/bin/hermes serve --host IP_BACKEND_HERMES --port 9119

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

On active le plugin pour authentifier le dashboard :

# hermes plugins enable dashboard_auth/basic

On recharge la configuration systemd :

# systemctl daemon-reload

On active le service au démarrage :

# systemctl enable hermes-backend.service

On le démarre :

# systemctl start hermes-backend.service

Pour vérifier son état :

# systemctl status hermes-backend.service

Pour suivre les logs :

# journalctl -u hermes-backend.service -f

Pour vérifier que le backend écoute :

# ss -lntp | grep 9119

Pour tester son API :

# curl http://IP_HERMES:9119/api/status

Pour ouvrir l'interface web d'administration, on peut toujours utiliser :

# hermes dashboard --host IP_DASHBOARD --port 9119 --no-open

Utiliser une CA privée avec Hermes

Si Hermes doit se connecter à un service HTTPS signé par une CA privée locale, il peut être nécessaire d’indiquer explicitement à Python et aux bibliothèques HTTP quel bundle de certificats utiliser.

Éditer l’override systemd du service :

# systemctl edit hermes-gateway.service

Ajouter dans la zone d’édition (partie supérieure du fichier) :

...
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
Environment="SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"
Environment="REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt"
Environment="CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt"

### Edits below this comment will be discarded
...

Explication rapide :

  • SSL_CERT_FILE : utilisé par Python / OpenSSL
  • REQUESTS_CA_BUNDLE : utilisé par la bibliothèque Python requests
  • CURL_CA_BUNDLE : utilisé par curl et parfois comme variable de repli

Puis recharger et redémarrer le service :

# systemctl daemon-reload
# systemctl restart hermes-gateway.service

Cas particulier : bundle certifi du venv Hermes

Dans certains cas, le runtime Python embarqué par Hermes peut continuer à utiliser le bundle interne de certifi au lieu du magasin système.

Si curl valide déjà le certificat avec /etc/ssl/certs/ca-certificates.crt mais que Hermes/OpenAI/httpx échoue avec CERTIFICATE_VERIFY_FAILED, on peut remplacer le bundle certifi du venv Hermes par le bundle système.

Sauvegarder le bundle actuel :

# cp /root/.hermes/hermes-agent/venv/lib/python3.11/site-packages/certifi/cacert.pem /root/.hermes/hermes-agent/venv/lib/python3.11/site-packages/certifi/cacert.pem.bak

Remplacer par le bundle système :

# cp /etc/ssl/certs/ca-certificates.crt /root/.hermes/hermes-agent/venv/lib/python3.11/site-packages/certifi/cacert.pem

Attention : cette modification peut être écrasée lors d’une mise à jour de Hermes, du venv Python ou du paquet certifi.

Automatiser le remplacement du certificat du venv

On édite le service Hermes :

# systemctl edit hermes-gateway.service

Ajouter dans la zone d’édition (partie supérieure du fichier) :

...
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStartPre=/bin/cp /etc/ssl/certs/ca-certificates.crt /root/.hermes/hermes-agent/venv/lib/python3.11/site-packages/certifi/cacert.pem

### Edits below this comment will be discarded
...

Puis recharger systemd et redémarrer le service :

# systemctl daemon-reload
# systemctl restart hermes-gateway.service

Éditer le prompt système

On édite le fichier suivant :

# vi ~/.hermes/SOUL.md

Puis on modifie le contenu de SOUL.md, qui constitue la partie personnalisée du prompt système de Hermes, par exemple :

You are Hermes Agent, an intelligent AI assistant created by Nous Research. You are helpful, knowledgeable, and direct. You assist users with a wide range of tasks including answering questions, writing and editing code, analyzing information, creative work, and executing actions via your tools. You communicate clearly, admit uncertainty when appropriate, and prioritize being genuinely useful over being verbose unless otherwise directed below. Be targeted and efficient in your exploration and investigations.
     TOOLS:
     - For current, recent, upcoming, time-sensitive, location-specific, or uncertain factual questions, use searxng_web_search.
     - To read a specific URL, use web_url_read.
     - For search suggestions, use searxng_search_suggestions.
     - For SearXNG instance capabilities, categories, engines, locales, and plugins, use searxng_instance_info.
     - For indexed documents, internal documentation, knowledge bases, or when the user asks to check the docs, use the RAGFlow MCP tools: ragflow_retrieval, ragflow_list_datasets, and ragflow_list_chats.
     RULES:
     - Respond in the same language as the user.
     - Do not invent tool results.
     - If a tool fails, say so explicitly.
     - Do not promise to continue later, follow up later, or say that results are still coming.
     - When the user asks multiple questions, use the tools needed, then answer all requested items in one final structured response.
     - Never answer current, recent, upcoming, time-sensitive, location-specific, or uncertain factual questions without using the required tool first.
     - This includes weather, news, prices, schedules, releases, availability, rankings, versions, and other facts that may have changed.
     - If no tool result is available, say you cannot verify and do not guess.
     - Use search categories when relevant.
     - For image searches, use searxng_web_search with categories=images.
     - For video searches, use searxng_web_search with categories=videos.
     - For image search results, use the `img_src` field as the direct image URL.
     - When displaying an image result, ALWAYS output the direct image URL exactly in this format: ![](URL)
     - Do not output the image URL as a normal Markdown link.
     - For video results, output links in this format: ~_~N [Titre de la vidéo](URL)
     - Do not use tools for simple rewriting, translation, or opinion unless needed.
     - Do not use terminal/curl to query RAGFlow unless explicitly asked for a network diagnostic.
     - If no RAGFlow dataset exists or no relevant result is found, say so clearly.

Enfin, on redémarre le service :

# systemctl restart hermes-gateway.service

Installer les dépendances pour la voix du CLI

# cd ~/.hermes/hermes-agent
# uv pip install --python ./venv/bin/python -e ".[voice]"
# apt update
# apt install libportaudio2

LXC

Voir audio passthrough

Signal

Signal est une messagerie instantanée axée sur la confidentialité. Tous les messages et appels y sont chiffrés de bout en bout par défaut via le Signal Protocol, de sorte que ni Signal ni un tiers ne peuvent en lire le contenu. Signal se distingue également par une collecte de données très limitée, l’absence de publicité et de traqueurs, ainsi qu’un effort particulier pour réduire les métadonnées accessibles au service.

Signal permet de relier Hermes à la messagerie chiffrée Signal. La connexion ne se fait pas directement avec le téléphone, mais via signal-cli exécuté en mode daemon HTTP, auquel Hermes se connecte ensuite.

  • Installation :

Via Docker

Procédure d'installation via Docker (non recommandée)

# mkdir -p /opt/signal-cli
# vi /opt/signal-cli/docker-compose.yml
services:
  signal-cli:
    image: registry.gitlab.com/packaging/signal-cli/signal-cli-jre:latest
    container_name: signal-cli
    restart: unless-stopped
    command: daemon --http 0.0.0.0:8080
    ports:
      - "IP_SERVEUR:8080:8080" # Remplacer par IP du serveur, ou 127.0.0.1 pour restreindre acces local uniquement
    volumes:
      - signal-cli-data:/var/lib/signal-cli
    tmpfs:
      - /tmp:exec

volumes:
  signal-cli-data:
 # docker compose -f /opt/signal-cli/docker-compose.yml --project-directory /opt/signal-cli up -d
  • Connexion au téléphone :

Installer qrencode :

# apt install qrencode

Dans le terminal :

# docker exec signal-cli signal-cli link -n "HermesAgent" > /tmp/signal-link.txt 2>&1 & sleep 3 && grep -o 'sgnl://[^ ]*' /tmp/signal-link.txt | qrencode -t ANSIUTF8 && wait

Puis scanner le QR code avec l’application Signal via Paramètres -> Appareils associés.

  • Connecter Hermes à Signal :
# hermes setup
Connect a messaging platform? (Telegram, Discord, etc.)
-> ●) Set up messaging now (recommended)

Puis redémarrer le service :

# systemctl restart hermes-gateway.service
  • Communiquer avec le serveur via Signal (depuis le téléphone dans l'application Signal) :
    • Appuyer sur l’icône ✏️ (nouveau message)
    • Tout en haut de la liste, "Note to Self" ou votre propre nom apparaît
    • Taper votre message : Hermes répond dans cette même conversation
    • (Optionnel) taper /sethome pour définir le canal par défaut pour les notifications et les tâches planifiées.

Native

  • Installation :

On installe les dépendances si nécessaire :

# apt update && apt upgrade
# apt install tar qrencode openjdk-25-jre-headless

On s'assure que Java 25 est la version utilisée par défaut :

# update-alternatives --set java /usr/lib/jvm/java-25-openjdk-amd64/bin/java
# java -version

On Installe le programme :

# cd /tmp
# VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed 's/^.*\/v//')
# curl -L -O "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}.tar.gz"
# tar xf "signal-cli-${VERSION}.tar.gz" -C /opt
# ln -sf "/opt/signal-cli-${VERSION}/bin/signal-cli" /usr/local/bin/
# rm /tmp/signal-cli-${VERSION}.tar.gz

Appareillage du téléphone :

# signal-cli link -n "HermesAgent" > /tmp/signal-link.txt 2>&1 & sleep 5 && grep -o 'sgnl://[^ ]*' /tmp/signal-link.txt | qrencode -t ANSIUTF8 && wait

Puis scanner le QR code avec l’application Signal via Paramètres -> Appareils associés.

On créé un service :

# vi /etc/systemd/system/signal-cli.service
[Unit]
Description=signal-cli daemon
After=network.target

[Service]
Environment="JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64"
ExecStart=/usr/local/bin/signal-cli --account +VOTRE_NUMERO daemon --http 127.0.0.1:8080
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
# systemctl daemon-reload
# systemctl enable --now signal-cli
# systemctl status signal-cli
  • Connecter Hermes à Signal :
# hermes setup
Connect a messaging platform? (Telegram, Discord, etc.)
-> ●) Set up messaging now (recommended)

Puis redémarrer le service :

# systemctl restart hermes-gateway.service
  • Communiquer avec le serveur via Signal (depuis le téléphone dans l'application Signal) :
    • Appuyer sur l’icône ✏️ (nouveau message)
    • Tout en haut de la liste, "Note to Self" ou votre propre nom apparaît
    • Taper votre message : Hermes répond dans cette même conversation
    • (Optionnel) taper /sethome pour définir le canal par défaut pour les notifications et les tâches planifiées.

Mise à jour automatique

  • Version native :

On créé un script :

# vi /usr/local/bin/update-signal-cli.sh
#!/bin/bash
set -e

systemctl stop signal-cli || true
cd /tmp

VERSION=$(curl -Ls -o /dev/null -w '%{url_effective}' https://github.com/AsamK/signal-cli/releases/latest | sed 's/^.*\/v//')

curl -L -O "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}.tar.gz"

rm -rf "/opt/signal-cli-${VERSION}"
tar xf "signal-cli-${VERSION}.tar.gz" -C /opt

ln -sf "/opt/signal-cli-${VERSION}/bin/signal-cli" /usr/local/bin/signal-cli
rm -f "/tmp/signal-cli-${VERSION}.tar.gz"

# Vérifier la compatibilité Java
if ! signal-cli --version > /dev/null 2>&1; then
    ERROR="$(signal-cli --version 2>&1 || true)"

    echo "$ERROR"

    REQUIRED="$(echo "$ERROR" | grep -oE 'class file (version|major version) [0-9]+' | grep -oE '[0-9]+' | tail -n1 || true)"

    if [ -z "$REQUIRED" ]; then
        echo "Impossible de détecter automatiquement la version Java requise."
        echo "Installation de Java 21 par défaut..."
        JAVA_VERSION=21
    else
        JAVA_VERSION=$((REQUIRED - 44))
        echo "Class file version $REQUIRED détectée => Java $JAVA_VERSION requis."
    fi

    apt update
    apt install -y "openjdk-${JAVA_VERSION}-jre-headless"

    if [ -x "/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64/bin/java" ]; then
        update-alternatives --set java "/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64/bin/java"
    else
        echo "Java $JAVA_VERSION installé, mais chemin update-alternatives non trouvé. Vérifie avec :"
        echo "update-alternatives --config java"
    fi
fi

systemctl start signal-cli
# chmod +x /usr/local/bin/update-signal-cli.sh

On ajoute le script au crontab :

# crontab -e

Ajouter :

...
0 3 1 * * /usr/local/bin/update-signal-cli.sh
  • Version docker :

Voir Watchtower pour la mise à jour automatique de tous les containers Docker. (tuto à faire..)

SearXNG

Il faut d’abord avoir installé une instance SearXNG.

Sur le serveur Hermes, lancer :

# hermes tools
→ (○) Reconfigure an existing tool's provider or API key
→ (○) 🔍 Web Search & Scraping
→ (●) SearXNG [free · self-hosted] — Free, privacy-respecting metasearch. Point SEARXNG_URL at your instance. [active]

Entrer ensuite l’adresse de l’instance SearXNG :

SearXNG instance URL (e.g. http://localhost:8080) (Enter to keep current):

Scraping via Firecrawl

Il faut d’abord avoir installé une instance Firecrawl.

On renseigne l'adresse de l'instance Firecrawl :

# vi ~/.hermes/.env
...
FIRECRAWL_API_URL=http://IP_DE_FIRECRAWL:3002
...

Si besoin configurer l'API key :

...
FIRECRAWL_API_KEY=c-xxxxxxxxxxxxxxxx
...

Ensuite on édite la configuration de Hermes :

# vi ~/.hermes/config.yaml

On remplace :

web:
  backend: searxng
  search_backend: 
  extract_backend: 

Par :

web:
  backend: searxng
  search_backend: searxng
  extract_backend: firecrawl

Enfin on redémarre Hermes :

# systemctl restart hermes-gateway.service

Camofox

Installation native

Il faut d’abord avoir installé une instance Camofox.

Sur le serveur Hermes, lancer :

# hermes tools
→ (○) Reconfigure an existing tool's provider or API key
→ (○) 🌐 Browser Automation
→ (●) Camofox [free · local] — Anti-detection browser (Firefox/Camoufox)

Entrer ensuite l’adresse de l’instance Camofox:

Camofox server URL (Enter to keep current):
  • Règle prompt système facultative :
...
  RULES:
  - If `web_extract` fails or is blocked, use `browser_navigate` through the Camofox browser provider.
...

Via MCP

Voir MCP-CamoFox

Forcer les timeouts d'une IA locale pour une IA derrière une URL

Si l'API de votre IA locale est desservie derrière une URL publique, Hermes peut ne pas la détecter comme un fournisseur local. Il est possible de modifier les timeouts utilisés pour les fournisseurs locaux :

# vi ~/.hermes/.env
...
HERMES_API_TIMEOUT=1800
HERMES_STREAM_READ_TIMEOUT=1800
# HERMES_STREAM_STALE_TIMEOUT=180
HERMES_API_CALL_STALE_TIMEOUT=900
HERMES_AGENT_TIMEOUT=900
# systemctl restart hermes-gateway.service

Fonctions auxiliaires

Configuration des modèles utilisés par Hermes pour les tâches secondaires, indépendamment du modèle principal de conversation.

Editer la section auxiliary dans :

# vi ~/.hermes/config.yaml
  • vision : analyse d’images (modèle multimodal avec vision)
  • compression : compression et résumé du contexte (LLM texte avec un contexte suffisamment long)
  • skills_hub : recherche et sélection de compétences (LLM texte léger)
  • approval : évaluation et validation des actions sensibles (LLM texte léger et fiable)
  • mcp : sélection et routage des outils MCP (LLM texte efficace en appel d’outils)
  • title_generation : génération de titres de conversations (petit LLM texte rapide)
  • tts_audio_tags : génération de balises expressives pour la synthèse vocale (LLM texte léger)
  • triage_specifier : génération d’une spécification détaillée à partir d’une tâche placée dans le Triage du Kanban (LLM texte léger)
  • kanban_decomposer : décomposition d’un projet en tâches Kanban (LLM texte avec de bonnes capacités de planification)
  • profile_describer : génération d’une description de profil (LLM texte)
  • curator : sélection et organisation du contenu (LLM texte avec de bonnes capacités de synthèse)
  • monitor : analyse des résultats de surveillance (LLM texte léger)
  • background_review : révision et analyse en arrière-plan (LLM texte)
  • moa_reference : production d’une réponse de référence pour le MoA (LLM texte performant)
  • moa_aggregator : agrégation et synthèse des réponses MoA (LLM texte performant avec un contexte long)
  • memory_query_rewrite : reformulation des requêtes destinées aux systèmes de mémoire afin d'améliorer la pertinence de la recherche (LLM texte léger)
  • goal_judge : évaluation de l'état d'un objectif actif afin de déterminer s'il est terminé, doit continuer ou doit attendre (LLM texte léger et fiable)

Note sur vision et les modèles principaux multimodaux

Certains modèles principaux (ex. Qwen3.6-27B) sont nativement multimodaux (encodeur vision intégré au checkpoint) — ce n’est donc pas systématiquement une histoire de « modèle texte qui ne sait pas voir ». Même dans ce cas, garder vision sur un petit modèle auxiliaire séparé reste recommandé pour trois raisons :

  1. Évite de mobiliser le modèle principal (VRAM + temps de calcul) pour une tâche que 4B suffit à traiter.
  2. Le petit modèle peut se décharger automatiquement (--sleep-idle-seconds), pas forcément possible/souhaitable sur le principal.
  3. Latence : un 4B répond bien plus vite qu’un 27B pour une simple description/OCR d’image.

Recommandations

Dans une configuration locale disposant de peu de VRAM disponible, il est recommandé d’attribuer certaines fonctions auxiliaires à des modèles plus petits et plus rapides, afin d’éviter de mobiliser inutilement le modèle principal.

  • vision : fortement recommandé avec un modèle multimodal séparé (environ 3 à 4 milliards de paramètres ; par exemple Qwen2.5-VL-3B-Instruct, Qwen3-VL-4B-Instruct ou Gemma 3 4B). Un modèle quantifié en Q4 est généralement suffisant pour l’analyse d’images courante. Pour de l’OCR/transcription de texte dense, augmenter --image-min-tokens/--image-max-tokens (ex. 2048/8192) et ajouter un --repeat-penalty 1.1 pour éviter les boucles de génération sur du texte peu lisible à basse résolution.
  • title_generation : recommandé avec un petit modèle texte très rapide (environ 0,5 à 3 milliards de paramètres ; par exemple Qwen3 0.6B, 1.7B ou un modèle instruct équivalent). Tâche simple, ne nécessite pas un modèle puissant.
  • compression : recommandé avec un modèle texte disposant d’un contexte long et de bonnes capacités de résumé. Un petit modèle rapide peut convenir si sa qualité et sa fenêtre de contexte sont suffisantes.
  • mcp : petit modèle texte fiable en sélection/appel d’outils (environ 3 à 8 milliards de paramètres). Éviter les modèles extrêmement petits si plusieurs outils ont des descriptions proches.
  • approval : petit modèle texte fiable (environ 3 à 8 milliards de paramètres). Qualité et respect des consignes plus importants que la vitesse brute — valide des actions sensibles, ne pas sous-dimensionner.
  • skills_hub, triage_specifier, tts_audio_tags, monitor et memory_query_rewrite : petit modèle texte rapide suffisant (environ 1 à 4 milliards de paramètres).
  • goal_judge : petit modèle texte rapide mais fiable (environ 1 à 4 milliards de paramètres). La sortie est courte et structurée ; un modèle rapide convient bien s'il respecte correctement les consignes et le format demandé.
  • profile_describer : petit ou moyen modèle texte (environ 3 à 8 milliards de paramètres).
  • kanban_decomposer, background_review : modèle texte intermédiaire avec de bonnes capacités de planification/analyse (environ 7 à 14 milliards de paramètres).
  • curator : modèle texte intermédiaire à performant pour synthèse (7 à 14 milliards de paramètres, plus pour analyses complexes).
  • moa_reference : modèle texte performant (14 milliards de paramètres ou plus).
  • moa_aggregator : modèle texte performant avec contexte long (14B+, au moins 64 000 jetons de contexte).

Pour une première optimisation, il est conseillé de configurer en priorité :

  • vision : modèle multimodal de 3 à 4 milliards de paramètres
  • title_generation : petit modèle texte très rapide
  • triage_specifier : petit modèle texte rapide
  • memory_query_rewrite : petit modèle texte rapide
  • goal_judge : petit modèle texte rapide et fiable
  • compression : modèle disposant d’un contexte long et de bonnes capacités de résumé

Les autres fonctions peuvent rester sur provider: auto tant qu’elles ne provoquent pas de lenteurs, de délais d’attente ou une consommation excessive de ressources.

Contexte et reasoning recommandés par fonction

Le sur-dimensionnement du contexte (-c) est l’un des plus gros gaspillages de VRAM sur les petits modèles auxiliaires. Il est donc préférable d’adapter le contexte à chaque fonction plutôt que d’utiliser une valeur unique « confortable » pour toutes les routes.

Hermes permet également de contrôler le niveau de reasoning indépendamment pour chaque tâche auxiliaire avec :

auxiliary:
  vision:
    reasoning_effort: "none"

  compression:
    reasoning_effort: "low"

Les valeurs disponibles sont :

none, minimal, low, medium, high, xhigh, max et ultra.

Une valeur vide ou absente laisse le provider choisir son comportement par défaut.

Fonction Contexte suggéré Reasoning suggéré Remarque
title_generation 2-4K none Ne voit généralement que les premiers messages. Aucun raisonnement complexe nécessaire.
tts_audio_tags 2-4K none Annotation d’un passage de texte court. Le thinking apporte très peu.
triage_specifier 4-8K none / minimal Développe une tâche courte du Triage Kanban en spécification plus détaillée. Un reasoning profond est généralement inutile.
memory_query_rewrite 4-8K none / minimal Reformulation courte destinée à améliorer la recherche mémoire ; aucun reasoning profond n'est normalement nécessaire.
goal_judge 4-8K none / minimal Évalue l'objectif et la dernière réponse pour produire un verdict structuré. La fiabilité du respect du format est plus importante que la profondeur du reasoning.
skills_hub 4-8K none / minimal Recherche ou sélection de compétences. Un reasoning profond est inutile dans la majorité des cas.
monitor 8K none / minimal Selon la taille des données surveillées. Le plus souvent détection/classification.
profile_describer 8K low Synthèse d’un historique modéré.
approval 8-16K low / medium Analyse d’une action proposée et de son contexte. Conserver davantage de reasoning pour les décisions sensibles.
mcp 8-16K low Dépend principalement du nombre et de la taille des définitions d’outils. Un peu de reasoning aide à sélectionner correctement l’outil.
kanban_decomposer 8-16K low / medium Décomposition d’un projet en tâches ; le reasoning peut réellement améliorer la structure.
vision 16K none Recommandé pour description/OCR simple : le modèle principal peut effectuer le raisonnement ensuite. Passer à low pour diagrammes ou analyse visuelle complexe. Ajuster le contexte si usage OCR intensif.
curator 16-32K low Peut agréger plusieurs sources et doit sélectionner les éléments pertinents.
background_review 16-32K low / hérité Si un modèle auxiliaire distinct est utilisé, low est généralement suffisant. Un review fork utilisant le même modèle que l’agent hérite du reasoning du modèle principal.
moa_reference 32K ou plus Par preset Le reasoning ne se configure pas via auxiliary.moa_reference.reasoning_effort. Il se règle individuellement dans moa.presets.<nom>.reference_models[].reasoning_effort.
compression 64-128K typiquement low Nécessite une fenêtre de contexte suffisamment grande pour recevoir le contenu à compresser.
moa_aggregator 64K minimum, souvent davantage Par preset, généralement medium / high Agrège plusieurs réponses longues et bénéficie réellement du reasoning. Le réglage se fait dans moa.presets.<nom>.aggregator.reasoning_effort.
Quand désactiver le reasoning

Le reasoning peut généralement être désactivé avec reasoning_effort: "none" lorsque la tâche consiste principalement à classer, annoter, extraire ou décrire des données sans prise de décision complexe.

Bonnes candidates :

  • title_generation
  • tts_audio_tags
  • triage_specifier
  • memory_query_rewrite
  • goal_judge
  • vision pour description ou OCR simple
  • monitor
  • skills_hub

Pour les tâches de synthèse ou de décision, low constitue généralement un meilleur compromis :

  • compression
  • profile_describer
  • curator
  • mcp

Les tâches de planification ou d’agrégation complexe peuvent conserver medium ou davantage :

  • kanban_decomposer
  • approval pour les décisions sensibles
  • moa_aggregator

À noter que reasoning_effort est un raccourci Hermes. Si un extra_body.reasoning explicite est également défini pour la même tâche, celui-ci est prioritaire.

Pour le modèle principal, le réglage global reste :

agent:
  reasoning_effort: "medium"

et peut être modifié temporairement dans une session avec :

/reasoning none
/reasoning low
/reasoning medium
/reasoning high

Le réglage /reasoning concerne le modèle principal ; il ne remplace pas les réglages reasoning_effort propres aux tâches auxiliaires.

Cas particulier de la compression

Le contexte nécessaire pour compression dépend directement du volume de contexte transmis au modèle auxiliaire.

Le réglage général de la compression se fait dans :

~/.hermes/config.yaml

Par défaut :

compression:
  enabled: true
  threshold: 0.50

threshold représente une fraction de la fenêtre de contexte du modèle principal.

Par exemple, avec un modèle principal configuré à 262144 tokens :

262144 × 0.50 = 131072 tokens

La compression peut donc être déclenchée autour de 131K tokens.

Le modèle auxiliaire lui-même se configure séparément :

auxiliary:
  compression:
    provider: auto
    model: NOM_DU_MODELE
    base_url: http://ADRESSE:PORT/v1
    api_key: CLE_API
    timeout: 120

Le modèle utilisé pour la compression doit disposer d'une fenêtre de contexte suffisamment grande pour traiter les données que Hermes lui transmet.

Dans le cas d'un petit modèle local disposant malgré tout d'un contexte long, comme LFM2.5-2.6B configuré à 128K, celui-ci peut être particulièrement intéressant grâce à son coût réduit et à sa vitesse élevée de traitement du prompt.

Quantification (poids du modèle)

  • Tâches à faible enjeu (title_generation, tts_audio_tags, skills_hub, monitor, triage_specifier, memory_query_rewrite, goal_judge) : Q4_K_M suffit largement.
  • Tâches à enjeu réel (approval, mcp, compression, vision) : privilégier Q5_K_M ou Q6_K — sur des modèles 4-8B, l’écart VRAM entre Q4 et Q6 reste faible (souvent < 1,5 Go), autant prendre la marge.

KV cache (contexte)

  • En dessous de 16K de contexte : pas de quantification ou q8_0/q8_0 en KV cache partout par défaut sur les routes auxiliaires — risque de dégradation négligeable.
  • À partir de 32K (moa_reference, compression) : q8_0/q8_0 devient intéressant, avec un gain notable de mémoire pour un risque faible.
  • moa_aggregator à 64K+ : seule route où une approche plus poussée type q8_0/q6_0 + Hadamard (si le fork le supporte) peut se justifier, vu le volume réel de KV cache à comprimer.

Réglages de la voix

Text-to-Speech (TTS)

# vi ~/.hermes/config.yaml

Configuration par défaut :

...
tts:
  provider: edge
  edge:
    voice: en-US-AriaNeural
...

Parmi les options gratuites et simples proposées par Hermes, Edge constitue probablement le meilleur choix par défaut. Il est toutefois recommandé de sélectionner une voix adaptée à la langue utilisée. Exemple pour le français :

...
tts:
  provider: edge
  edge:
    voice: fr-FR-DeniseNeural
...
TTS via CrispASR

Voir CrispASR

CrispASR expose une API de synthèse vocale compatible avec l’API audio d’OpenAI. Hermes doit donc utiliser le fournisseur openai pour envoyer le texte au serveur TTS.

  • Dans le fichier ~/.hermes/config.yaml :
...
tts:
  provider: openai
...
  openai:
    model: qwen3-tts-12hz-0.6b-base-q8_0
    voice: default
    base_url: http://IP_CRISPASR:7981/v1
    consent_attestation: "I have the speaker's consent"
...

Lorsque CrispASR est lancé avec une voix de référence fixe, la valeur default peut être utilisée pour l’option voice. La voix réellement utilisée dépend alors du fichier WAV et de sa transcription chargés par le serveur.

Le nom du modèle exposé peut être vérifié avec la commande suivante :

curl http://IP_CRISPASR:7980/v1/models
  • Dans le fichier ~/.hermes/.env :
...
VOICE_TOOLS_OPENAI_KEY=local
...

Speech-to-Text (STT)

# vi ~/.hermes/config.yaml

Configuration par défaut :

...
stt:
  enabled: true
  provider: local
  local:
    model: base
    language: 
  openai:
    model: whisper-1
  mistral:
    model: voxtral-mini-latest
  elevenlabs:
    model_id: scribe_v2
    language_code: 
    tag_audio_events: false
    diarize: false
...

Le Speech-to-Text permet de convertir les enregistrements vocaux en texte. Par défaut, Hermes utilise localement faster-whisper avec le modèle base. Cette solution est gratuite, ne nécessite aucune clé API et conserve les enregistrements sur la machine.

Lorsque le champ language reste vide, la langue est détectée automatiquement. Il est toutefois possible de préciser la langue afin de limiter les erreurs de détection, par exemple avec fr pour le français.

Le modèle base offre un bon compromis entre rapidité et consommation de ressources, mais il peut manquer de précision avec les noms propres, les nombres, les adresses IP ou les termes techniques. Il est alors possible d’utiliser un modèle local plus volumineux, par exemple small ou medium. Plus le modèle est grand, plus la transcription est généralement précise, mais plus elle demande de mémoire et de temps de traitement.

Exemple avec un modèle local plus précis configuré pour le français :

...
stt:
enabled: true
provider: local
local:
 model: small
 language: fr
...

Hermes peut également utiliser un service externe de reconnaissance vocale, par exemple OpenAI, Mistral ou ElevenLabs. Dans ce cas, il suffit de sélectionner le fournisseur souhaité avec l’option provider et de renseigner la clé API correspondante dans le fichier ~/.hermes/.env.

STT via CrispASR

Voir CrisASR

CrispASR expose une API compatible avec l’API audio d’OpenAI. Hermes doit donc utiliser le fournisseur openai pour envoyer les fichiers audio au serveur.

  • Dans le fichier ~/.hermes/config.yaml (exemple avec parakeet-tdt-0.6b-v3) :
...
stt:
  enabled: true
  provider: openai
  local:
    model: small
    language: 'fr'
  openai:
    model: parakeet-tdt-0.6b-v3-q8_0
...
  • Dans le fichier ~/.hermes/.env :
...
VOICE_TOOLS_OPENAI_KEY=local
STT_OPENAI_BASE_URL=http://IP_CRISPASR:7980/v1

Après modification, on redémarre Hermes afin que la nouvelle configuration soit prise en compte.

Changer le mode d'approbation des commandes

# vi ~/.hermes/config.yaml
approvals:
  mode: manual

Remplacer manual par smart pour confier l’évaluation des commandes sensibles à un LLM. Les opérations jugées sûres sont automatiquement autorisées, les opérations dangereuses sont refusées et les cas incertains sont soumis à l’utilisateur. Par défaut, Hermes utilise le modèle principal pour cette évaluation, sauf si un modèle auxiliaire spécifique est configuré pour la tâche approval.

Commandes

  • Lancer l’interface interactive :
# hermes
  • Choisir le modèle et le provider LLM :
# hermes model
  • Configurer les outils disponibles :
# hermes tools
  • Modifier une configuration spécifique :
# hermes config set
  • Démarrer le gateway (Telegram, Discord, etc.) :
# hermes gateway
  • Lancer le script de configuration complet :
# hermes setup
  • Migrer depuis OpenClaw :
# hermes claw migrate
  • Mettre à jour Hermes :
# hermes update
  • Diagnostiquer les problèmes :
# hermes doctor

Modules d’extension et d’intégration

Ressource :

Pour accéder a toutes les fonctionnalités sur une connexion distante il est préférable de se connecter via SSH, sur le serveur Hermes Agent :

# ssh-keygen -t ed25519 -f ~/.ssh/hermes_desktop

Ne PAS entrer de mot de passe :

Enter passphrase for "/root/.ssh/hermes_desktop" (empty for no passphrase): 
Enter same passphrase again:

Ensuite, ajouter la clé publique aux clés autorisées du compte auquel Hermes Desktop doit se connecter :

# cat ~/.ssh/hermes_desktop.pub >> ~/.ssh/authorized_keys
# chmod 600 ~/.ssh/authorized_keys

Windows

On lance PowerShell et on récupère la clef :

> scp utilisateur@IP_DU_SERVEUR:~/.ssh/hermes_desktop "$env:USERPROFILE\.ssh\hermes_desktop"

Dans Private Key Path il faudra alors entrer :

C:\Users\NOM_UTILISATEUR\.ssh\hermes_desktop

Sur le serveur Hermes Agent :

# apt update && apt upgrade
# apt install -y git python3 python3-venv
# cd /opt/
# git clone https://github.com/nesquena/hermes-webui.git
# cd hermes-webui
# ./ctl.sh start

On arrête temporairement le serveur :

# ./ctl.sh stop

On crée un service systemd pour qu'il se lance automatiquement :

# vi /etc/systemd/system/hermes-webui.service
[Unit]
Description=Hermes WebUI
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/opt/hermes-webui

Environment=HOME=/root
Environment=HERMES_HOME=/root/.hermes
Environment=HERMES_WEBUI_HOST=127.0.0.1
Environment=HERMES_WEBUI_PORT=8787
EnvironmentFile=-/opt/hermes-webui/.env

ExecStart=/bin/bash /opt/hermes-webui/start.sh --foreground

Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=20

[Install]
WantedBy=multi-user.target

Par défaut l'interface n'est accessible que depuis le serveur, pour autoriser l'écoute sur une IP remplacer HERMES_WEBUI_HOST=127.0.0.1 par 0.0.0.0 ou une interface IP du serveur.

Ensuite on ajoute un mot de passe :

# vi /opt/hermes-webui/.env
HERMES_WEBUI_PASSWORD=MOT_DE_PASSE
# chmod 600 /opt/hermes-webui/.env

(Recommandé) On peut également conserver 127.0.0.1 et passer par un proxy Caddy.

On active le service systemd :

# systemctl daemon-reload
# systemctl enable --now hermes-webui.service

Pour pouvoir utiliser un reverse-proxy il faut ajouter :

# vi /opt/hermes-webui/.env
HERMES_WEBUI_PASSWORD=MOT_DE_PASSE
HERMES_WEBUI_SECURE=1
HERMES_WEBUI_ALLOWED_ORIGINS=https://hermes.exemple.net
HERMES_WEBUI_TRUST_FORWARDED_HOST=1
HERMES_WEBUI_TRUST_FORWARDED_FOR=1
HERMES_WEBUI_TRUST_FORWARDED_PROTO=1
# systemctl restart hermes-webui

Exemple de vhost Nginx :

server {
    listen 80;
    listen [::]:80;
     server_name hermes.exemple.net;

    return 301 https://$host$request_uri;
}

server {
    server_name hermes.exemple.net;

    # protection
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000" always;

    client_max_body_size 256m;

    location / {
        proxy_set_header Host              $proxy_host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;

        proxy_http_version 1.1;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;

        proxy_pass http<font color = red>s</font>://192.168.1.123:8787;

           proxy_ssl_verify off;
           proxy_ssl_server_name on;
           proxy_ssl_name 192.168.1.123;

        proxy_buffering off;
        proxy_cache off;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot

    ssl_certificate /etc/letsencrypt/live/hermes.exemple.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hermes.exemple.net/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
}

Mise à jour

Mettre à jour Hermes Agent :

# hermes update

Mettre à jour Hermes WebUI :

# cd /opt/hermes-webui
# git pull --ff-only
# systemctl restart hermes-webui.service

Garder toutes les parties de réponse ouvertes (extension keep-expanded)

Par défaut, Hermes WebUI replie automatiquement les parties intermédiaires d'une réponse (le texte que l'IA écrit entre deux appels d'outils) dès qu'il y en a plus de 3 : un lien Show N earlier updates apparaît pour les retrouver. Ce comportement est codé en dur (INTERIM_COLLAPSE_THRESHOLD=3 dans static/messages.js) et ne propose aucune option. Les modèles qui produisent beaucoup de petites notes de progression (Qwen par exemple) le rendent vite pénible, car le bloc de code final peut se retrouver dans une partie repliée.

Le WebUI supporte les extensions CSS (voir docs/EXTENSIONS.md dans le dépôt). Voici une petite extension maison qui désactive ce repli.

  • Étape 1 — Créer le fichier CSS :
# mkdir -p ~/.hermes/webui/extensions
# vi ~/.hermes/webui/extensions/keep-expanded.css

Y coller :

/* Extension hermes-webui : garder depliee chaque segment de reponse
   (desactive le repli automatique des notes intermediaires). */
.interim-collapsed { display: revert !important; }
.interim-collapse-toggle { display: none !important; }
  • Étape 2 — Déclarer l'extension au service : le service systemd lit déjà EnvironmentFile=-/opt/hermes-webui/.env (voir le service plus haut), on y ajoute donc deux lignes :
# vi /opt/hermes-webui/.env
HERMES_WEBUI_EXTENSION_DIR=/root/.hermes/webui/extensions
HERMES_WEBUI_EXTENSION_STYLESHEET_URLS=/extensions/keep-expanded.css
  • Étape 3 — Redémarrer et vérifier :
# systemctl restart hermes-webui.service
# systemctl is-active hermes-webui.service

Puis recharger la page du WebUI dans le navigateur (F5) : toutes les parties des réponses restent ouvertes, et le lien Show N earlier updates a disparu.

Retour en arrière : supprimer les deux lignes de /opt/hermes-webui/.env (ou le fichier CSS), puis relancer le service — le repli automatique revient.

Résistance aux mises à jour : cette configuration survit aux mises à jour normales (git pull dans /opt/hermes-webui puis redémarrage) :

  • .env est listé dans le .gitignore du projet : git ne le touche jamais ;
  • le CSS vit dans ~/.hermes/webui/extensions/, hors du dépôt git — c'est le répertoire officiellement recommandé pour les extensions.

Seuls une réinstallation complète (suppression + re-clone) ou un changement de nom des classes interim-collapsed / interim-collapse-toggle côté développeur pourraient casser l'extension. Si le repli revient tout seul après une mise à jour, il suffit de vérifier ces sélecteurs dans le CSS.

Un espace de travail complet : orchestrez vos agents, parcourez la mémoire, gérez les compétences et contrôlez l’ensemble depuis une interface unique.

Docker

# mkdir -p /opt/hermes-workspace/hermes-workspace-data
# chown -R 1000:1000 /opt/hermes-workspace/hermes-workspace-data
# vi /opt/hermes-workspace/docker-compose.yml
services:
  hermes-workspace:
    image: ghcr.io/outsourc-e/hermes-workspace:latest
    container_name: hermes-workspace
    restart: unless-stopped

    environment:
      #HERMES_HOME: /home/workspace/.hermes
      #HERMES_WORKSPACE_DIR: /workspace

      HERMES_API_URL: http://IP_HERMES:8642
      HERMES_DASHBOARD_URL: http://IP_HERMES:9119

      HERMES_API_TOKEN: ""
      HERMES_PASSWORD: "change-moi"

      HERMES_DASHBOARD_BASIC_AUTH_USERNAME: "admin"
      HERMES_DASHBOARD_BASIC_AUTH_PASSWORD: "MOT_DE_PASSE_DASHBOARD"      

      HERMES_UID: 1000
      HERMES_GID: 1000

      COOKIE_SECURE: "0"
      TRUST_PROXY: "0"

    volumes:
      - ./hermes-workspace-data:/home/workspace/.hermes
      - ./workspace:/workspace

    ports:
      - "3000:3000"


# docker compose -f /opt/hermes-workspace/docker-compose.yml --project-directory /opt/hermes-workspace up -d

Exemple de vhost :

server {
    listen 80;
    listen [::]:80;
     server_name hermes.exemple.net;

    return 301 https://$host$request_uri;
}

server {
    server_name hermes.exemple.net;

    # protection
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000" always;

    client_max_body_size 256m;

     location = /api/terminal-stream {
        proxy_pass http://IP_SERVEUR_HERMES:3000;

        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;

        proxy_set_header Connection "";

        proxy_buffering off;
        proxy_cache off;

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    location / {
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;

        proxy_http_version 1.1;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;

        proxy_pass http://IP_SERVEUR_HERMES:3000;

        # proxy_buffering off;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot

    ssl_certificate /etc/letsencrypt/live/hermes.exemple.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hermes.exemple.net/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
}

Fix Temporaire

On construit une version patché du conteneur :

# cd /opt/
# git clone https://github.com/outsourc-e/hermes-workspace.git
# cd hermes-workspace
# git fetch origin pull/758/head:pr-758
# git fetch origin pull/735/head:pr-759
# git checkout -b hermes-fixed origin/main
# git cherry-pick f7a50c5dcad8ba0f88e8d29e6f85c60b07681362
# git cherry-pick 36b7192
# docker build -t hermes-workspace:fixed .

Dans le fichier docker-compose.yml remplacer image: ghcr.io/outsourc-e/hermes-workspace:latest par image: hermes-workspace:fixed.

# docker compose -f /opt/hermes-workspace/docker-compose.yml --project-directory /opt/hermes-workspace up -d

Réglage voix

Pour le moment le plus simple est d'utiliser mon fork.

Catalogue des Skills et serveurs MCP dans le conteneur

Pour le moment le plus simple est d'utiliser mon fork.

Modification de Dockerfile pour référence

Le catalogue des Skills nécessite le script skills-search.py de Hermes Workspace ainsi que certains modules de Hermes Agent.

Pour les intégrer directement à l'image Docker :

# cd /opt/hermes-workspace
# vi Dockerfile

Ajouter les éléments suivant en bleu :

...
RUN apt-get update && apt-get install -y --no-install-recommends \
      ca-certificates curl tini python3 python3-httpx python3-yaml \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd -r workspace && useradd -r -g workspace -u 10010 -m workspace

COPY scripts/skills-search.py /app/scripts/skills-search.py

RUN curl -L \
      https://codeload.github.com/NousResearch/hermes-agent/tar.gz/refs/heads/main \
      -o /tmp/hermes-agent.tar.gz \
    && mkdir -p /hermes-agent \
    && tar -xzf /tmp/hermes-agent.tar.gz -C /hermes-agent --strip-components=1 \
    && rm /tmp/hermes-agent.tar.gz

ENV PYTHONPATH=/hermes-agent
...

Puis plus bas :

...
COPY --from=build --chown=workspace:workspace /app/dist ./dist
COPY --from=build --chown=workspace:workspace /app/node_modules ./node_modules
COPY --from=build --chown=workspace:workspace /app/package.json ./package.json
COPY --from=build --chown=workspace:workspace /app/server-entry.js ./server-entry.js
COPY --from=build --chown=workspace:workspace /app/skills ./skills
COPY --chown=workspace:workspace docker/entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY --chown=workspace:workspace assets/mcp-presets.seed.json ./assets/mcp-presets.seed.json
...

Reconstruire ensuite l'image et recréer le conteneur :

# docker build -t hermes-workspace:fixed .
# docker compose up -d --force-recreate

On peut vérifier le fonctionnement avec :

# docker exec hermes-workspace python3 -c \
  'import httpx, yaml, tools.skills_hub; print("Skills Hub OK")'

Le résultat attendu est :

Skills Hub OK

Les opérations se déroulent directement sur l'hôte Hermes.

La configuration présentée ici utilise DonSeTch avec SearXNG et Firecrawl comme services locaux. Il faut d’abord avoir installé une instance SearXNG et installé une instance Firecrawl.

  • On installe et active le plugin :
# hermes plugins install web-search-plus --enable

Répondre y si l’installation des dépendances Node est proposée :

Install them into the plugin's own node_modules now? [y/N]: y

Répondre yes si une autorisation est demandée :

This is a privileged capability: an override can intercept everything the agent routes
through that tool.
Grant it?

On initialise le plugin avec le preset self-hosted :

# hermes web-search-plus setup --preset self-hosted

L’assistant propose ensuite d’activer Jev :

Enable optional Jev (TypeSafe) for search-type overlay, extract quality, and language fill? [y/N]:

Jev est un service extérieur optionnel utilisé pour améliorer la détection du type de recherche, l’évaluation des extractions et la détection de la langue. Il n’est pas nécessaire pour une configuration locale avec SearXNG, DonSeTch et Firecrawl. Répondre :

n

On renseigne l’adresse de l’instance SearXNG :

# hermes config set SEARXNG_INSTANCE_URL http://IP_PRIVEE_SEARXNG:PORT

Si le service SearXNG est accessible via une adresse IP privée, il faut l’autoriser explicitement :

# hermes config set SEARXNG_ALLOW_PRIVATE 1

Le preset self-hosted peut utiliser Keenable comme service de secours extérieur sans clé API. Pour l’autoriser :

# hermes config set KEENABLE_ALLOW_PUBLIC 1

Cette commande doit être omise pour conserver une configuration strictement locale.

Pour que Web Search Plus considère Firecrawl comme configuré, une valeur doit être renseignée dans FIRECRAWL_API_KEY. Si l’instance locale n’utilise aucune authentification, employer une valeur factice :

# hermes config set FIRECRAWL_API_KEY local-firecrawl
  • On installe DonSeTch :
# npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch

Pour s'assurer que le service Hermes puisse trouver Node :

# test -e /usr/local/bin/node || ln -s /root/.local/bin/node /usr/local/bin/node

On installe Chromium et Xvfb pour les fonctions nécessitant un navigateur :

# apt update
# apt install chromium xvfb

On vérifie le fonctionnement de DonSeTch :

# /root/.hermes/node/bin/donsetch --version
# /root/.hermes/node/bin/donsetch doctor

On enregistre le chemin du binaire dans le profil Hermes :

# hermes config set DONSETCH_BIN /root/.hermes/node/bin/donsetch

Le preset self-hosted limite le routage automatique à SearXNG et Keenable. Pour utiliser DonSeTch en priorité pour la recherche et Firecrawl pour l’extraction, on réinitialise la configuration avec le profil standard :

# python3 ~/.hermes/plugins/web-search-plus/setup.py config reset --yes
# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-routing on

On désactive les fournisseurs inutilisés :

# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable brave
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable exa
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable linkup
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable octen
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable parallel
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable querit
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable serpbase
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable serper
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable tavily
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable tinyfish
# python3 ~/.hermes/plugins/web-search-plus/setup.py config disable you

On définit les priorités de recherche et d’extraction :

# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-priority donsetch,searxng
# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-extract-priority donsetch,firecrawl

On autorise l’utilisation automatique de DonSeTch :

# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-auto-allow donsetch on

Si Keenable a été activé, on le définit comme fournisseur de secours :

# python3 ~/.hermes/plugins/web-search-plus/setup.py config set-fallback keenable

Cette commande doit également être omise pour conserver une configuration strictement locale.

Le preset self-hosted ajoute l’autorisation d’utiliser le service public Keenable dans le fichier de routage du plugin. Ce réglage étant ignoré sous Hermes, il faut supprimer uniquement le bloc correspondant dans ce fichier.

On édite le fichier de routage :

# vi ~/.hermes/plugins/config.json

On supprime uniquement le bloc suivant :

   "keenable": {
       "allow_public": true
   },

Il ne faut pas supprimer les autres occurrences de keenable, notamment celles qui le définissent comme fournisseur de secours.

On ajoute ou modifie ensuite l’objet firecrawl situé à la racine du fichier :

  "firecrawl": {
    "api_url": "http://IP_FIRECRAWL:3002/v2/search",
    "scrape_url": "http://IP_FIRECRAWL:3002/v2/scrape"
  },

L’option suivante n’est nécessaire que si web_extract_plus doit être autorisé à extraire des pages situées sur un réseau privé ou un intranet :

  "extract": {
    "allow_private_urls": true
  },

Les objets doivent être séparés par une virgule afin de conserver un fichier JSON valide. Les valeurs booléennes JSON s’écrivent en minuscules : true et false.

Hermes peut réessayer automatiquement les appels Web qui échouent auprès de services externes anonymes comme Exa, Parallel, Firecrawl ou Keenable. Web Search Plus disposant déjà de son propre fournisseur de secours, on désactive ce second mécanisme :

# hermes config set web.keyless_rescue false

On vérifie la syntaxe du fichier de routage :

# python3 -m json.tool ~/.hermes/plugins/config.json >/dev/null && echo "JSON valide"

On vérifie la configuration finale :

# hermes web-search-plus status
# python3 ~/.hermes/plugins/web-search-plus/setup.py config show --json

Provider Web pour les outils natifs Hermes

Web Search Plus peut servir directement de backend aux outils natifs web_search et web_extract de Hermes.

On active Web Search Plus pour la recherche et l’extraction :

# hermes config set web.search_backend wsp
# hermes config set web.extract_backend wsp

Le backend wsp ne doit être sélectionné pour l’extraction que si au moins un fournisseur compatible est disponible, comme DonSeTch ou Firecrawl.

Les outils natifs nécessitent que le toolset web reste activé. Si celui-ci figure dans agent.disabled_toolsets, il faut le retirer.

On vérifie l’intégration :

# hermes web-search-plus fastpath
# hermes web-search-plus status

Enfin, on redémarre Hermes :

# systemctl restart hermes-gateway.service

Test

On teste directement le fournisseur Firecrawl :

# cd ~/.hermes/plugins/web-search-plus
# FIRECRAWL_API_KEY=local-firecrawl python3 search.py --query "Hermes Agent latest release" --provider firecrawl --quality-report

On lance ensuite Hermes :

# hermes

On lui demande d’effectuer une recherche avec Web Search Plus :

Recherche la dernière version de Hermes Agent en utilisant web_search_plus et indique le fournisseur utilisé.

On peut également demander un test explicite de DonSeTch :

Recherche la documentation de Hermes Agent avec web_search_plus en imposant le fournisseur donsetch.

Mise à jour

  • Hermes Web Search Plus
# hermes plugins update web-search-plus

Si l’installation des dépendances Node est de nouveau proposée, répondre y.

Le scanner de sécurité de Hermes peut désactiver le plugin après une mise à jour. Si nécessaire, le réactiver :

# hermes plugins enable web-search-plus

Puis redémarrer Hermes :

# systemctl restart hermes-gateway.service
  • DonSeTch

On met à jour DonSeTch vers la dernière version disponible :

# npm install -g --allow-scripts=donsetch,@google/genai,protobufjs donsetch@latest

On vérifie ensuite la version et le bon fonctionnement de DonSeTch :

# /root/.hermes/node/bin/donsetch --version
# /root/.hermes/node/bin/donsetch doctor

Sources :

Mnemosyne est une couche de mémoire universelle, conçue en priorité pour Hermes, mais compatible avec n’importe quel framework d’agents.

  • On installe le plugin :
# source ~/.hermes/hermes-agent/venv/bin/activate
# python -m ensurepip --upgrade
# python -m pip install --upgrade pip
# python -m pip install mnemosyne-hermes
# mkdir -p ~/.hermes/plugins/mnemosyne
# ln -sfn "$(~/.hermes/hermes-agent/venv/bin/python -c 'import pathlib, mnemosyne_hermes; print(pathlib.Path(mnemosyne_hermes.__file__).resolve().parent)')"/* ~/.hermes/plugins/mnemosyne/
# hermes config set memory.provider mnemosyne

On édite ensuite la configuration de Hermes :

# vi ~/.hermes/config.yaml
...
memory:
  memory_enabled: false
  user_profile_enabled: false
...

Ces deux options désactivent la mémoire native de Hermes (MEMORY.md et USER.md) afin d'utiliser Mnemosyne comme fournisseur de mémoire.

Embedding

Mnemosyne-Hermes est livré avec le support des embeddings locaux. Un modèle d'embedding est nécessaire au fonctionnement de la mémoire vectorielle ; il peut être exécuté localement ou fourni par un serveur externe.

Modèle local

Le modèle utilisé par défaut, BAAI/bge-small-en-v1.5, est principalement destiné à l'anglais. Pour un usage multilingue, on peut par exemple utiliser le modèle léger sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 :

# mnemosyne config set embedding_model sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
# mnemosyne config set embedding_dim 384
# mnemosyne config set embedding_api_url ""
# mnemosyne config set embedding_api_key ""
# mnemosyne config set embeddings_via_api false

Le modèle local est téléchargé automatiquement lors de sa première utilisation.

Voir la liste des modèles d'embedding disponibles.

Serveur d'embedding externe

Mnemosyne peut également utiliser un serveur d'embedding compatible avec l'API OpenAI :

# mnemosyne config set embedding_api_url "http://ADRESSE_SERVEUR:PORT/v1"
# mnemosyne config set embedding_api_key "CLEF_API"
# mnemosyne config set embedding_model "NOM_EXACT_DU_MODELE"
# mnemosyne config set embedding_dim DIMENSION_DU_MODELE
# mnemosyne config set embeddings_via_api true

La valeur de embedding_dim doit impérativement correspondre à la dimension produite par le modèle d'embedding utilisé.

Attention : changer de modèle d'embedding ou de dimension sur une base Mnemosyne contenant déjà des données peut nécessiter une reconstruction des embeddings existants.

Changer de modèle d'embedding ou de dimension

Pour changer de modèle d'embedding en toute sécurité, il faut d'abord enregistrer le nouveau modèle et, si nécessaire, sa dimension dans la configuration utilisée par Hermes après redémarrage.

On arrête ensuite Hermes ainsi que tout autre processus susceptible d'écrire dans la même base SQLite :

# systemctl stop hermes-gateway.service

On vérifie alors le plan de réindexation sans modifier la base :

# mnemosyne reindex --model NOM_EXACT_DU_MODELE --dry-run

La commande doit afficher le modèle et la dimension attendus. Il ne faut pas poursuivre si ces valeurs ne correspondent pas à la configuration qui sera utilisée après le redémarrage.

Une fois le résultat vérifié, on lance la réindexation :

# mnemosyne reindex --model NOM_EXACT_DU_MODELE --yes

Par défaut, Mnemosyne crée une sauvegarde, recalcule les embeddings des mémoires de travail et épisodiques et, lorsque sqlite-vec est disponible, reconstruit les tables vectorielles avec la dimension définie par la configuration effective.

Attention : l'option --model ne remplace pas une valeur explicite de embedding_dim. Si une dimension est configurée, elle doit correspondre à la dimension réellement produite par le nouveau modèle.

On redémarre ensuite Hermes :

# systemctl start hermes-gateway.service

LLM optionnel

Mnemosyne peut utiliser un LLM en complément du modèle d'embedding.

Ce LLM n'est pas nécessaire au fonctionnement de base de la mémoire. Lorsqu'il est activé, il est notamment utilisé pour :

  • la consolidation et la synthèse des souvenirs lors des opérations de sleep ;
  • l'extraction structurée de faits ;
  • certaines opérations avancées basées sur un LLM.

En l'absence de LLM disponible, Mnemosyne peut continuer à fonctionner en utilisant ses mécanismes de traitement non-LLM.

Aucun LLM

Par défaut dans la configuration déclarée de Mnemosyne, le LLM est désactivé.

Pour forcer explicitement ce fonctionnement :

# mnemosyne config set llm_enabled false
# mnemosyne config set host_llm_enabled false
# mnemosyne config set llm_base_url ""
# mnemosyne config set llm_api_key ""
# mnemosyne config set llm_model ""

Mnemosyne fonctionne alors sans LLM pour les opérations de consolidation et d'extraction.

Utiliser le LLM local

Mnemosyne possède une configuration par défaut pour un petit LLM local GGUF utilisé pour les opérations de consolidation, de synthèse et d'extraction de faits.

Le backend local n'est toutefois disponible que si ses dépendances optionnelles sont installées :

# source ~/.hermes/hermes-agent/venv/bin/activate
# python -m pip install -U 'mnemosyne-memory[all]'

On active ensuite le support LLM de Mnemosyne et on désactive l'utilisation du LLM fourni par Hermes :

# mnemosyne config set llm_enabled true
# mnemosyne config set host_llm_enabled false

Pour forcer l'utilisation du modèle local plutôt qu'un éventuel serveur LLM distant :

# mnemosyne config set force_local true

Par défaut, Mnemosyne utilise :

* Dépôt : openbmb/MiniCPM5-1B-GGUF
* Modèle : MiniCPM5-1B-Q4_K_M.gguf
* Contexte : 2048 tokens
* Sortie maximale : 2048 tokens
* Threads CPU : 4
* Cache : ~/.hermes/mnemosyne/models

On peut enregistrer explicitement ces valeurs dans la configuration :

# mnemosyne config set llm_repo "openbmb/MiniCPM5-1B-GGUF"
# mnemosyne config set llm_file "MiniCPM5-1B-Q4_K_M.gguf"
# mnemosyne config set llm_n_ctx 2048
# mnemosyne config set llm_max_tokens 2048
# mnemosyne config set llm_n_threads 4
# mnemosyne config set model_cache_dir "~/.hermes/mnemosyne/models"

Le modèle GGUF est téléchargé dans le cache lors de sa première utilisation.

Les valeurs par défaut correspondantes sont :

MNEMOSYNE_LLM_ENABLED=true
MNEMOSYNE_LLM_N_CTX=2048
MNEMOSYNE_LLM_MAX_TOKENS=2048
MNEMOSYNE_LLM_N_THREADS=4
MNEMOSYNE_LLM_REPO=openbmb/MiniCPM5-1B-GGUF
MNEMOSYNE_LLM_FILE=MiniCPM5-1B-Q4_K_M.gguf
MNEMOSYNE_MODEL_CACHE_DIR=~/.hermes/mnemosyne/models

Utiliser le modèle auxiliaire de Hermes

Mnemosyne peut déléguer ses opérations LLM au client auxiliaire de Hermes.

On active d'abord les opérations LLM de Mnemosyne ainsi que le backend fourni par Hermes :

# mnemosyne config set llm_enabled true
# mnemosyne config set host_llm_enabled true

Pour utiliser directement la configuration auxiliaire de Hermes, on laisse les éventuelles surcharges Mnemosyne vides :

# mnemosyne config set host_llm_provider ""
# mnemosyne config set host_llm_model ""

Pour utiliser le LLM auxiliaire de Hermes, le backend hôte doit également être activé dans l'environnement du service Hermes :

# systemctl edit hermes-gateway.service
[Service]
Environment="MNEMOSYNE_HOST_LLM_ENABLED=true"
Environment="MNEMOSYNE_LLM_ENABLED=true"

Puis :

# systemctl restart hermes-gateway.service

Mnemosyne utilise alors la configuration auxiliary.compression de Hermes. Si aucun modèle auxiliaire particulier n'est configuré dans Hermes, la configuration par défaut fait utiliser à Hermes son modèle principal pour cette tâche.

Utiliser un LLM via une API

Mnemosyne peut également utiliser directement un serveur compatible avec l'API OpenAI.

On désactive dans ce cas le backend LLM fourni par Hermes :

# mnemosyne config set host_llm_enabled false

Puis on active le LLM et on configure l'API :

# mnemosyne config set llm_enabled true
# mnemosyne config set llm_base_url "http://ADRESSE_SERVEUR:PORT/v1"
# mnemosyne config set llm_api_key "CLEF_API"
# mnemosyne config set llm_model "NOM_EXACT_DU_MODELE"

Cette API peut être fournie par exemple par llama.cpp, vLLM, Ollama, LM Studio ou tout autre serveur proposant une interface compatible avec l'API OpenAI.

Si le serveur n'exige pas de clé API, llm_api_key peut rester vide :

# mnemosyne config set llm_api_key ""

Fin de la configuration

Après modification de la configuration, on redémarre Hermes :

# systemctl restart hermes-gateway.service

Mise à jour

# source ~/.hermes/hermes-agent/venv/bin/activate
# python -m pip install --upgrade mnemosyne-hermes
# systemctl restart hermes-gateway.service

Hermes-LCM est un plugin de gestion de contexte « lossless » pour Hermes Agent. Lorsqu’il est utilisé comme moteur de contexte, il remplace la compression ponctuelle du contexte actif par un système persistant basé sur SQLite et une structure hiérarchique en DAG.

On installe les fichiers du projet :

# git clone https://github.com/stephenschoettler/hermes-lcm \
  ~/.hermes/plugins/hermes-lcm

Puis on exécute le script d'installation afin d'exposer également le skill Hermes-LCM :

# cd ~/.hermes/plugins/hermes-lcm
# ./scripts/install.sh

On active la configuration de Hermes pour activer le plugin :

# vi ~/.hermes/config.yaml

Dans la sction plugins: ajouter :

...
plugins:
  enabled:
    - hermes-lcm
...

Puis rempalcer :

...
context:
  engine: compressor
...

Par :

...
context:
  engine: lcm
...

Enfin, on redémarre Hermes :

# systemctl restart hermes-gateway.service

Mise à jour

# cd ~/.hermes/plugins/hermes-lcm && git pull --ff-only

MCP

MCP pour remplacer les outils web natifs de Hermes

Hermes utilise Tool Search pour éviter d'envoyer au modèle la description complète de tous les outils disponibles à chaque requête.

Lorsque Tool Search est activé, Hermes conserve directement dans le contexte les outils principaux, tandis qu'une partie des outils supplémentaires — notamment ceux provenant des serveurs MCP et de certains plugins — est placée dans une liste d'outils différés.

Le modèle doit alors retrouver l'outil dont il a besoin à l'aide de mécanismes comme :

tool_search
tool_describe
tool_call

Cela permet de réduire la taille du prompt lorsque des dizaines ou des centaines d'outils sont disponibles.


Dans :

# vi ~/.hermes/config.yaml

Ajouter ou modifier :

tools:
  tool_search:
    enabled: off

Puis redémarrer Hermes :

# systemctl restart hermes-gateway.service
# vi ~/.hermes/config.yaml

On inscrit le serveur MCP SearXNG (créer la section mcp_servers si absente) :

...
mcp_servers:
  searxng:
    url: http://IP_MCP:3000/mcp
...

On redémarre le service :

# systemctl restart hermes-gateway.service

Ou depuis Hermes :

/reload-mcp

Test :

# hermes mcp list
# hermes mcp test searxng

Le serveur ihor-sokoliuk/mcp-searxng expose notamment les outils suivants :

searxng_web_search
searxng_search_suggestions
searxng_instance_info
web_url_read
...
  TOOLS:
  - For current, recent, upcoming, time-sensitive, location-specific, or uncertain factual questions, use searxng_web_search.
  - To read a specific URL, use web_url_read.
  - For search suggestions, use searxng_search_suggestions.
  - For SearXNG instance capabilities, categories, engines, locales, and plugins, use searxng_instance_info.
  - For system tasks, use terminal.

  RULES:
  - Respond in the same language as the user.
  - Do not invent tool results.
  - If a tool fails, say so explicitly.
  - Do not promise to continue later, follow up later, or say that results are still coming.
  - When the user asks multiple questions, use the tools needed, then answer all requested items in one final structured response.
  - Never answer current, recent, upcoming, time-sensitive, location-specific, or uncertain factual questions without using the required tool first.
  - This includes weather, news, prices, schedules, releases, availability, rankings, versions, and other facts that may have changed.
  - If no tool result is available, say you cannot verify and do not guess.
  - Use search categories when relevant.
  - For image searches, use searxng_web_search with categories=images.
  - For video searches, use searxng_web_search with categories=videos.
  - For image search results, use the `img_src` field as the direct image URL.
  - When displaying an image result, ALWAYS output the direct image URL exactly in this format: ![](URL)
  - Do not output the image URL as a normal Markdown link.
  - For video results, output links in this format: 🎬 [Titre de la vidéo](URL)
  - Do not use tools for simple rewriting, translation, or opinion unless needed.

  • Recommandation :

Il est recommandé de désactiver le service natif correspondant :

# hermes tools

Décocher pour les environnements concernés :

🔍 Web Search & Scraping  (web_search, web_extract)  [no API key]

Skill pour aider la recherche et l'affichage d'images

Dans SOUL.md on remplace :

- For image searches, use searxng_web_search with categories=images.
- For image search results, use the `img_src` field as the direct image URL.
- When displaying an image result, ALWAYS output the direct image URL exactly in this format: ![](URL)
- Do not output the image URL as a normal Markdown link.

Par :

- For any image or photo search, ALWAYS load and follow the `searxng-photo` skill before using any search, browser, or web tool.

Puis on crée le skill :

# mkdir -p ~/.hermes/skills/searxng-photo
# vi ~/.hermes/skills/searxng-photo/SKILL.md
---
name: searxng-photo
description: Find and display an image using the SearXNG MCP image search tool.
---

Use this skill when asked to find, show, or search for an image or photo.

Steps:

1. Call `mcp__searxng__searxng_web_search` with:
   - `query`: a concise image search query
   - `categories`: `"images"`
   - `num_results`: `5`
   - `response_format`: `"json"`

2. Select a relevant result containing `img_src`.

3. For each selected image result, output its direct `img_src` URL using this exact Markdown format:

![](DIRECT_IMAGE_URL)

Replace `DIRECT_IMAGE_URL` with the exact value of the result's `img_src` field.

Rules:

- `categories` must be the string `"images"`, not an array.
- Use `num_results`, not `max_results`.
- Use the exact returned `img_src`.
- Once a usable `img_src` is found, do not call another tool.
- If no usable `img_src` is found, retry once with a better query.
- Do not output the image URL as a normal link.

Enfin on redémarre Hermes :

# systemctl restart hermes-gateway.service

On inscrit le serveur MCP CamoFox (créer la section mcp_servers si absente) :

...
mcp_servers:
  camofox:
    url: http://IP_MCP:3000/mcp
    headers:
     Authorization: Bearer UNE_CLE_ALEATOIRE_DE_32_CARACTERES_MINIMUM
...

On redémarre le service :

# systemctl restart hermes-gateway.service

Ou depuis Hermes :

/reload-mcp

Test :

# hermes mcp list
# hermes mcp test camofox
...
TOOLS:
- For browser navigation, clicking, typing, page interaction, snapshots, screenshots, or rendered page inspection, use the Camofox MCP tools.
- Do not use Camofox web_search for general web searches; use searxng_web_search instead.
...
RULES:
- Do not use memory for ordinary factual lookups, web searches, image searches, or one-off questions unless the user explicitly asks to remember or recall something.
- Do not search local files for ordinary factual or web questions unless the user explicitly asks about local files or documents.

  • Recommandation :

Il est recommandé de désactiver le service natif correspondant :

# hermes tools

Décocher pour les environnements concernés :

[ ] 🌐 Browser Automation  (navigate, click, type, scroll)

Désactiver les outils web natifs de Hermes

Afin d'être sûr qu'Hermes n'expose plus ses outils web natifs aux modèles, il faut modifier la section platform_toolsets :

# vi ~/.hermes/config.yaml

On supprime les occurrences de web et browser dans les sections souhaitées.

Il faut également éviter les toolsets composites susceptibles de les réintroduire, comme hermes_cli, et définir explicitement les outils souhaités.

Pour les requêtes effectuées via l'API Hermes, notamment depuis OpenWebUI, on crée également une section api_server.

Exemple :

...
platform_toolsets:
  cli:
    - clarify
    - code_execution
    - computer_use
    - cronjob
    - delegation
    - file
    - image_gen
    - mcp
    - memory
    - messaging
    - session_search
    - skills
    - terminal
    - todo
    - tts
    - vision

  telegram:
    - hermes-telegram

  discord:
    - clarify
    - code_execution
    - computer_use
    - cronjob
    - delegation
    - file
    - image_gen
    - mcp
    - memory
    - session_search
    - skills
    - terminal
    - todo
    - tts
    - vision

  api_server:
    - clarify
    - code_execution
    - computer_use
    - context_engine
    - cronjob
    - delegation
    - discord
    - discord_admin
    - file
    - homeassistant
    - image_gen
    - mcp
    - memory
    - session_search
    - skills
    - spotify
    - stt
    - terminal
    - todo
    - tts
    - video
    - video_gen
    - vision
    - x_search
    - yuanbao
...

Enfin on redémarre Hermes :

# systemctl restart hermes-gateway.service
# vi ~/.hermes/config.yaml

On inscrit le serveur MCP RAGFlow (créer la section mcp_servers si absente) :

...
mcp_servers:
  ragflow:
    url: http://IP_MCP:9382/mcp
...

On redémarre le service :

# systemctl restart hermes-gateway.service

Ou depuis Hermes :

/reload-mcp

Test :

# hermes mcp list
# hermes mcp test ragflow

Prompt système recommandé :

 TOOLS:
 - For indexed documents, internal documentation, knowledge bases, or when the user asks to check the docs, use the RAGFlow MCP tools: ragflow_retrieval, ragflow_list_datasets, and ragflow_list_chats.

 RULES:
 - Do not use terminal/curl to query RAGFlow unless explicitly asked for a network diagnostic.
 - If no RAGFlow dataset exists or no relevant result is found, say so clearly.

Rerank

Il faut d’abord identifier l’identifiant complet du modèle de reranking.

Exemple de reranker configuré dans RAGFlow.

Dans cet exemple, l’identifiant est :

/opt/models/jina-reranker-v3/Q8_0/jina-reranker-v3-Q8_0.gguf@Serveur Rerank@OpenAI-API-Compatible

Il faut ensuite créer un skill Hermes afin de demander l’utilisation systématique du reranker lors des appels à ragflow_retrieval.

  • Créer le dossier du skill :
# mkdir -p ~/.hermes/skills/ragflow-search
  • Créer le fichier principal du skill :
# vi ~/.hermes/skills/ragflow-search/SKILL.md

Y placer le contenu suivant :

---
name: ragflow-search
description: Use this skill whenever querying RAGFlow datasets through the `ragflow_retrieval` MCP tool. Always include the configured reranker.
version: 1.0.0
---

# RAGFlow Search

Use this skill for every call to the `ragflow_retrieval` MCP tool.

## Tool

Call the MCP tool:

`ragflow_retrieval`

Do not use a generic web search when the requested information is expected to be present in RAGFlow.

## Mandatory reranker

For every call to `ragflow_retrieval`, always include this exact argument:

```yaml
rerank_id: "/opt/models/jina-reranker-v3/Q8_0/jina-reranker-v3-Q8_0.gguf@Serveur Rerank@OpenAI-API-Compatible"
```

Do not omit, shorten, rename, translate, or modify this value.

## Dataset selection

- Use `ragflow_list_datasets` when the relevant dataset ID is unknown.
- Prefer selecting only the datasets relevant to the question.
- Do not invent dataset IDs.
- Omit `dataset_ids` only when searching across all datasets is appropriate.

## Recommended retrieval parameters

Use these defaults unless the task requires otherwise:

```yaml
page: 1
page_size: 10
top_k: 30
similarity_threshold: 0.2
vector_similarity_weight: 0.3
keyword: false
```

## Verification

Do not claim that reranking was used unless `rerank_id` was actually included in the tool call.

If `ragflow_retrieval` is called without `rerank_id`, treat the call as invalid and retry it with the exact configured value.

Une fois le fichier enregistré, démarrer une nouvelle session Hermes afin que le skill soit détecté.

Sommaire