Hermes - Partie 04 : Les cerveaux de Roger, LM Studio sur Mac et GLM (Z.ai)

En bref
  • Deux cerveaux pour Roger : Qwen3.5 9B (MLX, 8 bits, contexte de 65 536 tokens) exécuté par LM Studio sur un MacBook Pro M4, et GLM via l'abonnement Coding Lite de Z.ai en secours.
  • Notions clés pour choisir un modèle local : nombre de paramètres, quantification, contexte (KV-cache), bande passante mémoire, MoE et mode « thinking », désactivé ici via le template Jinja.
  • LM Studio n'écoute que sur 127.0.0.1 avec un jeton obligatoire, derrière un NGINX en HTTPS (certificat de la PKI interne) et le pare-feu pf qui n'accepte que la VM.
  • Clé Z.ai de type « Personal Coding Plan », quotas en crédits, et les deux secrets rangés dans Vault sous secret/hermes/roger/.

Introduction

Dans la partie précédente, on a installé Hashicorp Vault dans une version durcie, avec une policy et un AppRole dédiés à notre agent "Roger", puis NGINX en frontal HTTPS du futur dashboard. Le coffre sécurisé est prêt, mais il est encore vide.

Cet article s'occupe des cerveaux de Roger. Comme annoncé dans la première partie, Roger en aura deux :

  • un modèle local, exécuté par LM Studio sur mon MacBook M4 Pro (24 Go de mémoire unifiée), qui sera son modèle par défaut ;
  • un modèle cloud, GLM, via mon abonnement GLM Coding Lite chez Z.ai, pour le raisonnement plus lourd et comme solution de secours si le Mac ne répond pas.

Dans la série OpenClaw, le lien entre l'agent et Ollama passait en HTTP, sans chiffrement ni authentification. Je l'avais déjà signalé comme une limite. Cette fois, on va essayer de faire mieux. L’idée est que LM Studio exige un jeton en n'écoutant que localement sur le Mac. C’est une autre instance de NGINX, installé sur mon laptop à la pomme, qui servira de frontal HTTPS avec un filtre sur les IP sources.

À la fin de l'article, les deux modèles seront testés depuis la VM et leurs clés d’accès API rangées dans Vault.

Pourquoi LM Studio plutôt qu'Ollama

J'ai déjà eu l’occasion de présenter Ollama dans d’autres articles. Pour cette série il aurait parfaitement fait l'affaire. Depuis sa version 0.40, il utilise même par défaut MLX, le moteur d'Apple optimisé pour ses puces. Mais pour ce cas d'usage, LM Studio coche plus de cases :

Critère LM Studio 0.4 Ollama 0.40
Moteur sur Apple Silicon MLX (et llama.cpp) MLX par défaut
Authentification de l'API Jetons avec permissions Aucune (clé ignorée)
Taille de contexte Réglable par modèle au chargement Réglage global ou Modelfile
Maintien en mémoire Modèle chargé sans limite de durée possible Variable OLLAMA_KEEP_ALIVE
Provider dans Hermes lmstudio natif Point de terminaison « custom »

Les deux points décisifs qui m’ont fait changer pour LM Studio sont l'authentification native et le contrôle du contexte. Rappelez-vous : Hermes refuse de démarrer avec un contexte de moins de 64 000 tokens. Avec Ollama, la valeur par défaut sur une machine de 24 Go est de 4 096 tokens, et l'oublier provoque des erreurs déroutantes. Avec LM Studio, on fixe la taille au moment du chargement, et on la voit.

LM Studio sur le Mac

Installation

Ma machine est un MacBook Pro M4 avec 24 Go de mémoire, sous macOS Tahoe 26.6.2, avec une adresse fixe 192.168.10.105 réservée sur mon DHCP. Homebrew, le gestionnaire de paquet open source pour Mac, est déjà installé (on en aura besoin pour NGINX).

LM Studio s'installe depuis le site officiel (lmstudio.ai/download), en version 0.4.x. Attention au piège : la page de téléchargement propose aussi Bionic, la nouvelle application d'agent de l'éditeur, sorti en juillet 2026. Ce n'est pas elle qu'il nous faut. Bionic est un agent prêt à l'emploi qui s'appuie sur le moteur de LM Studio, alors que nous avons besoin du serveur d'API de LM Studio et de sa gestion des jetons. Les deux applications peuvent cohabiter sur le même Mac.

Page de téléchargement de LM Studio 0.4.26 pour macOS sur puce M, avec l'installation de llmster en dessous Enregistrement de l'image disque LM-Studio-0.4.26-4-arm64.dmg dans Téléchargements Ouverture de l'image disque de LM Studio : glisser l'application dans Applications Fenêtre d'installation de LM Studio 0.4.26 avec le raccourci vers Applications

Cliquez sur l'image pour l'agrandir.

Au premier lancement, on active les réglages avancés dans Settings > Developer.

Premier lancement de LM Studio 0.4.26 : écran d'accueil Local AI on your computer Écran Advanced settings de LM Studio : Developer Mode et démarrage du service local à l'ouverture de session activés Fenêtre principale de LM Studio avec les menus Chat, Model Server, Library, LM Link et Model Search

Cliquez sur l'image pour l'agrandir.

LM Studio fournit aussi un outil en ligne de commande, lms. On l'ajoute au PATH du shell du Mac pour mon user:

~/.lmstudio/bin/lms bootstrap
# ouvrir un nouveau terminal, puis :
lms version
lms bootstrap : le CLI de LM Studio est déjà installé pour le shell zsh lms version : bannière du CLI de LM Studio

Cliquez sur l'image pour l'agrandir.

Il existe aussi une version sans interface graphique, llmster, qui s'installe avec un script (curl -fsSL https://lmstudio.ai/install.sh | bash) et se lance avec lms daemon up. C'est la cible idéale pour un serveur. Sur un Mac que j'utilise aussi au quotidien, je préfère garder l'application : la gestion des jetons y est plus simple et visuelle.

Quelques notions avant de choisir

Trois notions conditionnent le choix du modèle. Il est très important de bien les maitriser et d’en comprendre l’impact.

Le nombre de paramètres: un modèle de 9 milliards de paramètres (9B) est un ensemble de 9 milliards de nombres. Ces nombres représentent les paramètres possibles du modèle. Plus il y en a, plus le modèle est normalement performant, mais il occupe plus de place en mémoire et peut être plus lent à l’exécution. C’est pourquoi non seulement la quantité de mémoire est importante, mais également sa vitesse, d’où l’impact des architectures Apple type M, ou la mémoire commune au CPU et au GPU est extrêmement rapide et donc adapté à l’exécution de modèle. C’est aussi le cas des cartes graphiques où le bus mémoire est bien plus rapide entre le GPU et la mémoire en comparaison du bus mémoire entre le CPU et les classiques barrettes de mémoire.

