Hermes Agent sur Mac : setup local-first avec deux profils, A (local) et B (cloud)

Read in English

Pour qui ? Utilisateurs macOS Apple Silicon qui veulent un assistant IA local-first souverain pour leurs données sensibles, ET un mode cloud haute performance pour les tâches qui le justifient. Public technique à l’aise avec le terminal, qui n’a pas peur d’éditer un config.yaml.

TL;DR

Mode A (local)Mode B (cloud)
Modèlegpt-oss:20b (Ollama, daily driver) ; override gemma4:26bMiniMax-M3 (minimax-oauth) plus une chaîne de fallback
RéseauAucun. Strictement local.Cloud (Claude, Gemini, Nous, OpenRouter)
Données sensiblesOuiJamais
ToolsetLéger (6 outils, ~83 KB prompt)Complet
Performance~40 s première réponse (gpt-oss), ~80 s (gemma4:26b)<5 s (cloud)
Basculeops/hermes-mode.sh aops/hermes-mode.sh b

Trois principes du setup :

  1. Code dans ~/DEV/hermes-agent (versionné Git), secrets dans ~/.hermes (hors repo)
  2. Deux profils Hermes natifs : default (local) et cloud, chacun avec son HERMES_HOME isolé
  3. Un répertoire de travail dédié (~/DEV/hermes-work/) avec son propre AGENTS.md, pas lancé depuis le repo

Partie 1 : installation

Pourquoi deux profils et pas un seul ?

Avant de me lancer, j’ai hésité : un seul profil avec hermes chat -m <model> pour switcher de modèle à la volée, c’est tentant. En creusant, j’ai compris que les profils Hermes ne servent pas qu’à choisir un modèle. Chaque profil est un HERMES_HOME complètement isolé :

  • config.yaml, .env, mémoire, sessions, sandbox, skills : tout est dupliqué
  • l’AGENTS.md du cwd est lu séparément
  • les outils peuvent diverger entre profils

Conséquences concrètes :

  • Isolation des secrets : si une injection de prompt lit le .env en Mode A, elle ne trouve aucun token Claude/Gemini/OpenRouter
  • Posture par défaut safe : Mode A est l’actif au démarrage, je dois basculer explicitement pour sortir
  • Isolation des sandbox : mon travail cloud ne pollue pas mes fichiers locaux
  • Toolsets divergents : Mode A peut être allégé pour la perf sans toucher au Mode B

C’est documenté côté Nous Research, et c’est un choix de design : « profiles are independent islands on purpose » (cf. l’AGENTS.md du repo, ligne 138).

Pourquoi code et secrets séparés ?

~/DEV/hermes-agent est un repo Git (celui de Nous Research, forkable). Si j’y mets mes clés API, git status les affiche et un git add maladroit les commit. Solution : tout ce qui est secret ou partagé vit dans ~/.hermes/, jamais dans le repo. C’est la convention que suit le bootstrap officiel.

Pré-requis

  • macOS Apple Silicon (testé sur M2 Max)
  • Docker Desktop lancé (icône active dans la barre de menus) : le backend d’exécution sandbox
  • Ollama installé (brew install ollama)
  • un terminal zsh
docker info        # doit afficher les server info, pas une erreur de connexion
ollama --version

Build et install

# Build tools
curl -LsSf https://astral.sh/uv/install.sh | sh
brew install node
exec $SHELL -l

# Clone (submodules = plugins/skills tiers)
mkdir -p ~/DEV
git clone --recurse-submodules https://github.com/NousResearch/hermes-agent.git ~/DEV/hermes-agent
cd ~/DEV/hermes-agent

# venv + editable install (Python 3.11 pinned)
uv venv venv --python 3.11
export VIRTUAL_ENV="$(pwd)/venv"
uv pip install -e ".[all,dev]"

# Navigateur local (utilisé par certains outils en Mode A)
npm install
npx playwright install chromium

# Expose le launcher venv (PAS le wrapper du repo)
mkdir -p ~/.local/bin
ln -sf "$(pwd)/venv/bin/hermes" ~/.local/bin/hermes
grep -q 'HOME/.local/bin' ~/.zshrc || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
exec $SHELL -l

hermes version      # vérifie l'install

Configuration initiale

hermes setup        # wizard interactif
hermes doctor       # health check complet

Clés et identités : où vit quoi

Mode A, ~/.hermes/.env (la posture souveraine réelle, gardée clean) :

