Hermes - Partie 04 : Les cerveaux de Roger, LM Studio sur Mac et GLM (Z.ai)
- 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.
Cliquez sur l'image pour l'agrandir.
Au premier lancement, on active les réglages avancés dans Settings > Developer.
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
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…
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
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 😊
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
À 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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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 :
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 ».
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é |
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 :
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.
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
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.
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
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
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.
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 :
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.
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"
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
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.
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.
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'
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é.
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.
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.
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'
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
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.3pour une analyse plus poussée, avec la commande/modeldirectement 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 :
- Le pare-feu pf du Mac n'accepte les connexions sur le port 443 que depuis l'adresse de la VM.
- 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.
- LM Studio vérifie le jeton, qui ne donne que les droits d'inférence et de liste des modèles.
- LM Studio n'écoute que localement : même en contournant NGINX, il est impossible de l'atteindre depuis le réseau.
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.