Pour vous donner quelques idées de comparaison, même si les grands leaders du marché ne communiquent pas toujours de manière très explicite sur la taille de leur paramètre, sachez que GPT3 (2020) était aux alentours des 175B pour arriver à des estimations entre 2000B et 5000B pour GPT5 (version pro)…Fable 5 se situerait entre 5000B et 10000B selon les estimations…On arrive à des tailles d’occupation entre 5To et 20To de mémoire…et on ne parle pas du stockage du contexte des conversations…

Schéma de l'évolution de la taille des modèles en échelle logarithmique, de GPT-2 (2019, 1,5B) à GPT-3 (2020, 175B), PaLM (2022, 540B), GPT-4 (2023, environ 1 800B), GPT-5 pro (2025, 2 000 à 5 000B estimés) et Fable 5 (2026, 5 000 à 10 000B estimés), puis les modèles open weight Llama 3.1 405B, DeepSeek-V3, Kimi K2, Qwen3.5-397B, Kimi K3 (2 800B) et Qwen3.5 9B, avec la mémoire occupée par les poids

Cliquez sur l'image pour l'agrandir.

Question vitesse, la bande passante mémoire d’un PC standard avec de la DDR-5, c’est entre 77 et 90 Go/s, soit 13 à 15 tokens/s (un token représente grossièrement trois quarts de mots). Une puce comme celle de mon MAC offre une vitesse de 273 Go/s soit 40 à 45 token/s. Un GPU type RTX 5070 Ti monte à 896 Go/s pour 130 à 150 tokens/s….Quand un GPU spécialisé utilisé en datacenter type NVIDIA B200 c’est 8000 Go/s soit +1200 tokens/s

Schéma de la bande passante mémoire et de la vitesse de génération, du PC en DDR4 (51 Go/s) et DDR5 (77 à 90 Go/s) au MacBook Pro M4 Pro (273 Go/s), RTX Spark, M4 Max, RTX 5070 Ti (896 Go/s), RTX 5090, H100, H200, B200 (8 000 Go/s) et Vera Rubin (19 200 Go/s), avec le plafond de tokens par seconde associé

Cliquez sur l'image pour l'agrandir.

Je trouve toujours important d’avoir quelques référentiels en tête, même si les chiffres cités restent des approximations, ça permet de situer un peu les enjeux… Maintenant la taille du modèle ne fait pas tout comme on va le voir dans la définition de la notion suivante.

La quantification: chaque paramètre est stocké sur un certain nombre de bits. À l'origine, c'est 16 bits. Quantifier un modèle en 8 bits divise sa taille par deux, avec une perte de qualité généralement imperceptible ; en 4 bits, on la divise par quatre, avec une perte plus sensible, notamment sur la rigueur des appels d'outils. Pour un agent qui doit produire des appels d'outils bien formés, il est préférable de ne pas aller en dessous des 8 bits quand la mémoire le permet. En gros la quantification permet de garder le même nombre de paramètres, mais un besoin en stockage plus réduit…en sacrifiant la précision des réponses et en augmentant le risque d’hallucination. Attention à la quantification, car certains acteurs du marché ne sont pas toujours très « franc » là-dessus. Par exemple, quand NVIDIA propose son récent processeur RTX Spark destiné au laptop, lui-même issu du DGX Spark dont j’avais déjà parlé, ils communiquent sur des capacités d’exécution de modèles jusqu’à 120 milliards de paramètres (120B)… oui, mais en 4 bits 😊

Schéma de la quantification : un paramètre stocké sur 16 bits à l'origine, 8 bits divisent la taille par deux, 4 bits par quatre ; Qwen3.5 9B passe de 18 Go à 9 Go puis 4,5 Go sous les 24 Go du Mac de Roger, un modèle de 120B passe de 240 Go à 120 Go puis 60 Go et ne tient confortablement dans les 128 Go du RTX Spark qu'en 4 bits

Cliquez sur l'image pour l'agrandir.

Le contexte: c'est la quantité de texte que le modèle peut prendre en compte à chaque échange : les instructions du SOUL.md, l'historique de la conversation, les réponses des outils. Il se mesure également en tokens. Le contexte a un coût en mémoire qui s'ajoute à celui du modèle, et ce coût augmente avec sa taille. Or, un agent consomme énormément de contexte : une seule réponse du serveur MCP de Xen Orchestra peut contenir des milliers de tokens. C'est pourquoi Hermes exige 64 000 tokens au minimum.. Cette occupation de mémoire supplémentaire se traduit physiquement en mémoire par le KV-cache et est propre à chaque conversion… Là aussi, quelques ordres de grandeur sont intéressants à connaitre. Comme vous pourrez l’apprendre dans mon podcast préféré, à la french, que je vous encourage grandement à regarder, pour un serveur spécialisé à 500 000€, avec un modèle chargé en mémoire d’environ 500B, la prise en compte du KV cache ne permet d’accueillir en mémoire restante qu’environ 10 personnes en simultanés. Enfin, 100 000 tokens de contexte, autrefois qualifié de cas rare (l’analyse d’un gros PDF sur un RAG (Retrieval-Augmented Generation) par exemple) sont devenus un standard quotidien dans l’agentique.

Quelques précisions, quant au terme MLX. C’est le moteur de calcul « dédié à l’IA » d’Apple, optimisé pour ses puces. Sur un Mac, il exploite pleinement la mémoire unifiée partagée entre le processeur et le GPU, ce qui permet de charger, comme expliquer plus haut, des modèles plus lourds. C'est ce qui rend un MacBook de 24 Go intéressants pour l'IA locale (et, globalement, pourquoi les ordinateurs Apple connaissent un intérêt certain pour ceux qui souhaitent faire de l’IA en local (c'est même juste pour ça que j'ai bypassé mes principes pour acheter un mac....)).

Enfin, pour terminer cette section de l’article, sachez que pour générer un token, l’intégralité des paramètres actifs doit être parcourue… La vitesse maximale théorique de génération de token peut donc se calculer via

Vitesse max (tokens/s) ≈ Bande passante mémoire (Go/s) Taille des paramètres actifs en mémoire (Go)

À cette contrainte, il existe maintenant des optimisations, ou des modèles à plusieurs milliards de paramètres, n’ont pas forcement d’actif l’intégralité de ces derniers, mais uniquement ceux qui sont nécessaires à la problématique posée, on parle de MoE (Mixture of Experts). C’est notamment autour de ses optimisations, reprises aujourd’hui par tout le monde, que des entreprises comme Mistral (cocorico) ou DeepSeek se sont fait connaitre.

Comme dit plus haut, il est important d’avoir tous ses fondamentaux en tête lorsqu’on va être amené à choisir un modèle pour son agent.

Le choix du modèle