TAVILY_API_KEY=tvly-xxxx        # recherche web
FIRECRAWL_API_KEY=fc-xxxx       # extraction de page
GITHUB_TOKEN=ghp_xxxx           # rate limit Skills Hub (optionnel)
TELEGRAM_BOT_TOKEN=...          # OK : canal chat, pas auth modèle
TELEGRAM_ALLOWED_USERS=...
TELEGRAM_HOME_CHANNEL=...
TERMINAL_ENV=docker
# Volontairement absent : OPENROUTER_API_KEY, EXA_API_KEY, GOOGLE_API_KEY,
# ANTHROPIC_TOKEN. Toutes les clés cloud vivent dans profiles/cloud/.env.

Mode B, ~/.hermes/profiles/cloud/.env (et/ou via login OAuth) :

ProviderAuthCommande
MiniMax-M3 (primary actuel)OAuth (minimax-oauth)hermes -p cloud model
Claude (Anthropic)OAuth (Claude Pro)hermes -p cloud model puis Anthropic puis login navigateur
Google Geminiclé API (GEMINI_API_KEY)Choix volontaire : Google déconseille l’OAuth Gemini dans un agent tiers
Nous PortalOAuthhermes login
OpenRouterclé APIFilet de secours large

Note ToS : utiliser un abonnement Claude Pro via OAuth dans un agent tiers est une zone grise. Ça fonctionne, mais la voie officielle reste la clé API Anthropic (comme pour Google Gemini).

Backend d’exécution : Docker

hermes setup tools    # Execution / Terminal backend puis docker
hermes config set terminal.backend docker    # verify

L’image par défaut est nikolaik/python-nodejs:python3.11-nodejs20. Les sandbox persistent leurs fichiers dans ~/.hermes/sandboxes/<profile>/.

Modèles : choisir pour chaque usage

Mode A : deux modèles pour deux usages

ModèleTailleTemps 1re réponseUsage
gpt-oss:20b13 GB~40 sDaily driver : Excel, CSV, transformations, debug
gemma4:26b-a4b-it-q4_K_M17 GB~80 sVeille sensible, raisonnement long

Pourquoi deux modèles ? Au début je tournais uniquement sur le 26B Gemma. Problème : sur un M2 Max, un forward pass à travers 26B de paramètres sature le compute avant que la taille du prompt ne compte. Mesures : toolset complet vs toolset léger = 1m vs 1m20s, dans la marge d’erreur. Le vrai levier de vitesse locale, c’est la taille du modèle.

Setup :

# Vérifier ce qui est installé
ollama list

# Puller ce qui manque (gpt-oss:20b fait 13 GB, gemma4:26b fait 17 GB)
ollama pull gpt-oss:20b
ollama pull gemma4:26b-a4b-it-q4_K_M

# Mettre gpt-oss:20b comme primary du Mode A
hermes -p default model    # picker interactif

# Override ponctuel vers le 26B (veille sensible) :
hermes -p default chat -m gemma4:26b-a4b-it-q4_K_M

Piège context window : Hermes Agent exige au moins 64K tokens de contexte. Plusieurs modèles 7B (comme qwen2.5-coder:7b) plafonnent à 32K et sont refusés à l’init. Toujours vérifier avant d’adopter un modèle : ollama show <model> ou ollama run <model> /show info.

Mode B : primary plus chaîne de fallback