Pas question ici de pouvoir utiliser des modèles de OpenIA ou Anthropic. Ils sont fermés et utilisables uniquement dans le cadre d’un abonnement associé et au sein de leur propre applicatif la plupart du temps. Par contre il existe des modèles dits OpenWeight, à ne pas confondre avec OpenSource. Le plus souvent chinois, il s’agit de LLM entrainés en datacenters et mis ensuite à disposition du public pour un usage interne. Leur particularité est que leurs paramètres finals sont configurables et adaptables. Ce qui permet d’en faire de multiples déclinaisons, partant du modèle le plus lourd à des modèles bien plus léger. (Attention ! Le plus souvent, vous ne savez pas comment le modèle a été entrainé ni les détails de sa création.)

Les modèles OpenWeight ont tendance à rattraper leur retard sur les modèles dits « frontier » propres aux leaders du marché. Même s’ils n’égalent jamais (ou du moins pas encore) les ténors actuels, ils peuvent se montrer bien suffisants pour de nombreux cas d’usage.

Classement Artificial Analysis Intelligence Index par modèles propriétaires et open weight : Claude Fable 5.2 en tête avec 65, Kimi K3.1 Pro premier modèle open weight avec 46, GLM-5.3 à 32

Source : Artificial Analysis, Intelligence Index v5.0.1 (propriétaires / open weight).

Cliquez sur l'image pour l'agrandir.

Cependant, ce n’est pas parce que, par exemple, Kimi K3 est disponible gratuitement que vous pourrez le faire tourner avec ses 2800 milliards de paramètres dans sa version de base… Ses poids étant ouverts, il faudra se rabattre sur des versions avec bien moins de paramètres.

Sur mon Mac, avec 24 Go de mémoire partagés entre macOS, les applications et le modèle, il faut être raisonnable. La contrainte d'Hermes (64 000 tokens minimum) pèse lourd : un contexte de cette taille occupe déjà plusieurs gigaoctets en plus des poids du modèle.

Je me suis donc arrêté sur Qwen3.5 9B au format MLX, quantifié en 8 bits (lmstudio-community/Qwen3.5-9B-MLX-8bit). C'est la même famille que celle utilisée avec OpenClaw, elle gère l'appel d'outils et accepte nativement de très longs contextes. Ce modèle publié par Alibaba Cloud est décliné dans toute une gamme de LLM allant jusqu’’à 397 milliards de paramètres, avec de nombreuses variantes. Il sert de base à bien des déclinaisons fonction du but recherché. Dans notre cas, Qwen3.5 9B est un bon compromis pour un agent conversationnel en charge de taches simples.

On télécharge le modèle. Petite subtilité : avec un identifiant de la forme propriétaire/nom, lms get cherche dans le catalogue de LM Studio et répond que le modèle n'existe pas. Pour aller le chercher sur Hugging Face, la librairie de modèle LLm la plus conséquente au monde, il faut lui donner l'URL complète du dépôt :

lms get https://huggingface.co/lmstudio-community/Qwen3.5-9B-MLX-8bit

lms annonce la taille du téléchargement (10,45 Go pour la version 8 bits) et demande une confirmation avant de commencer.

lms get : téléchargement de Qwen3.5 9B 8bit terminé

Cliquez sur l'image pour l'agrandir.

Une fois le téléchargement terminé, attention : LM Studio n'enregistre pas le modèle sous le nom du dépôt Hugging Face, mais sous une clé courte qui lui est propre. On la retrouve avec lms ls :

Chez moi, la clé est qwen3.5-9b-mlx. C'est elle qu'il faut utiliser avec lms load. Avec le nom du dépôt, lms répond qu'il ne trouve pas le modèle et propose de le choisir dans une liste.

lms ls : le modèle qwen3.5-9b-mlx de 10,45 Go et le modèle d'embedding nomic

Cliquez sur l'image pour l'agrandir.

On demande ensuite à LM Studio une estimation de la mémoire nécessaire avec le contexte voulu, avant de charger réellement le modèle :

lms load qwen3.5-9b-mlx --context-length 65536 --estimate-only
lms load --estimate-only : estimation de 13,63 Gio pour 65 536 tokens de contexte

Cliquez sur l'image pour l'agrandir.

Chez moi, LM Studio estime le besoin à 13,63 Gio : un peu moins de 10 Gio pour le modèle lui-même, le reste pour les 65 536 tokens de contexte. Sur 24 Go, il reste donc environ 10 Go pour macOS et mes applications, ce qui est confortable. Notez le Confidence: LOW : il s'agit d'une estimation, pas d'une mesure.

Si l'estimation dépasse ce que votre Mac (ou votre matériel) peut réserver confortablement (gardez au moins 6 à 8 Go pour macOS et vos applications), passez à la version 4 bits.

Chargement et serveur

On charge le modèle avec un contexte de 65 536 tokens et un identifiant court. C'est cet identifiant que Hermes utilisera pour désigner le modèle :

lms load qwen3.5-9b-mlx \
  --context-length 65536 --identifier qwen3.5-9b-mlx-8bit
lms ps

On retrouve notre identifiant, le contexte de 65 536 tokens et aucune durée de vie (TTL vide). La taille affichée, 10,45 Go, correspond aux poids du modèle : la mémoire du contexte s'y ajoute au fil des échanges. La colonne PARALLEL indique que LM Studio peut traiter jusqu'à 4 requêtes en même temps.

Sans option de durée de vie (--ttl), un modèle chargé avec lms load reste en mémoire. C'est voulu : dans la série OpenClaw, je constatais que la première conversation était lente parce qu'Ollama rechargeait le modèle. Ici, il reste chargé en permanence.

lms load de qwen3.5-9b-mlx avec l'identifiant qwen3.5-9b-mlx-8bit, chargement en cours lms ps : qwen3.5-9b-mlx-8bit chargé avec un contexte de 65536, 4 requêtes parallèles et sans TTL

Cliquez sur l'image pour l'agrandir.

Sans option de durée de vie (--ttl), un modèle chargé avec lms load reste en mémoire. C'est voulu : dans la série OpenClaw, je constatais que la première conversation était lente parce qu'Ollama rechargeait le modèle. Ici, il reste chargé en permanence.

Les réglages du serveur ne sont pas dans les préférences de l'application. Ils sont à chercher dans la barre latérale, section Developer. On ouvre « Local Server » (la page s'intitule « Model Server »), puis on clique sur le bouton « Server Settings » en haut de la page :

  • « Server Port » : 1234 ;
  • « Require Authentication » activé ;
  • « Serve on Local Network » désactivé : LM Studio n'écoute que sur 127.0.0.1 (c'est l'adresse affichée dans « The local server is reachable at »), et c'est NGINX qui exposera le service ;
  • « Allow per-request MCPs » désactivé, ainsi que « Allow calling servers from mcp.json » : Roger a ses propres serveurs MCP côté Hermes, il n'a aucune raison de demander à LM Studio d'en appeler d'autres ;
  • « Just-in-Time Model Loading » désactivé : sans lui, un client ne peut pas faire charger un autre modèle à la volée. Seul le modèle que l'on a chargé nous-mêmes répond, et la mémoire du Mac reste sous contrôle.
LM Studio, page Model Server et panneau Server Settings : port 1234, Require Authentication, Serve on Local Network, MCP et Just-in-Time Model Loading

Cliquez sur l'image pour l'agrandir.

Reste la clef API. Toujours dans « Server Settings », sous « Require Authentication », le bouton « Manage Tokens » ouvre la fenêtre « LM Studio API Tokens ». On clique sur « Create new token » et on le nomme roger.

Bouton Manage Tokens sous Require Authentication, aucune clé API active Fenêtre LM Studio API Tokens vide avec le bouton Create new token

Cliquez sur l'image pour l'agrandir.

LM Studio ne propose pas de permissions très fines (mais c’est toujours mieux que ollama) : un jeton donne toujours l'accès de base à l'API (« Baseline access »). Les seules permissions réglables concernent les serveurs MCP, et on les passe toutes les deux à Deny :

  • « Allow per-request remote MCP servers » : Deny ;
  • « Allow calling servers from mcp.json » : Deny.

Attention, la valeur par défaut de la première est « Allow ». C'est un réflexe à prendre : quand une permission ne sert à rien, on la ferme.

Fenêtre Create new permission token avec le nom roger et les deux permissions MCP

Cliquez sur l'image pour l'agrandir.

Le jeton (de la forme sk-lm-…) n'est affiché qu'une seule fois, dans la fenêtre « Token Created ». Copiez-le avec le bouton « Copy » et gardez-le de côté le temps de cet article : on le rangera dans Vault un peu plus loin.

Fenêtre Token Created de LM Studio, valeur du jeton masquée

Cliquez sur l'image pour l'agrandir.

On démarre le serveur et on vérifie qu'il n'écoute bien que localement :

lms server start
lms server status
lsof -nP -iTCP:1234 -sTCP:LISTEN

La dernière commande doit montrer une écoute sur 127.0.0.1:1234, et non sur *:1234.

lms server start et status, lsof montrant LM Studio en écoute sur 127.0.0.1:1234

Cliquez sur l'image pour l'agrandir.

Tests locaux

On teste l'API compatible OpenAI, d'abord sans jeton (ce qui doit échouer), puis avec :

read -rs LMS_TOKEN && export LMS_TOKEN   # coller le jeton, rien ne s'affiche
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:1234/v1/models
curl -s http://127.0.0.1:1234/v1/models -H "Authorization: Bearer $LMS_TOKEN" | jq -r '.data[].id'

On attend un code 401 pour la première commande, puis l'identifiant qwen3.5-9b-mlx-8bit dans la liste.

Saisie masquée du jeton LM Studio dans la variable LMS_TOKEN Requête sans jeton vers /v1/models : code 401 Requête avec jeton vers /v1/models : qwen3.5-9b-mlx-8bit

Cliquez sur l'image pour l'agrandir.

Un agent passe son temps à appeler des outils. On vérifie donc que le modèle sait produire un appel d'outil, avec une fonction fictive :

curl -s http://127.0.0.1:1234/v1/chat/completions \
  -H "Authorization: Bearer $LMS_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.5-9b-mlx-8bit",
    "messages": [{"role": "user", "content": "Combien de VMs tournent sur le pool ?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "count_running_vms",
        "description": "Retourne le nombre de VMs démarrées sur le pool Xen Orchestra",
        "parameters": {"type": "object", "properties": {}, "required": []}
      }
    }]
  }' | jq '.choices[0].message.tool_calls, .usage'

La réponse doit contenir un tool_calls vers count_running_vms, et non une réponse inventée. C'est un bon indicateur de la capacité du modèle à fonctionner comme cerveau d'un agent.

Réponse JSON de Qwen3.5 contenant un tool_calls vers count_running_vms et le détail usage

Cliquez sur l'image pour l'agrandir.

Pour une idée de la vitesse, on chronomètre une génération un peu plus longue et on regarde le nombre de tokens produits :

time curl -s http://127.0.0.1:1234/v1/chat/completions   -H "Authorization: Bearer $LMS_TOKEN" -H "Content-Type: application/json"   -d '{"model":"qwen3.5-9b-mlx-8bit","messages":[{"role":"user","content":"Explique en 200 mots ce qu un hyperviseur de type 1."}]}'   | jq '.usage'

Et là, première surprise. Chez moi, la commande a mis plus de cinq minutes à rendre la main :

Journaux du serveur LM Studio : 7273 tokens de réponse dont 6969 tokens de raisonnement Nouveau test d'appel d'outil puis génération chronométrée : plus de 7000 tokens produits

Cliquez sur l'image pour l'agrandir.

Plus de 7 000 tokens pour une réponse de 200 mots, dont près de 7 000 de reasoning_tokens. Le modèle n'est pas lent, il réfléchit beaucoup trop. Qwen3.5 fonctionne par défaut en mode « thinking » : avant de répondre, il produit un long raisonnement interne. Pour un agent qui enchaîne les appels d'outils, c'est rédhibitoire : chaque tour prendrait des minutes et grignoterait le contexte.

Cet incident permet de mettre en avant cette notion de thinking ou de raisonnement. C’est une notion de plus à appréhender. C’est la capacité du modèle à générer un brouillon de réflexion interne, avant de vous donner la réponse à votre question. (c'est le "Il faut tourner sept fois sa langue dans sa bouche avant de parler" appliqué" à l'IA).

Si au départ, les modèles incluant ce principe de fonctionnement étaient distincts des modèles sans, la capacité de raisonnement est désormais incluse pour beaucoup de modèles, mais réglable, selon le niveau voulu (low, medium, high) ou désactivable. Activer le plus haut niveau de raisonnement d’un modèle permet d’affiner les réponses et d’éviter les erreurs, mais possède un coup en token et en temps pouvant s’avérer très important, voir contre-productif quand il s’agit de demander au modèle des taches simples.

Dans mon cas, Roger n’aura pas à résoudre des problèmes mathématiques avancés ni a essayer de prévoir le résultat des élections françaises… mais de répondre à de simples questions types « combien j’ai de VM allumée ». Le mode thinking est donc totalement inadapté dans mon cas, voire même pénalisant.

Désactiver le thinking et ajuster l'inférence

LM Studio ne propose pas d'interrupteur pour couper le thinking de Qwen3.5, et les paramètres que l'on pourrait envoyer dans la requête ne sont pas pris en compte. La solution passe par le modèle de prompt (le template Jinja) du modèle.

Dans la barre latérale, on ouvre Library, puis on clique sur l'icône d'engrenage du modèle qwen3.5-9b-mlx. Le panneau de droite affiche sa configuration par défaut. Dans l'onglet Inference, section Prompt Template, on ajoute cette ligne tout en haut du template (onglet « Template (Jinja) ») :

{%- set enable_thinking = false %}

Le modèle passe alors en statut « Customized ».

LM Studio, Library, Qwen3.5-9B-MLX-8bit en statut Customized, onglet Inference et section Prompt Template Template Jinja du modèle commençant par la ligne set enable_thinking = false