Primary actuel : MiniMax-M3 (provider minimax-oauth, base https://api.minimax.io/anthropic). Chaîne de fallback ordonnée :

Primary:   MiniMax-M3              (via minimax-oauth)
  1. claude-opus-4-8              (via anthropic, OAuth Claude Pro ou clé API)
  2. gemini-3.5-flash            (via gemini, clé API Google)
  3. stepfun/step-3.7-flash:free (via nous)
  4. openai/gpt-5.5              (via openrouter, clé API)

L’ordre se gère via :

hermes -p cloud fallback list        # inspecter
hermes -p cloud fallback add         # ajouter une entrée (picker validé)
hermes -p cloud fallback remove      # retirer une entrée
hermes -p cloud fallback clear       # repartir de zéro

Certains me demandent pourquoi MiniMax-M3 plutôt que Claude Opus 4.8. Le coût, déjà, sans commune mesure. Et puis le contexte géostratégique : je suis français, je vis en Europe, et je vois bien que ça pue d’avoir tous nos œufs dans le même panier.

Setup des deux profils

Le wrapper ops/hermes-mode.sh est fourni par le repo et simplifie la bascule. Pour le Mode B :

# One-time : crée le profil 'cloud' isolé
ops/hermes-mode.sh bootstrap

# Logins cloud (one-time, interactif)
hermes -p cloud model              # MiniMax-M3 primary, puis Claude OAuth si besoin

# Mettre les clés API dans ~/.hermes/profiles/cloud/.env :
#   GEMINI_API_KEY=...
#   OPENROUTER_API_KEY=...

# Chaîne de fallback
hermes -p cloud fallback clear
hermes -p cloud fallback add       # Gemini, puis Nous, puis OpenRouter
hermes -p cloud fallback list

Partie 2 : usage quotidien

Le piège : ne JAMAIS lancer hermes chat depuis le repo

Si tu fais cd ~/DEV/hermes-agent && hermes chat, l’AGENTS.md du repo (69 KB, un guide de dev pour contributeurs Hermes) est auto-injecté puis tronqué à ~31 KB. Résultat : un warning systématique, Context file AGENTS.md TRUNCATED: 69356 chars exceeds limit of 31457, et tu bouffes du prompt, donc des tokens, pour rien.

Solution : crée un répertoire de travail dédié.

mkdir -p ~/DEV/hermes-work

Mets-y un AGENTS.md qui te ressemble : généraliste, 1 à 2 KB, avec ton style de travail et tes contraintes. Exemple de contenu :

# Contexte utilisateur

Tu es mon assistant IA personnel. Tu m'aides dans mes tâches quotidiennes
(analyse de données, veille, scripting, debug, rédaction, etc.).

## Posture par défaut
- Mode local (par défaut) : Ollama via Hermes, aucun appel réseau.
- Mode cloud : uniquement sur demande explicite.

## Style de travail
- Va droit au but. Pas de préambule, pas de reformulation.
- Réponds en français sauf si la tâche est en anglais.
- Pour le code : snippets concrets et exécutables.
- Si tu ne sais pas, dis-le.

## Sécurité et données
- Pas de commande destructive sans validation.
- Pas d'upload hors du Mac sans demande explicite.
- Pour les fichiers sensibles : traitement local (pandas/openpyxl).

## Mémoire et contexte
- Pour un travail récurrent, propose un nom de projet pour `hermes sessions --continue`.

Personnalise-le au fil du temps (préférences, contraintes métier). C’est ton briefing de contexte par répertoire.

Toolset léger pour Mode A

Par défaut, le profil default charge le toolset complet (hermes-cli, ~100 outils, ~250 KB de schemas). Pour le Mode A, on allège.

Dans ~/.hermes/config.yaml :

platform_toolsets:
  cli: [file, memory, session_search, terminal, todo, web]
  telegram: [file, memory, session_search, terminal, todo, web]

Effet mesuré : toolset léger = 31 outils, ~83 KB de prompt (vs ~250 KB). Le gain de vitesse est marginal sur le 26B (compute-bound), mais sensible sur gpt-oss:20b. Le vrai bénéfice est ailleurs : surface d’attaque réduite, logs plus lisibles.

Pour tout réactiver ponctuellement : cli: [hermes-cli], ou bascule en Mode B.

Workflow quotidien

# Une session de travail local
cd ~/DEV/hermes-work
ops/hermes-mode.sh a          # Mode A (local/privé)
hermes chat                   # ouvre la session

# Une session de travail cloud
cd ~/DEV/hermes-work
ops/hermes-mode.sh b          # Mode B (cloud)
hermes chat

# Overrides ponctuels (sans toucher au profil actif)
hermes chat -m gemma4:26b-a4b-it-q4_K_M    # autre modèle, même profil
hermes chat -p cloud                        # une session Cloud sans basculer
hermes chat -p cloud -m claude-opus-4-8     # Cloud + modèle spécifique
hermes chat -t terminal,file,memory         # toolset ad hoc

Le profil actif est persistant

Une fois ops/hermes-mode.sh a (ou b) exécuté, tous tes hermes suivants héritent du choix, y compris après fermeture du terminal. La seule source de vérité est ~/.hermes/active_profile. Pour vérifier :

ops/hermes-mode.sh status
hermes profile

Bot Telegram

Le bot Telegram est un daemon séparé, installé comme service launchd.

Setup initial :

# Mode A par défaut
ops/hermes-mode.sh a
hermes gateway install              # daemon + auto-start au login
hermes gateway start                # démarre maintenant
hermes gateway status               # vérifie : "running"

Après chaque bascule A/B, redémarre le gateway pour qu’il recharge la config (sinon il continue à servir avec l’ancien profil) :

ops/hermes-mode.sh b
hermes gateway restart

Côté mobile : aucun réglage à faire. Envoie un message à ton bot, il répond avec le modèle du profil actif.

Commandes utiles :

ActionCommande
Voir l’état du gatewayhermes gateway status
Stopper le bothermes gateway stop
Redémarrer après basculehermes gateway restart
Logshermes logs gateway

Tableau résumé

Tu veux…Tu fais…
Travailler en localcd ~/DEV/hermes-work && ops/hermes-mode.sh a && hermes chat
Travailler en cloudcd ~/DEV/hermes-work && ops/hermes-mode.sh b && hermes chat
Une seule session cloudhermes -p cloud chat (profil actif inchangé)
Changer de modèlehermes chat -m <model-id>
Voir l’étatops/hermes-mode.sh status
Bascule + bot Telegramops/hermes-mode.sh b && hermes gateway restart
Stopper le bothermes gateway stop

Mise à jour

Une seule commande :

hermes update

Elle met à jour le repo, les deps Python et les submodules. Inutile de refaire un git pull à la main.

Où sont stockées mes données ?

Quoi
Code Hermes~/DEV/hermes-agent
Config + secrets Mode A~/.hermes/
Config + secrets Mode B~/.hermes/profiles/cloud/
Sandbox Mode A (bind Docker)~/.hermes/sandboxes/
Sandbox Mode B (bind Docker)~/.hermes/profiles/cloud/sandboxes/
Sessions, memory, skillsDans chaque profil (~/.hermes/<profile>/...)

L’image Docker utilisée est nikolaik/python-nodejs:python3.11-nodejs20. Le home /root du container est mappé en bind vers le sandbox dir ci-dessus, donc il persiste entre les sessions.

Validation périodique

ops/hermes-mode.sh status
hermes doctor                 # Mode A
hermes -p cloud doctor        # Mode B
hermes -p cloud fallback list # chaîne de fallback Mode B
hermes prompt-size            # Mode A : 31 outils, ~83 KB prompt total

Partie 3 : pour aller plus loin

Use cases documentés

Tips officiels

https://hermes-agent.nousresearch.com/docs/guides/tips

Et si la CLI te fatigue : Hermes Desktop

Une fois que tout ça tourne et que c’est pleinement opérationnel, si la CLI finit par te fatiguer et qu’une petite GUI à la Claude Desktop ou Minimax Code te manque, tu peux lancer hermes desktop et voilà. Même config, mêmes profils, mêmes secrets isolés : juste une interface graphique par-dessus.

https://hermes-agent.nousresearch.com/docs/user-guide/desktop

Leçons de ce setup

  1. Les profils Hermes sont une frontière de sécurité, pas un choix de modèle. Isolation des secrets, des sandbox, des sessions, posture par défaut safe.
  2. Le bottleneck de perf en local, c’est le compute (taille du modèle), pas le prompt. Réduire le toolset de 100 à 6 outils ne change presque rien au chrono. Le levier de vitesse, c’est le modèle.
  3. Toujours vérifier le context window avant d’adopter un modèle. Hermes exige au moins 64K. Les modèles 7B plafonnent souvent à 32K.
  4. Ne JAMAIS lancer hermes chat depuis le repo du framework. L’AGENTS.md du repo (69 KB) est injecté et tronqué. Crée un répertoire de travail avec ton propre AGENTS.md.
  5. Un .env Mode A clean, c’est la posture souveraine réelle. Toute clé cloud dans ~/.hermes/.env est un canal de fuite en cas d’injection de prompt.

Conclusion

Ce setup tourne en production perso depuis quelques semaines. Mode A pour toute la donnée sensible (Excel de compta, CSV clients, configs, RGPD inside), Mode B pour la veille publique et les tâches qui demandent un gros modèle. Le wrapper ops/hermes-mode.sh rend la bascule transparente. Le bot Telegram donne accès au même agent depuis mon mobile.

Le coût en complexité est celui du setup initial. Au quotidien, c’est juste cd ~/DEV/hermes-work && ops/hermes-mode.sh a && hermes chat.

Note : ce document est une note personnelle restructurée pour publication. Tout retour est bienvenu.