Cliquez sur l'image pour l'agrandir.

Tant qu'on y est, on corrige les paramètres de génération, toujours dans l'onglet Inference. Les valeurs par défaut de LM Studio sont assez éloignées de celles que recommande Qwen pour le mode sans thinking :

Réglage Valeur par défaut Recommandation Qwen
Temperature 0.1 0.7
Top K Sampling 100 20
Top P Sampling désactivé activé, 0.8
Repeat Penalty activé, 1.1 désactivé
Paramètres d'inférence en cours de modification : Temperature 0.7 et Top K Sampling 20 Enregistrement de la configuration sous un preset nommé hermes

Cliquez sur l'image pour l'agrandir.

À quoi correspondent exactement ces paramètres, je ne saurais le détailler ici… et pour ne pas vous mentir, c’est l’IA elle-même, a savoir mon ami OPUS 5.5, qui m’a conseillé ces valeurs. La température reste le plus intéressante à mon niveau, puisqu’elle définit le degré de hasard (ou de créativité) du modèle dans le choix des tokens. Ça ne change ni la vitesse ni la consommation de mémoire, mais influence le modèle sur son choix de probabilité associé à votre question. Définissez une température à 0 et le modèle choisira toujours le mot qui a le plus de probabilité de correspondre au sens de la phrase. Rapprochez-vous de 1 et vous autorisez le LLM à retenir des mots s’éloignant du mot ayant le score de probabilité le plus haut…d’où plus de créativité… On serait tenté de mettre zéro tout le temps, notamment pour des tâches comme le codage… mais, associé au thinking, vous pouvez provoquer une boucle de répétition infinie où le modèle cherche absolument le niveau de probabilité le plus haut. Acceptez d’augmenter la température et vous autorisez le modèle à explorer plusieurs pistes de résolution.

Globalement, il est souvent nécessaire de « tweaker » la conf d’un modèle open weight pour une exécution en local. Il faut souvent tester et itérer pour trouver le bon paramétrage qui correspond au compromis recherche entre qualités des réponses et vitesse d’exécution. Pour le coup, l’usage d’un IA frontier est une bonne aide pour vous aider à affiner votre paramétrage d’IA locale… Il faut savoir combiner le meilleur des deux mondes pour exploiter l’IA 😊

On continue en éjectant le modèle (bouton « Eject » dans la page Model Server), on le recharge avec la même commande lms load que plus haut, et on relance le test :

Test de génération après désactivation du thinking : reasoning_tokens à 0 et durée d'environ 11 secondes

Cliquez sur l'image pour l'agrandir.

Plus aucun token de raisonnement, 313 tokens de réponse, et la main rendue en moins de 12 secondes, soit environ 27 tokens par seconde. C'est tout à fait confortable pour Roger.

Le Mac ne doit pas dormir

Un modèle local n'a d'intérêt que si le Mac est disponible. Or un MacBook n'aime rien tant que de se mettre en veille, et un Mac endormi, c'est un Roger qui bascule sur GLM, voire qui ne répond plus.

Pour ma part, j'utilise Amphetamine, une petite application gratuite disponible sur le Mac App Store. Elle s'installe dans la barre des menus et empêche la mise en veille, sur secteur comme sur batterie, pour une durée choisie ou indéfiniment. L'écran, lui, peut s'éteindre sans problème : seule la veille du système nous gêne. C'est la solution la plus souple sur un portable que je déplace et que j'utilise aussi au quotidien.

Page d'Amphetamine sur le Mac App Store : utilitaire gratuit qui empêche la mise en veille du Mac

Cliquez sur l'image pour l'agrandir.

Si vous préférez vous passer d'une application, pmset fait le même travail en ligne de commande, mais uniquement sur secteur (option -c) :

sudo pmset -c sleep 0 disksleep 0 displaysleep 10
pmset -g | grep -E ' sleep|displaysleep|disksleep'

Pour que le modèle soit rechargé automatiquement après un redémarrage, on active dans les réglages de LM Studio le démarrage du serveur à l'ouverture de session, et on ajoute un petit agent de lancement qui recharge le modèle avec le bon contexte (une idée de l’IA 😊) :

mkdir -p ~/bin ~/Library/LaunchAgents
cat > ~/bin/lms-load-roger.sh <<'EOF'
#!/bin/zsh
# Recharge le modèle de Roger avec 64K de contexte au démarrage de la session
LMS="$HOME/.lmstudio/bin/lms"
for i in {1..30}; do "$LMS" server status >/dev/null 2>&1 && break; sleep 10; done
"$LMS" ps | grep -q qwen3.5-9b-mlx-8bit || \
  "$LMS" load qwen3.5-9b-mlx --context-length 65536 --identifier qwen3.5-9b-mlx-8bit
EOF
chmod +x ~/bin/lms-load-roger.sh

cat > ~/Library/LaunchAgents/tech.myprivatelab.lms-load-roger.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>tech.myprivatelab.lms-load-roger</string>
  <key>ProgramArguments</key><array><string>$HOME/bin/lms-load-roger.sh</string></array>
  <key>RunAtLoad</key><true/>
  <key>StandardOutPath</key><string>/tmp/lms-load-roger.log</string>
  <key>StandardErrorPath</key><string>/tmp/lms-load-roger.log</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/tech.myprivatelab.lms-load-roger.plist
Création du script lms-load-roger.sh et de l'agent de lancement tech.myprivatelab.lms-load-roger.plist, puis launchctl load

Cliquez sur l'image pour l'agrandir.

Cela suppose une session utilisateur ouverte au démarrage du Mac (ouverture de session automatique), ce qui est acceptable sur une machine personnelle chez moi, mais clairement pas en entreprise.

Exposer le modèle proprement

DNS et certificat

C'est le moment de créer la seconde entrée DNS annoncée : llmmac.coolcorp.priv, enregistrement A vers l'adresse du Mac, 192.168.10.105. Je préfère un nom qui désigne la machine plutôt que le service : demain, une autre source de modèles pourrait arriver et elle aura simplement son propre nom à côté. Je pense en particulier à ma carte NVIDIA RTX 5070 Ti et ses 16 Go de VRAM, que j'avais utilisée avec OpenClaw. Pour l'instant, elle est réservée à un autre projet, dont je vous parlerai peut-être un jour sur ce site s'il aboutit.

Console DNS Windows, nouvel enregistrement A llmmac vers 192.168.10.105 avec son pointeur PTR

Cliquez sur l'image pour l'agrandir.

Pour le certificat, on procède comme dans la partie 03, mais directement sur le Mac. L'OpenSSL fourni par macOS est en réalité LibreSSL ; j'utilise celui de Homebrew pour rester identique à la VM :

brew install openssl@3 nginx
OPENSSL="$(brew --prefix openssl@3)/bin/openssl"
mkdir -p ~/pki && cd ~/pki
cat > llmmac.cnf <<'EOF'
[req]
default_bits       = 4096
prompt             = no
distinguished_name = dn
req_extensions     = ext

[dn]
CN = llmmac.coolcorp.priv
O  = COOLCORP
C  = FR

[ext]
subjectAltName = DNS:llmmac.coolcorp.priv
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
EOF
$OPENSSL req -new -newkey rsa:4096 -nodes -config llmmac.cnf -keyout llmmac.key -out llmmac.csr
chmod 600 llmmac.key
brew install openssl@3 nginx : installation de NGINX 1.31.8 sur le Mac Variable OPENSSL pointant vers l'OpenSSL de Homebrew Création du dossier ~/pki et du fichier de configuration llmmac.cnf Génération de la clé privée RSA 4096 et de la demande llmmac.csr Contenu du dossier ~/pki : llmmac.cnf, llmmac.csr et llmmac.key

Cliquez sur l'image pour l'agrandir.

On soumet llmmac.csr à l'autorité depuis un poste Windows avec le même modèle :

certreq -submit -attrib "CertificateTemplate:TPL-SRV-WEB-DEFAULT" llmmac.csr llmmac.cer
certreq -submit avec le modèle TPL-SRV-WEB-DEFAULT : certificat émis

Cliquez sur l'image pour l'agrandir.

On ramène le fichier llmmac.cer et le certificat racine CA (CA-COOLCORP.crt, identique à celui vu dans la section 03) dans le dossier ~/pki sur le Mac.

Contenu PEM du certificat llmmac.cer copié sur le Mac Dossier ~/pki avec CA-COOLCORP.crt et llmmac.cer

Cliquez sur l'image pour l'agrandir.

Première étape : ajouter la CA au trousseau système, pour que les outils du Mac lui fassent confiance. Petite subtilité, celle-ci ne se fait pas en SSH. Modifier les réglages de confiance du trousseau système demande une confirmation dans une fenêtre de macOS, qu'une session SSH ne peut pas afficher : la commande security add-trusted-cert échoue. On passe donc par l'interface graphique, directement sur le Mac :

security add-trusted-cert en SSH : authorization denied since no user interaction was possible

Cliquez sur l'image pour l'agrandir.

  • dans le Finder, double-cliquer sur CA-COOLCORP.crt : macOS ouvre Trousseau d'accès et propose d'ajouter le certificat, on choisit le trousseau Système ;
  • dans Trousseau d'accès, double-cliquer sur le certificat ajouté, déplier la section Se fier et choisir Toujours approuver pour « Lors de l'utilisation de ce certificat » ;
  • fermer la fenêtre et confirmer avec le mot de passe administrateur.
Trousseau d'accès macOS : ajout de CA-COOLCORP.crt au trousseau Système

Cliquez sur l'image pour l'agrandir.

Tout le reste se fait sans problème en SSH. On convertit et on vérifie le certificat du Mac, puis on l'installe pour NGINX :

cd ~/pki
$OPENSSL x509 -in llmmac.cer -out llmmac.crt
$OPENSSL verify -CAfile CA-COOLCORP.crt llmmac.crt
NGX="$(brew --prefix)/etc/nginx"
mkdir -p "$NGX/certs"
install -m 644 llmmac.crt "$NGX/certs/llmmac.coolcorp.priv.crt"
install -m 600 llmmac.key "$NGX/certs/llmmac.coolcorp.priv.key"
Conversion du certificat en llmmac.crt et vérification contre la CA : OK Installation du certificat et de la clé dans le dossier certs de NGINX

Cliquez sur l'image pour l'agrandir.

NGINX sur le Mac

La configuration reprend le principe de celle de la VM, avec deux précautions en plus : seule l'adresse de la VM est admise, et toute requête sans en-tête Authorization est rejetée avant même d'atteindre LM Studio. Les réponses d'un LLM peuvent durer plusieurs minutes et arrivent en flux, d'où les délais longs et la désactivation de la mise en tampon.

cat > "$(brew --prefix)/etc/nginx/servers/llmmac.conf" <<'EOF'
# Termine le TLS devant LM Studio (127.0.0.1:1234) pour l'agent Roger.
server {
    listen 443 ssl;
    http2 on;
    server_name llmmac.coolcorp.priv;

    ssl_certificate     /opt/homebrew/etc/nginx/certs/llmmac.coolcorp.priv.crt;
    ssl_certificate_key /opt/homebrew/etc/nginx/certs/llmmac.coolcorp.priv.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Seule la VM de Roger est admise (pf filtre aussi en amont)
    allow 192.168.10.41;
    deny all;

    # Défense en profondeur : pas de jeton, pas de passage
    if ($http_authorization = "") { return 401; }

    location / {
        proxy_pass http://127.0.0.1:1234;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header Authorization $http_authorization;
        proxy_buffering off;
        proxy_read_timeout 900;
        proxy_send_timeout 900;
    }
}
EOF
nginx -t
brew services start nginx
lsof -nP -iTCP:443 -sTCP:LISTEN
Création du fichier llmmac.conf de NGINX sur le Mac nginx -t : syntaxe correcte brew services start nginx : service démarré lsof : NGINX en écoute sur le port 443

Cliquez sur l'image pour l'agrandir.

Sur un Mac Apple Silicon, Homebrew est installé dans /opt/homebrew, d'où les chemins des certificats. Depuis plusieurs versions, macOS permet à un utilisateur normal d'écouter sur le port 443 ; si le démarrage échoue malgré tout, lancez le service avec sudo brew services start nginx.

Le pare-feu du Mac

L'allow de NGINX est une première barrière, mais je préfère que les paquets venant d'une autre machine n'atteignent même pas NGINX. On utilise pf, le pare-feu intégré de macOS, avec une ancre dédiée :

sudo tee /etc/pf.anchors/llmmac >/dev/null <<'EOF'
# N'accepter le 443 (NGINX -> LM Studio) que depuis la VM Roger
block in quick proto tcp from ! 192.168.10.41 to any port 443
pass  in quick proto tcp from 192.168.10.41 to any port 443
EOF
sudo cp /etc/pf.conf /etc/pf.conf.bak
printf '\nanchor "llmmac"\nload anchor "llmmac" from "/etc/pf.anchors/llmmac"\n' | sudo tee -a /etc/pf.conf
sudo pfctl -vnf /etc/pf.conf      # vérification de la syntaxe sans appliquer
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
sudo pfctl -a llmmac -sr

Attention à deux points. D'abord, une mise à jour majeure de macOS peut réécrire /etc/pf.conf : pensez à vérifier après chaque mise à jour. Ensuite, pf n'est pas forcément réactivé au redémarrage ; il faudra vérifier son état (sudo pfctl -s info) après un reboot et, si besoin, ajouter un LaunchDaemon qui exécute pfctl -e.

Création de l'ancre pf /etc/pf.anchors/llmmac Ajout de l'ancre à pf.conf, activation de pf et sudo pfctl -a llmmac -sr montrant les deux règles

Cliquez sur l'image pour l'agrandir.

Tests depuis la VM

On revient sur la VM prdlinage501. La CA est déjà dans le magasin de confiance depuis la partie 03, donc curl doit accepter le certificat sans option particulière. On teste sans jeton, puis avec :

dig +short llmmac.coolcorp.priv
curl -s -o /dev/null -w "%{http_code}\n" https://llmmac.coolcorp.priv/v1/models
read -rs LMS_TOKEN && export LMS_TOKEN
curl -s https://llmmac.coolcorp.priv/v1/models -H "Authorization: Bearer $LMS_TOKEN" | jq -r '.data[].id'

On attend 192.168.10.105, puis 401, puis qwen3.5-9b-mlx-8bit.

Tests curl depuis la VM vers llmmac.coolcorp.priv : 401 sans jeton, liste des modèles avec jeton

Même test depuis un conteneur du compte hermes, car c'est de là que Roger fera ses appels. On monte le certificat de la CA déposé dans la partie 03 et on vérifie simplement qu'on obtient bien un 401 sans jeton : cela prouve que le DNS, le chemin réseau et le TLS fonctionnent depuis un conteneur rootless, sans faire transiter de secret.

# Session hermes (sudo machinectl shell hermes@)
podman run --rm -v ~/vault/tls/ca.crt:/ca.crt:ro docker.io/library/alpine:3.22 sh -c \
  'apk add -q --no-progress curl && curl -s --cacert /ca.crt -o /dev/null -w "%{http_code}\n" https://llmmac.coolcorp.priv/v1/models'
Ouverture d'une session hermes avec machinectl Test depuis un conteneur rootless alpine du compte hermes : 401 sans jeton

Cliquez sur l'image pour l'agrandir.

Rangement du jeton dans Vault

Le jeton LM Studio a rempli son office pour les tests. On le range maintenant dans Vault, avec mon compte d'administration, puis on l'efface de la session :

vault login -method=userpass username=bvivi57
set +o history
vault kv put secret/hermes/roger/lmstudio_api_key value="$LMS_TOKEN"
unset LMS_TOKEN
set -o history
vault kv get -field=value secret/hermes/roger/lmstudio_api_key | wc -c

La dernière commande affiche la longueur du jeton, pas sa valeur : c'est suffisant pour vérifier qu'il a bien été enregistré.

vault login avec le compte d'administration bvivi57 (token masqué) vault kv put du jeton LM Studio sous secret/hermes/roger/lmstudio_api_key et vérification de sa longueur

Cliquez sur l'image pour l'agrandir.

GLM chez Z.ai

L'abonnement et la clé API

Pour le cloud, j'utilise l'offre GLM Coding Lite de Z.ai, en abonnement annuel (150 $ par an). Bonne surprise lors de la préparation de la série : Hermes Agent figure dans la liste officielle des outils pris en charge par cette offre, dans la catégorie des agents généralistes, en « best effort ». Comprenez : ça doit fonctionner, mais sans garantie, et les débits peuvent être limités aux heures chargées.

Je n’ai pas utilisé mon abonnement Cloud Anthropic car il n’est pas autorisé d’exploiter ce dernier avec des outils tiers…ou alors il faut payer des calls API en plus. J’ai choisi GLM, qui est également disponible en OpenWeight, pour sa bonne réputation, notamment dans le codage. Comme j’ai besoin de performance, je passe par l’offre hébergée ou le modèle est disponible avec bien plus de paramètres que je pourrais me permettre d’exécuter en local.

Depuis le 16 octobre 2026, Z.ai gère séparément les clés « Standard API » (paiement à l'usage) et les clés « Coding Plan », et c'est le type de clé qui détermine la facturation. Dans la page de gestion des clés (https://z.ai/manage-apikey), il faut donc créer une clé de type « Personal Coding Plan », et surtout pas une clé « Standard ». Une clé standard fonctionnerait aussi sur le point de terminaison du Coding Plan, mais elle serait facturée à l'usage sur le solde du compte, sans toucher au quota de l'abonnement. Le genre d'erreur que l'on découvre en fin de mois.

Console Z.ai, onglet Personal Coding Plan et création d'une clé API nommée roger

Cliquez sur l'image pour l'agrandir.

L'offre donne accès à deux modèles : glm-5.3, le modèle complet, qui raisonne systématiquement avant de répondre, et glm-5.3-flash, plus rapide et beaucoup moins gourmand en quota.

Comprendre les quotas

L'offre Lite ne compte pas en tokens, mais en crédits : 2 000 crédits par fenêtre glissante de cinq heures, et 10 000 par semaine. Le coût d'une requête dépend du modèle, avec des coefficients différents pour les tokens d'entrée, les tokens d'entrée en cache et les tokens de sortie..Et oui, souvent, le coût d’un token en entrée (l’analyse de la demande) n’est pas le même que le cout des token en sortie (la génération de la réponse)…et comme souvent, on retrouve du cache au milieu de tout ça…

Modèle Entrée Entrée en cache Sortie
glm-5.3 6,9 1,7 24
glm-5.3-flash 2,3 0,56 8

Le coût en crédits est la somme des tokens multipliés par leur coefficient, divisée par 10 000. Les heures creuses (hors 14 h-18 h heure de Pékin en semaine, soit 8 h-12 h à Paris en hiver) divisent la consommation par deux.

Il est temps de parler « tour (ou turn) ». Dans un usage d’agent, un « turn » désigne un aller-retour complet d’interaction. Chaque tour fait grossir le contexte, puisqu’on renvoie l’intégralité des tours précédents. Au bout d’un moment, la consommation de token est entièrement consommée par le rappel des aller-retour… sachant qu’une question peut déclencher plusieurs tours…

Faisons un calcul rapide pour Roger. Un tour d'agent typique, avec 20 000 tokens de contexte dont 90 % déjà en cache et 1 000 tokens de réponse, coûte environ (2 000 × 6,9 + 18 000 × 1,7 + 1 000 × 24) / 10 000 ≈ 6,8 crédits avec glm-5.3, et environ 2,3 avec la version Flash. Soit à peu près 300 tours avec glm-5.3 par fenêtre de cinq heures. C'est largement suffisant pour du raisonnement ponctuel, mais pas pour faire tourner Roger en continu sur le gros modèle. C'est ce qui justifie le modèle local par défaut.

Quand le quota est épuisé, la clé Coding Plan ne bascule pas sur un paiement à l'usage. Les requêtes échouent, tout simplement. D'où l'importance de garder le local comme modèle principal.

Point d'attention important : le calcul de consommation en IA est une vraie galère…c’est une des difficultés majeures que l’on rencontre aujourd’hui. Un même mot ne se traduit pas forcément par le même nombre de jetons d’un modèle à un autre. De plus, les providers appliquent des grilles tarifaires qui peuvent varier, le mode de distribution peut impacter les prix et vous arrivez souvent à des imbroglios de chiffres avec beaucoup de difficulté à anticiper les coûts. Encore une fois la solution réside dans les tests et les itérations, associé à une surveillance permanente des consommations et la mise en place de garde-fou en cas d’emballement. À cela s’ajoute le choix du bon modèle pour la bonne action…c’est un des enjeux clef à venir et on voit apparaitre de plus en plus de solutions de présélection de modèle fonction de la question posé pour router vers le modèle le plus adapté (et le plus économiquement) intéressant pour son besoin.

Test de la clé

Le point de terminaison à utiliser pour une clé Coding Plan est https://api.z.ai/api/coding/paas/v4, compatible avec l'API OpenAI. Utiliser le point de terminaison standard avec cette clé provoque une erreur de solde insuffisant. On teste depuis la VM, avec le modèle Flash pour économiser le quota :

set +o history
read -rs GLM_KEY && export GLM_KEY
curl -s https://api.z.ai/api/coding/paas/v4/chat/completions \
  -H "Authorization: Bearer $GLM_KEY" -H "Content-Type: application/json" \
  -d '{"model":"glm-5.3-flash","messages":[{"role":"user","content":"Réponds uniquement : OK Roger"}]}' \
  | jq -r '.choices[0].message.content, .usage'
Saisie masquée de la clé GLM dans la variable GLM_KEY, historique désactivé Réponse de glm-5.3-flash « OK Roger » avec le détail usage

Cliquez sur l'image pour l'agrandir.

Si la réponse est correcte, on range la clé dans Vault :

vault kv put secret/hermes/roger/glm_api_key value="$GLM_KEY"
unset GLM_KEY
set -o history
vault kv list secret/hermes/roger
Keys
----
glm_api_key
lmstudio_api_key
vault kv put de la clé GLM sous secret/hermes/roger/glm_api_key vault kv list secret/hermes/roger : glm_api_key et lmstudio_api_key

Cliquez sur l'image pour l'agrandir.

La stratégie de routage

Les deux cerveaux sont prêts. Voici comment Roger les utilisera, ce que l'on traduira dans sa configuration dans la partie 06 :

model:
  provider: lmstudio
  default: "qwen3.5-9b-mlx-8bit"
  base_url: "https://llmmac.coolcorp.priv/v1"
  context_length: 65536

fallback_providers:
  - provider: zai
    model: glm-5.3-flash

auxiliary:
  compression:
    provider: zai
    model: glm-5.3-flash
  • Par défaut, Roger utilise le modèle local. Les données restent chez moi.
  • En secours, si le Mac ne répond pas (éteins, modèle déchargé, NGINX arrêté), Hermes bascule automatiquement sur glm-5.3-flash.
  • Les tâches annexes de compression du contexte partent sur glm-5.3-flash, pour ne pas monopoliser le Mac.
  • À la demande, je pourrai basculer Roger sur glm-5.3 pour une analyse plus poussée, avec la commande /model directement dans Telegram.

Il n'y a pas de routage automatique « selon la difficulté » dans Hermes : le choix reste explicite (du moins à ma connaissance et au moment de l’écriture de l’article). Quand GLM est utilisé, le contexte de la conversation part chez Z.ai, y compris les réponses des outils. Roger aura une consigne pour le signaler.

La chaîne de protection du modèle local

Prenons un peu de recul sur ce que l'on vient de construire pour le modèle local. Une requête de Roger traverse désormais quatre contrôles successifs :

  1. Le pare-feu pf du Mac n'accepte les connexions sur le port 443 que depuis l'adresse de la VM.
  2. NGINX vérifie à son tour l'adresse source, termine le chiffrement TLS avec un certificat de la PKI interne, et refuse toute requête sans en-tête d'authentification.
  3. LM Studio vérifie le jeton, qui ne donne que les droits d'inférence et de liste des modèles.
  4. LM Studio n'écoute que localement : même en contournant NGINX, il est impossible de l'atteindre depuis le réseau.
Schéma de la chaîne de protection du modèle local : la requête de Roger depuis la VM 192.168.10.41 passe le pare-feu pf du Mac, puis NGINX qui vérifie l'adresse, termine le TLS et exige l'en-tête Authorization, puis LM Studio qui vérifie le jeton, LM Studio n'écoutant que sur 127.0.0.1:1234 ; une autre machine du LAN est bloquée par pf et ne peut pas joindre le port 1234

Cliquez sur l'image pour l'agrandir.

C’est déjà une bonne chaine de protection pour l’usage en interne, en tous cas déjà bien meilleur que ma première tentative avec OpenClaw. On pourrait se poser la question de la rotation de clefs API, mais, dans mon usage, je n’ai pas besoin d’aller jusque-là.

On archive

Les fichiers du Mac ne sont pas sur la VM, on les ajoute au dépôt à la main (sans clé ni certificat privé, évidemment) :

# Sur la VM, compte bvivi57, après avoir copié les fichiers du Mac dans ~/roger.coolcorp.priv/mac/
cd ~/roger.coolcorp.priv
ls mac/   # llmmac.conf  pf-llmmac  lms-load-roger.sh  tech.myprivatelab.lms-load-roger.plist  llmmac.cnf
git add mac/ && git commit -m "Partie 04 : LM Studio, NGINX et pf sur le Mac" && git push

Conclusion

Roger a désormais deux cerveaux. Le modèle local tourne sur le Mac avec un contexte de 64K, protégé par un jeton, du TLS et un pare-feu qui n'accepte que la VM. Le modèle cloud est prêt, avec une clé qui consomme bien le quota de l'abonnement. Les deux secrets sont dans Vault.

Le flux en clair vers le LLM, que je regrettais dans la série OpenClaw, appartient au passé. Cela demande un peu plus de travail, mais c'est le genre de détail qui fait la différence entre une démonstration et une installation durable qui pourrait être décliné en entreprise.

Cet article a été aussi l’occasion de mettre en avant bon nombre de notions et de principes importants à comprendre et à manipuler lorsqu’on se lance dans de l’IA, et particulièrement en local.

Le monde du hardware est en pleine ébullition au moment où je rédige de ce texte. Le coût des équipements IT explosent et choisir entre souveraineté et économie devient un enjeu majeur pour les entreprises qui adoptent l’IA. Pour l’instant, un seul setup, qu’il soit cloud ou local, ne peut répondre à tous les besoins (ou s’il le fait, c'est bien trop cher à l'échelle d'une société)) : les modèles sont nombreux, les implémentations aussi avec un écosystème en changement permanent. Pour ceux qui craignent pour certains emplois, sachez que pour moi, l’agentique, s’il rebat les cartes de nombreux métiers, il est aussi en train de générer bon nombre de nouvelles expertises. Je ne sais pas comment tout cela va évoluer, mais, en l’état, il y a encore beaucoup de travail !

Dans le prochain article, on préparera tout ce que Roger va surveiller et utiliser : le compte Xen Orchestra et son token, le bot Telegram et le vault Obsidian. Article à venir.