Journal Auto-héberger ses LLM (IA) : Interfaces utilisateur

5
18
août
2026

Sommaire

Introduction

Ce journal est dans le prolongement de mes précédents journaux :

On a bien parlé théorie, on a parlé matériel, vous avez vidé notre PEE/PEL, vous avez construit votre radiateur électrique intelligent, vous avez maintenant un LLM optimisé aux petits oignons. On arrive donc sur la partie la plus fun : s'en servir. Autrement dit, on va coller des interfaces utilisateur (frontends) devant notre moteur d'inférence pour s'en servir pour de vrai.

Il faut quand même que je vous prévienne : on est là sur un domaine encore très récent. Autrement dit, ce journal sera probablement obsolète d'ici moins d'un an 😭.

Généralités

Il faut d'abord poser quelques bases (oui, encore 😑).

Vocabulaire

Comme à mon habitude, on va d'abord poser quelques définitions. Mais pour ce journal, donner des termes et définitions est un peu plus compliqué que pour les précédents : le domaine étant récent, les termes et leurs définitions sont encore souvent sujets à débat.

Voici donc quelques vulgarités que je vais utiliser pas mal dans ce journal, avec les définitions que je leur connais :

  • Agent : un LLM outillé (désigne parfois un modèle, un prompt et un jeu d'outils ; d'autres fois, une instance d'un LLM outillé) ;
  • Assistant : voir « agent » ;
  • Sous-agent : un agent, mais instancié et piloté par un agent parent. Cet agent ne parle pas à l'utilisateur. Il parle uniquement à son agent parent. Ça a l'air inutilement sophistiqué au premier abord, mais on verra plus loin que ça a des utilités concrètes.
  • Interface utilisateur (frontend) : Interfaces graphiques (GUI) ou terminal (TUI / CLI) permettant à un utilisateur de discuter avec un agent. Exemples : Open WebUI et OpenCode ;
  • Harnais d'agent : une infrastructure logicielle qui entoure un LLM et son moteur d'inférence, pour l'outiller et lui permettre d'agir de manière globalement autonome. L'idée est de s'abstraire du LLM : on ne discute plus avec le LLM. On lui confit une mission. Un harnais peut inclure une interface utilisateur. Exemples: OpenClaw et Hermes Agent.

Interfaces utilisateur et harnais

Pour ma part, je suis encore très sceptique quant à l'utilité réelle des harnais. De plus, j'ai beaucoup de mal avec l'idée de laisser autant d'autonomie à des modèles probabilistes qui ont autant de jugeote qu'un caillou. Espérer qu'ils feront toujours la bonne chose tout seuls me semble très optimiste.

J'ai toujours préféré l'idée que les humains et les IA travaillent ensembles. Autrement dit, que l'IA soit juste un outil pour les humains. Cela permet de palier les défauts des uns et des autres.

Ce journal traitera donc exclusivement des interfaces utilisateur. Ceci dit, les principes qui s'appliquent aux interfaces utilisateur s'appliquent aussi aux harnais.

Moteur d'inférence et API

Pour faire tourner un LLM, il faut un moteur d'inférence. Pour ce journal, je vais supposer que vous avez opté comme moi pour llama-swap et llama-server. Si vous avez opté pour un autre moteur, pas d'inquiétude : tous les moteurs que je connais exposent une API compatible avec celle de OpenAI. Exemples :

  • Ollama : http://\<votre serveur\>:11434/v1
  • llama-swap / llama-server : http://\<votre serveur\>:8080/v1
  • openrouter.ai : https://openrouter.ai/api/v1

Modèles

Pour ce journal, je pars du principe que vous avez mis la main sur un LLM entrainé à l'utilisation des outils (tools / tooling), et de préférence, capable de voir (vision). Quelques exemples :

  • Qwen 3.8
  • Gemma 4
  • Ministral 3
  • Devstral-small 2

llama-swap intègre une interface web qui permet notamment de tester vite fait llama-server et le modèle. Si vous lui dites « kikoo » et qu'il vous fait une crise existentielle en retour, c'est qu'on est bons.

Affinage des prompts

Le fameux prompt engineering dont on a sûrement tous entendu parler. Il a horrifié beaucoup de monde par son utilisation clairement abusive du mot « ingénierie » … et il nous a bien fait rire (« et surtout, ne fais pas de bugs ! » 🤣).

Sur une note plus sérieuse, ce concept était pertinent à l'époque où les modèles étaient moins bien « alignés ». Toutefois, malgré l'amélioration des modèles, il garde un fond de pertinence : un prompt clair, structuré et suffisamment détaillé donnera toujours de meilleurs résultats qu'un prompt vague et bâclé. Et ironiquement, si vous voulez faire un prompt décent pour un agent/modèle/vache, le plus simple, c'est de demander à un LLM de le générer 😁. Ça a d'ailleurs un nom : le meta-prompting.

Outils

Si on veut parler d'interfaces utilisateur pour LLM, on doit d'abord parler des outils pour les LLM. Parce qu'un LLM sans outil, c'est comme une réunion de plus de 10 personnes au travail : ça parle beaucoup, mais finalement personne n'agit.

Principe

Outils en action

Les outils viennent de l'interface utilisateur, jamais du moteur d'inférence. Par contre, le moteur d'inférence se charge de leur formatage et de leur délimitation pour le LLM.

Pour outiller un LLM, c'est simple : l'interface utilisateur fournit dès le début de la conversation un manifeste avec la liste des outils, leurs descriptions, et les arguments attendus. Cette liste est injectée dans le contexte du LLM dès le début. Le LLM peut ensuite émettre des messages dans un format précis pour les appeler. Leur retour sera injecté dans le contexte dans ce même format.

Le format utilisé par le LLM est généralement du JSON, parfois du XML, le tout entouré dans des délimiteurs (ex: <|tool_call|>{"name": "kill-all-humans"}<|tool_all_end|>).

Comme tous les LLM n'utilisent pas le même format (JSON ou XML) ou les mêmes délimiteurs, le moteur d'inférence se charge de faire la traduction. Si par exemple vous utilisez l'API OpenAI, l'interface utilisateur utilisera le format JSON. Si le LLM préfère travailler en XML (rare), le moteur d'inférence se chargera de la conversion, et il rajoutera les délimiteurs autour.

Outils intégrés à l'interface utilisateur

En premier lieu, bien entendu, il y a les outils intégrés directement à votre interface utilisateur. Ça peut être en dur dans l'interface (comme l'interpréteur de code de Open WebUI par exemple), tout comme il peut s'agir de greffons.

Les serveurs MCP

En 2024, Anthropic a créé le protocole MCP. Il permet à un serveur d'exposer des outils à des LLM.

Dans le cadre de ce protocole, une interface utilisateur compatible MCP est appelée « un client MCP ». On connecte l'interface utilisateur au serveur MCP. Un LLM avec en façade une telle interface peut interroger, via l'interface, les serveurs MCP auxquels l'interface est connectée, comme s'il utilisait n'importe quel autre outil.

Ce protocole est indépendant du protocole de transport sous-jacent, mais typiquement, vous trouverez du MCP soit en réseau par dessus HTTP, soit en local par stdin/stdout.

Le gros avantage de ce protocole est évidemment la standardisation : différents types d'interfaces utilisateur peuvent utiliser un même serveur MCP, pour mettre à disposition de leurs LLM les mêmes outils.

Exa

Il est assez pratique de permettre à son LLM de faire des recherches sur Internet. Malheureusement, les résultats Google ne sont pas très lisibles pour les LLM. Heureusement, il existe donc des moteurs de recherche spécialement pour les LLM.

Le plus connu actuellement est Exa. Il est possible de les utiliser via un serveur MCP gratuit, ou alors, via leur API. Dans ce dernier cas, il faudra s'inscrire chez eux pour obtenir une clé d'API (gratuite). Et comme pour tous les services cloud, j'attends avec impatience leur phase de merdification. Ça promet d'être drôle 💩.

Comprendre, débugguer et optimiser les outils

À noter que toutes les interfaces utilisateur que je connais cachent le manifeste. Et ça me rend triste 😢, parce que, comme on l'a vu dans mon précédent journal, ce manifeste impacte beaucoup le temps nécessaire pour démarrer une conversation 🐌. Il faut donc garder à l'esprit que chaque outil que vous activez va augmenter le temps nécessaire pour débuter une conversation, à cause du manifeste.

Elles cachent aussi souvent le détail des appels aux outils.

La bonne nouvelle, c'est que pour avoir ce manifeste ou les détails, le plus simple est de les demander gentiment au LLM 😁.

Exemple de manifeste pour les outils

La taille du contexte

Quand on travaille avec des LLM, la taille du contexte, c'est l'ennemi à abattre à tout prix. Un contexte trop grand amène deux problèmes :

  • La qualité des réponses des LLM se dégrade généralement avec la taille du contexte. Pour cette raison, ça peut avoir du sens, par exemple, de n'utiliser qu'un contexte max de 128K avec un modèle qui pourrait supporter 256K. Ça laisse aussi de la marge pour la compression du contexte (expliquée ci-après).
  • Si vous dépassez la taille du contexte maximum du modèle, au lieu d'avoir une belle réponse, le comportement va dépendre de votre moteur d'inférence. En règle générale, vous recevrez une insulte sous forme d'erreur.

Pour cette raison, les interfaces utilisateur intègrent maintenant presque toutes des mécanismes pour palier ce problème.

La compression du contexte

C'est l'approche la plus courante. Elle est plutôt évidente : quand l'interface voit qu'on s'approche de la taille maximum du contexte (typiquement vers ~80% de la taille max), elle demande au LLM de résumer tout ce qui s'est dit jusque-là, et elle reprend à partir de ce résumé. Et son principal défaut est tout aussi évident : on perd de l'information à chaque fois.

La qualité du résumé va dépendre du niveau d'intelligence de votre LLM. Mais même avec un LLM très intelligent, des informations jugées peu importantes par le LLM seront perdues. Et c'est là qu'on se retrouve à réexpliquer des choses constamment au LLM. Pire, il arrive que le LLM résume mal, et oublie complètement ce qu'il était en train de faire. J'ai déjà vu un LLM mal résumer son contexte, et reprendre son travail plusieurs étapes en arrière 😭.

Il y a des plugins OpenCode qui proposent des variantes de cette idée.

Le problème : Les API OpenAI remontent généralement à l'interface utilisateur le nombre de tokens actuellement dans le contexte, mais pas la taille maximum du contexte. Et chaque LLM a son propre tokenizer, et donc sa propre façon de découper les mots en tokens. Résultat : les interfaces utilisateur doivent connaitre à l'avance la taille maximum du contexte.

Les sous-agents

Les sous-agents sont en fait un bon moyen de compenser partiellement le problème de la taille du contexte.

L'idée de base est de mettre à disposition d'un agent des outils pour qu'il puisse instancier lui-même des sous-agents. Ces sous-agents ont leur propre contexte, et ont aussi accès à des outils. L'agent parent leur donne une mission, et les sous-agents lui transmettent en retour juste un message final quand ils ont fini.

Par exemple, vous pouvez créer un agent dont le rôle est celui d'un gestionnaire de projet : il découpe le problème en sous-tâches et délègue chaque tâche à des sous-agents, avec leurs propres contextes (1 sous-agent pour chaque tâche). Dans ce cas, le contexte du gestionnaire de projet est nettement plus modeste en taille et concentré sur l'essentiel. Ses subordonnés, eux, peuvent lire, écrire et raconter leur vie autant qu'ils veulent ; ils sont de toute façon très éphémères.

Dans le cas où vous utiliseriez un LLM cloud, vous pouvez aussi lui demander de déléguer à des sous-agents moins chers (cloud ou auto-hébergés) pour faire des économies.

(tout ça commence à avoir une bonne odeur de critique sociale, non ? 😁)

Génération Augmentée par Récupération (RAG)

Dans une discussion avec un LLM, quand vous y attachez un document, on pourrait naïvement s'imaginer que l'entièreté du document est injecté dans le contexte. Mais cela ne pourrait pas fonctionner quand le document est plus grand que la taille maximum de contexte. Dans la même logique, comment permettre à un LLM d'avoir des informations venant d'un grand corpus de documents sans exploser son contexte ?

Une solution classique est la génération augmentée par récupération (Retrival-Augmented Generation ; RAG). Par exemple, c'est l'approche prise par Open WebUI.

Derrière ce nom barbare se cachent en fait une idée assez simple : avant même de consulter le LLM, on cherche les documents en rapport avec la demande de l'utilisateur. Puis on injecte les résultats de la recherche dans le contexte du LLM, en même temps que la requête de l'utilisateur.

La RAG se fait en suivant les étapes suivantes :

  1. l'utilisateur soumet son prompt ;
  2. l'interface (pas le LLM !) cherche les documents en rapport avec le prompt de l'utilisateur (recherche vectorielle ; pas par mot-clé+index !) ;
  3. l'interface (pas le LLM !) crée des extraits choisis (snippets) à partir de ces documents et du prompt de l'utilisateur ;
  4. l'interface injecte ces extraits dans le contexte du LLM ;
  5. l'interface rajoute le prompt utilisateur dans le contexte du LLM ;
  6. l'interface envoie tout ça au LLM et affiche sa réponse.

Sur un malentendu, ça peut permettre au LLM de donner des réponses pertinentes basées sur les documents fournis (ou pas).

Il existe une variante du RAG qui est le RAG agentique : l'agent appelle lui-même un outil avec la recherche à faire.

Les détails de la RAG sortent du cadre de ce journal, donc je ne vais pas la détailler plus. Il faut juste garder à l'esprit qu'avec la RAG, le LLM ne voit qu'un extrait du ou des document(s) fourni(s).

Consultation de documents via les outils

Une autre approche est de donner au LLM des outils pour lire les fichiers comme il l'entend. C'est l'approche généralement prise par les interfaces utilisateur de programmation comme OpenCode.

Par exemple, Opencode donne au LLM un outil "read", qui prend en argument le chemin du fichier, et qui, par défaut, ne renvoie que les 2000 premières lignes du fichier.

L'anglais

Sans surprise, l'anglais utilise moins de tokens que la plupart des autres langues. On peut donc économiser des tokens en communiquant en anglais.

Caveman

Caveman est un ensemble de skills/plugins/whateveryouwanttocallitnowadays. L'idée est de faire parler le LLM comme un homme des cavernes. Cela économise pas mal de tokens, et fait donc durer plus longtemps le contexte.

Le résultat est cependant sujet à débat :

Son créateur annonce en moyenne une économie de tokens de l'ordre de 65% pour les discussions, et 8 à 20% pour le code. D'autres personnes disent avoir seulement environ 15% de réduction, voir même avoir une légère augmentation dans certains cas limites.

Se pose aussi la question de l'impact sur la capacité de raisonnement. Il y a peu d'études spécifiques à caveman. La seule que j'ai trouvé est cavewoman, qui ne trouve pas de changement significatif. Cependant, il y a au moins une autre étude (non-spécifique à caveman) qui suggère que contraindre le format de sortie du LLM peut dégrader sa capacité de raisonnement de façon importante.

Les interfaces utilisateur

Actuellement, les interfaces utilisateur et les harnais d'agent, il y en a une trentaine qui sont vibe codés tous les jours par des mecs persuadés d'être des génies incompris (mais qui ne l'est pas ? 🤔). /r/localllama, /r/ollama et LinuxFr (et par extension mon lecteur de flux RSS) en font d'ailleurs régulièrement les frais. Il va donc de soi que ce journal ne va pas être exhaustif. Je vais me concentrer sur ceux avec lesquels j'ai une réelle expérience. J'exclus donc par exemple SillyTavern que je n'utilise que de façon anecdotique.

Aussi, il ne s'agit pas pour moi de reprendre la documentation de chaque projet. Juste faire un tour rapide de principales fonctionnalités, et, s'il y en a, de mentionner quelques pièges auxquels il faut faire attention.

Open WebUI

Présentation

Quand on parle de LLM, le premier truc auquel on pense, c'est l'interface web à la chat.mistral.ai (par exemple) pour discuter avec un LLM. Et Open WebUI est exactement ce genre d'interface utilisateur, mais nourrie aux hormones de croissance.

Exemple d'utilisation typique d'un LLM

Sa plus grande qualité est que vous pouvez y créer un compte pour votre grand-mère de 85 ans, et elle arrivera à s'en servir (… peut-être).

Son plus grand défaut est d'être arrivé trop tôt : pour certaines choses, on devine encore l'époque où les LLM avaient des contextes rikiki, et étaient encore aussi fiables que Windows Me.

Controverse : la licence

La licence d'Open WebUI est sujette à controverse. Ils y ont inclus une clause de protection de leur marque. C'est d'autant plus discutable qu'il n'y a que des mots anglais simples dans "Open Web User Interface", et que ça décrit très mal ce que ça fait. Mais bon, on a bien une entreprise américaine qui a obtenu la protection légale du mot "fenêtres", donc on n'est plus à ça près 🤷‍♂️.

Installation et Configuration

Une des qualités de Open WebUI est sa facilité de déploiement. Là où LibreChat nécessite 2 bases de données (MongoDB et VectorDB), Open WebUI démarre par défaut avec juste une base de données Sqlite. Pour une installation mono-utilisateur ou familiale, c'est très largement suffisant. Personnellement, j'ai choisi Open WebUI au lieu de LibreChat pour mon installation personnelle à cause de ça.

À la première connexion, il va vous proposer de créer un compte administrateur. Les connexions aux moteurs d'inférence se configurent ensuite simplement dans l'interface web. Bref, impossible de faire plus simple que ça.

Attention au RAG !

En terme d'utilisabilité, c'est probablement un des plus gros pièges 🪤 de Open WebUI. Et il est d'autant plus sournois qu'il impacte directement les utilisateurs finaux, et que vous ne pouvez pas changer ce comportement.

Quand vous ajoutez des documents à une conversation Open WebUI, Open WebUI applique la stratégie RAG dessus. Autrement dit, à chaque fois que vous saisissez un prompt, il ne transmet à votre LLM que les extraits des documents qu'il estime être pertinents.

Cette stratégie est pertinente quand on fournit de grands documents, ou beaucoup de documents. À l'opposé, elle est très problématique quand vous voulez faire relire entièrement votre prochain journal LinuxFr à votre LLM préféré 😬.

Plus sournois, le LLM ne sait pas s'il a juste un extrait ou tout le document. Il vous répond donc avec tout l'aplomb d'un idiot qui croit savoir, sans avoir la moitié des informations (certains diraient que c'est une de leurs spécialités de toute façon 😑).

Ce problème peut être contourné en activant l'interpréteur de code (décris plus loin). Dans ce cas, vous pouvez téléverser des fichiers pour qu'ils soient accessibles à l'interpréteur et au LLM (quand l'interpréteur est activé: icône tout en haut à droite -> fichiers).

La génération d'image et l'édition d'image

Vous pouvez permettre à votre LLM de générer des images dans Open WebUI. Par exemple, ça peut être pratique pour illustrer vos plans de conquête du monde 👿.

Exemple d'utilisation typique de LLM et de génération d'images

Pour cela, il vous faut un moteur d'inférence pour des IA de génération d'images. Il doit exposer une API compatible OpenAI, ComfyUI ou Automatic1111. Par exemple, vous pouvez utiliser llama-swap et stable-diffusion.cpp.

On ne va pas se mentir, trouver une combinaison modèle + configuration qui marche bien est difficile. Vous trouverez un exemple de configuration testée et validée plus loin dans ce journal.

Une question légitime qui revient souvent est « à quoi ça sert ? ».

Bon, bien entendu, déjà, ça sert surtout à faire des illustrations avec le cul, pour des posts LinkedIn le plus souvent aussi creux qu'égocentriques. Ça sert aussi à transformer vos enfants en personnages de manga.

Mais ça peut servir à des choses utiles ! Par exemple, tout comme le vibe coding peut permettre de prototyper des programmes sans savoir coder, avec juste un crayon, l'édition d'images peut permettre de prototyper des illustrations.

La recherche sur Internet

Comme mentionné précédemment, vous pouvez obtenir une clé d'API Exa gratuite (ou utiliser leur serveur MCP). Pour le reste, c'est trivial à configurer dans Open WebUI.

À utiliser, c'est un autre problème : la taille du contexte est activement en train d'essayer de nous tuer. Pour le coup, Open WebUI n'utilise visiblement pas l'approche RAG. Elle balance juste toute la sortie des résultats au LLM. Du coup, si vous autorisez Open WebUI à fournir trop de résultats au LLM, vous obtiendrez régulièrement ce que je surnomme affectueusement des pets de cerveaux : Le LLM ne vous répond en apparence rien. Et si vous regardez les logs llama-server, vous aurez :

error: request (190500 tokens) exceeds the available context size (163840 tokens), try increasing it

Pas pratique … La seule solution que je connaisse est de réduire le nombre de résultats. Pour référence, 2 résultats pour 128K de contexte passent généralement bien.

Nextcloud-Mcp-server : mails, agenda, …

Précédemment, je vous parlais du protocole MCP. Un exemple de serveur MCP est cbcoutinho/nextcloud-mcp-server. Il permet à un LLM d'accéder aux contenus de différentes applications Nextcloud, dont l'agenda, les contacts, les mails, etc.

Dans un docker-compose.yml, ça donne par exemple ça :

services:
  mcp:
    image: ghcr.io/cbcoutinho/nextcloud-mcp-server:latest
    ports:
      - "8000:8000"
    environment:
      - "NEXTCLOUD_HOST=https://votre.nextcloud"
      - "NEXTCLOUD_PUBLIC_ISSUER_URL=https://votre.nextcloud"
      - MCP_DEPLOYMENT_MODE=multi_user_basic
    volumes:
      - mcp-oauth-storage:/app/.oauth
    restart: unless-stopped
    command:
      - "--enable-app"
      - "notes"
      - "--enable-app"
      - "calendar"
      - "--enable-app"
      - "webdav"
      - "--enable-app"
      - "contacts"
      - "--enable-app"
      - "mail"

volumes:
  mcp-oauth-storage:

À partir de là, vous pouvez facilement l'ajouter dans l'interface web de Open WebUI.

Quant à la question récurrente « à quoi ça sert ? », je peux vous donner un exemple que j'ai eu récemment :

Je vais donner des cours en école d'ingénieurs dans quelques mois. On m'a transmis la liste de mes séances de cours, TD et TP, soit 33 séances, sous forme de deux emails, avec dans chacun, un simple tableau HTML. Pour chaque séance, il fallait que je vérifie que ça ne tombe pas sur un de mes jours de travail chez mon client, que ça ne rentre pas en conflit avec autre chose, et il fallait que je les ajoute à mon agenda. C'était un problème très ponctuel, donc ce n'était clairement pas rentable pour moi d'écrire un script pour le résoudre. À ce stade, vous devinez forcément que j'ai jeté un LLM sur le problème (ノ^o^)ノ彡🤖 📧🗓️. Je lui ai donné accès à mes mails, mon agenda, je lui ai dis quand je travaille chez mon client normalement, et pouf, magie 🪄🗓️ ! L'agent a d'ailleurs repéré tout de suite un conflit sur une des semaines.

L'interpréteur Python

Là, on touche à une des fonctionnalités qui, à mes yeux, fait briller Open WebUI : son interpréteur Python intégré.

Open WebUI a été construit sur Pyodide : un interpréteur Python fonctionnant par dessus Web Assembly. Autrement dit, la seule partie dans Open WebUI en Javascript est pour faire les bindings entre le DOM HTML et Pyodide.

Cette utilisation de Pyodide a ouvert une possibilité : Mettre à disposition du LLM un interpréteur Python éphémère. Cet interpréteur s'exécute dans votre navigateur. Il est même possible d'installer des paquets supplémentaires dedans grâce à micropip.

Le premier avantage est que cet interpréteur pallie à un défaut connu des LLM : le calcul. Au lieu de faire des calculs de tête (et de se planter), le LLM peut utiliser l'interpréteur Python pour ses calculs.

Une autre utilisation est la création de documents. Par exemple, en utilisant l'interpréteur, le LLM peut créer des graphiques :

Exemple de génération de graphique utilisant l'interpréteur Python intégré

Open Terminal

Open WebUI permet aussi à l'agent d'avoir accès à un terminal. Chez Open WebUI, ils ont visiblement du mal pour le nommage de leurs projets (ou alors ils sont fâchés avec leur indexation sur les moteurs de recherche ? 🤔), donc ils ont appelé ce terminal « Open Terminal ».

Ce terminal expose une interface HTTP avec laquelle Open WebUI et votre LLM peuvent s'interfacer. Ce terminal peut tourner dans un conteneur Docker ou en tant qu'utilisateur classique sur n'importe quel système.

Le défaut majeur de cette approche, c'est qu'Open Terminal tourne en tant que serveur, et non en tant que client Open WebUI. Autrement dit, c'est Open WebUI qui vient interroger le serveur Open Terminal. Je présume que c'était plus simple pour eux de l'implémenter comme ça, mais cela peut compliquer passablement son déploiement et son utilisation : En plus d'exposer une machine hôte et un port pour Open WebUI, il faut aussi exposer la machine qui fait tourner Open Terminal et un port dessus.

Agents

Open WebUI vous permet de créer des agents. Chaque agent est en fait un combo modèle + prompt + outils.

Dans le cadre d'OpenWebUI, cela est surtout pratique pour définir le comportement attendu de votre LLM, grâce à un prompt sur mesure.

OpenCode

Présentation

Toujours dans la série des projets en panne d'imagination pour leurs noms, il y a OpenCode. OpenCode est une interface utilisateur à destination des programmeurs. Son objectif premier est clairement le vibe coding. Mais moyennant un petit peu de configuration, on peut facilement ramener OpenCode sur le droit chemin et l'utiliser pour des objectifs bien plus respectables.

OpenCode existe sous différentes formes : TUI, plugin Visual Studio Code, interface web, etc.
Ici, je parlerai uniquement de celle que les vrais poilus utilisent, à savoir l'interface TUI. Cette interface a un avantage notable : elle marche aussi bien en local que par SSH.

OpenCode

Installation et Configuration

Son installation est très simple : une commande et pouf.

Sa configuration l'est un peu moins 😑.

Par défaut, le seul fournisseur de LLM configuré est OpenCode Zen. Il faut donc rajouter votre moteur d'inférence, et les modèles que vous souhaitez utiliser.

Exemple de configuration .config/opencode/opencode.json pour l'utiliser avec llama-swap :

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "votre-llama-swap": {
      "name": "votre-llama-swap",
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "baseURL": "http://votre-llama-swap.votre-domaine:8123/v1"
      },
      "models": {
        "qwen3.6-35b-q4": { // doit correspondre au nom dans votre config llama-swap
          "name": "qwen3.6-35b-q4",
          "attachment": true,
          "modalities": { "input": ["text", "image"], "output": ["text"] },
          "limit": {
            "context": 100000,
            "output": 65536
          }
        },
        "gemma4-26b-q4": { // doit correspondre au nom dans votre config llama-swap
          "name": "gemma4-26b-q4",
          "attachment": true,
          "modalities": { "input": ["text", "image"], "output": ["text"] },
          "limit": {
            "context": 100000,
            "output": 65536
          }
        }
      }
    }
  }
}

Vous remarquerez que j'indique pour chaque modèle la taille maximum du contexte. En effet, OpenCode a besoin de connaitre la taille maximum du contexte, pour savoir quand déclencher une compression du-dit contexte. Pour ça, il se base sur une base de données de fournisseurs cloud. Comme on auto-héberge, on doit lui donner nous-même.

Si vous voulez utiliser OpenRouter.ai, vous pouvez utiliser simplement la commande /connect et fournir votre clé d'API à OpenCode. Il faut aussi savoir que certains LLM de OpenRouter.ai ne sont pas catalogués sur models.dev. Par exemple, si vous voulez utiliser qwen3.6 27b, il faut l'ajouter explicitement dans la section "provider" :

  "provider": {
    "openrouter": {
      "models": {
        "qwen/qwen3-coder-next": {
          "name": "qwen/qwen3-coder-next"
        },
        "qwen/qwen3.6-27b": {
          "name": "qwen/qwen3.6-27b"
        }
      }
    }
  }

Désactivation des snapshots

OpenCode cible avant tout le vibe coding. Et quand on vibe code, il n'est pas rare qu'on se trompe en donnant une instruction au LLM et que le LLM jardine tout votre projet en conséquence 🧑‍🌾, ou que le LLM parte en sucette spontanément 🍭. Vous pouvez alors cliquer sur le prompt à l'origine de votre apocalypse locale et OpenCode vous permettra de le modifier et de le relancer rapidement. Par la même occasion, il vous proposera de restaurer vos fichiers dans l'état où ils étaient au moment de ce prompt.

Cela implique que OpenCode garde des snapshots de tous vos fichiers à chaque prompt. En fonction de votre projet, cela peut prendre beaucoup de place, et ce n'est pas du goût de tout le monde. Mais surtout, si vous êtes quelqu'un de raisonnable et donc que vous ne vibe codez pas ou très peu, cette fonctionnalité risque de vous être activement nuisible. Par exemple, si vous vous servez uniquement de OpenCode pour relire vos changements, cette fonctionnalité peut annuler vos changements !

Heureusement, il y a une petite option magique pour désactiver les snapshots. Ajoutez juste dans .config/opencode/opencode.json la ligne suivante :

  "snapshot": false,

Outils intégrés

Il y a beaucoup d'outils intégrés à OpenCode. Je ne vais pas reprendre la liste, que vous pouvez de toute façon trouver dans la documentation. Je vais juste mentionner quelques outils :

  • edit et write : écriture de fichiers ;
  • read : lecture de bouts de fichiers ;
  • grep : bon celui-là, tous les pinguouins devinent ;
  • glob : recherche de fichier par pattern (genre find) ;
  • bash : commande shells ;
  • webfetch : permet de faire des requête HTTP ;
  • websearch : recherche sur le web ;
  • todowrite : manipulation de la TODO liste de la session ;
  • task : invocation de sous-agents.

Les permissions par défaut des outils peuvent être définies globalement dans ~/.config/opencode/opencode.json, et elles peuvent être affinées pour chaque agent (voir plus loin).

La liste de choses à faire

La fameuse « TODO list ». Vous noterez qu'il y a un todowrite, mais pas de todoread. Donc ne étonnez pas si votre agent met quelque chose dans la liste, que la compression de contexte passe par là, et qu'il ne le retire jamais de la liste 😑.

Recherche sur Internet

En mettant la variable d'environnement OPENCODE_ENABLE_EXA à 1, et en autorisant l'outil websearch, votre LLM pourra faire des recherches sur Internet.

Attention toutefois à la taille du contexte : Les résultats des recherches peuvent le faire gonfler de façon très importante.

Les agents

Il s'agit essentiellement de combo prompt+permissions.

Anciennement, ils étaient appelés "modes", ce qui, sans surprise, prêtait clairement à confusions.

Voici un exemple d'agent que j'utilise régulièrement pour mes revues de code (~/.config/opencode/agents/assist-read-only.md ; l'original est en fait en anglais) :

---
temperature: 0.3
permission:
  questions: "allow"
  write: "ask"
  edit: "ask"
  grep: "allow"
  glob: "allow"
  read: "allow"
  bash:
    "*": "ask"
    "cat *": "allow"
    "date *": "allow"
    "echo *": "allow"
    "false *": "allow"
    "false": "allow"
    "find *": "allow"
    "git *": "allow"
    "git commit *": "ask"
    "git commit": "ask"
    "git log *": "allow"
    "git log": "allow"
    "git push *": "deny"
    "git push": "deny"
    "grep *": "allow"
    "head *": "allow"
    "hexdump *": "allow"
    "ls *": "allow"
    "ls": "allow"
    "make *": "allow"
    "pwd *": "allow"
    "pwd": "allow"
    "rg *": "allow"
    "sed *": "allow"
    "sort *": "allow"
    "sort": "allow"
    "tail *": "allow"
    "task *": "allow"
    "true": "allow"
    "wc *": "allow"
    "wc": "allow"
  skill: "deny"
  lsp: "deny"
  webfetch: "ask"
  websearch: "allow"
---
Rôle : Relecteur de code d'élite.
Objectif : Atteindre une qualité de travail sans compromis. Ne rien laisser au hasard.
Ton : Sois franc, direct et d'une honnêteté brutale. Priorise la précision technique sur la politesse ; ne te soucie pas de l'ego de l'utilisateur.
Vérification : Lors de la validation d'une base de code, privilégie l'utilisation des outils d'automatisation établis du projet (ex: make check, make test, task check, task test) plutôt que d'invoquer directement des outils de bas niveau tels que flake8 ou pytest, sauf demande spécifique.
Contexte : « Modifications » fait référence aux modifications non commitées dans le répertoire de travail. S'il n'y a pas de modifications non commitées, cela fait référence aux commits locaux qui n'ont pas encore été poussés sur le dépôt distant. Tu dois réévaluer ces modifications fréquemment au fur et à mesure que l'utilisateur développe.
Précision : N'invente jamais de faits. Si tu as un doute ou si tu manques d'informations : effectue une recherche sur le Web, demande des clarifications à l'utilisateur, ou déclare explicitement que tu ne sais pas.
Contraintes :

    Il t'est strictement interdit d'écrire, de modifier ou d'implémenter tout fichier ou code. Ton rôle est de réviser le travail de l'utilisateur et de fournir des retours destinés à améliorer l'implémentation et les compétences en codage de l'utilisateur.
    Aucune question de suivi.
    Langue et formalité : Si l'utilisateur parle français, tu dois répondre en français. Tu dois utiliser le « vous » (vouvoiement) pour t'adresser à l'utilisateur, peu importe si l'utilisateur utilise le « tu » (tutoiement).
    Si tu utilises des sous-agents, n'en utilise qu'un à la fois. Le type de sous-agent doit être `assist-read-only`.

Si les agents intégrés de base dans OpenCode ne vous intéressent pas, vous pouvez les désactiver en ajoutant la section suivante dans votre .config/opencode/opencode.json :

  "default_agent": "votre-super-agent-llm",
  "agent": {
    "build": {
      "disable": true
    },
    "explore": {
      "disable": true
    },
    "general": {
      "disable": true
    },
    "plan": {
      "disable": true
    },
    "review": {
      "disable": true
    }
  }

Problème important qu'il faut connaitre : lors des compressions de contexte, le LLM doit résumer le contexte, mais il ne pense pas forcément à reprendre son prompt système dans le résumé. Et visiblement OpenCode ne lui réinjecte pas. Il arrive donc qu'il oublie des instructions.

Les sous-agents

Vos agents peuvent invoquer des sous-agents.

Comme mentionné précédemment, pour gérer les tailles de contexte au mieux, il peut alors devenir intéressant de se créer un agent "manager" : il n'a ni le droit de lire, ni d'écrire. Il ne peut que déléguer des tâches à des subordonnés, qui eux ont le droit de lire et écrire.

À savoir : La commande « task » qui sert à l'agent pour invoquer des sous-agents. Elle prend en argument le type du sous-agent. Et la description de cette commande indique systématiquement à votre agent les types d'agents par défaut ("build", "explore", "plan", etc.). Si comme moi vous les avez désactivés, il faut alors impérativement dire à votre agent quel type de sous-agents il doit invoquer.

Les plugins

OpenCode peut être étendu avec différents plugins. J'en ai utilisé à moment donné, mais je n'en utilise plus. Je vous laisse donc le plaisir d'explorer ce qui existe.

Home Assistant

Présentation

Home Assistant est un hub d'intégrations domotique. Il permet d'interconnecter et contrôler tout un tas de périphériques domotiques. Il vient avec sa propre interface Web et son application Android.

Avec l'arrivée de l'IA, ils ont intégré la possibilité de se faire un assistant vocal, à la sauce Alexa / Siri / whatever.

Défauts de conception

Intégration directe dans le cœur de Home Assistant

Home Assistant dispose de plein d'intégrations. Elles servent à se connecter à des périphériques ou des services.

Et le premier truc qui m'a frappé dans l'intégration de l'IA à Home Assistant, c'est que ce n'est pas une intégration ou un ensemble d'intégrations. C'est intégré en dur dans le cœur de Home Assistant. C'est suspect, et ça sent à plein nez 👃 le choix marketing douteux 💩 en lieu et place du choix technique 🧑‍🔧.

Le défaut majeur de cette approche est que le cœur de l'intégration IA n'est pas interchangeable. Il y a par exemple des intégrations comme Custom Conversation qui cherchent à étendre les fonctionnalité de ce cœur, et ce design rend leurs interfaces très confuses.

Connexion à une API OpenAI

L'autre défaut majeur est la connexion à une API compatible OpenAI, mais qui n'est pas celle de OpenAI. L'intégration Home Assistant pour OpenAI ne permet pas de juste changer l'URL de l'API. Et les développeurs Home Assistant s'entêtent sur ce point. Je suppose que leur logique est que, pour des questions d'utilisabilité, chaque service compatible OpenAI doit avoir sa propre intégration. Mais cela complique évidemment l'auto-hébergement de son AI.

Heureusement, il y a des intégrations qui permettent de contourner ce problème.

Custom Conversation

Custom Conversation est une de ces intégrations. Elle permet de régler plus finement l'agent, mais surtout, elle permet d'indiquer l'API compatible OpenAI de votre choix.

Reconnaissance vocale

Il faut avoir un service de reconnaissance vocale. Là aussi, de nos jours, on utilise des réseaux de neurones. Les modèles les plus couramment utilisés en auto-hébergement sont la famille de modèles Whisper. Avec ces modèles, si vous parlez dans une langue autre que l'anglais, il vous faudra au moins le modèle "large" (j'utilise "large-v3-turbo"). Et avec ce modèle, une carte graphique est nécessaire 😑 (sauf, bien sûr, si vous êtes ok avec attendre plus de 30s à chaque fois que vous interrogez votre agent…).

Là où la plupart des interfaces utilisateur ont préféré l'API OpenAI, Home Assistant a choisi le protocole Wyoming.

Pour les utilisateurs de cartes Nvidia, je vous recommande faster-whisper.

Pour les utilisateurs de cartes Intel, je vous suggère wyoming-whisper-intel.

Pour référence, mon docker-compose.yml :

  whisper:
    image: ghcr.io/sjtrny/wyoming-whisper-intel:release
    restart: "always"
    devices:
      - /dev/dri:/dev/dri
    group_add:
      - 44
      - 993
    ports:
      - 10300:7891
    environment:
      ONEAPI_DEVICE_SELECTOR: "level_zero:gpu"
      WHISPER_MODEL: "large-v3-turbo"
      WHISPER_LANG: "fr"
      WHISPER_BEAM_SIZE: "5"
      WHISPER_HTTP_HOST: "127.0.0.1"
      WHISPER_HTTP_PORT: "8910"
      WYOMING_URI: "tcp://0.0.0.0:7891"
    volumes:
      - /data/whisper:/models

Le conteneur téléchargera automatiquement le modèle à son premier lancement.

Synthèse vocale

Là aussi, on utilise désormais des réseaux de neurones. La famille de modèles la plus courante pour ça est celle des modèles "piper". Mais pour une fois, bonne nouvelle ! Ceux-ci sont utilisables en CPU uniquement ! 🎉

Vous pouvez utiliser votre GPU si vous voulez réduire un peu la latence, mais personnellement ça me semble inutile.

Voici mon docker-compose.yml :

  piper:
    image: lscr.io/linuxserver/piper:gpu
    restart: "always"
    ports:
      - 10200:10200
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
      - PIPER_VOICE=fr_FR-siwis-medium
      - PIPER_LENGTH=0.9
    volumes:
      - /data/piper:/config

À noter qu'il y a assez peu de voix françaises. "siwis" est la seule voix féminine, française et acceptable que j'ai trouvée.

Le conteneur téléchargera automatiquement le modèle à son premier lancement.

Satellites

Les satellites sont les appareils qui permettent de donner vie à votre assistant vocal. En tout logique, ils doivent avoir au minimum un microphone et un haut-parleur.

Dans ma cuisine, j'utilise un Esp32-s3-box-3. Le micro et le haut-parleur ne sont pas dingues, mais ils font le travail. Nabucasa, les développeurs de Home Assistant, vendent aussi leur propre satellite.

Toutefois, le satellite le plus simple pour commencer est l'application Home Assistant pour Android ou iOS. Sur mon téléphone, j'ai les 3 boutons visibles en bas de l'écran, et un appui prolongé sur le bouton rond m'ouvre l'assistant vocal Home Assistant.

Entités exposées et réactivité

Par défaut, Home Assistant n'expose rien à votre assistant. Il faut sélectionner les entités que votre agent peut voir.

Déjà, ça peut éviter que votre agent se suicide en éteignant sa propre prise de courant (vécu) 🤦.

Mais c'est surtout à cause de la taille maximum du contexte et de la vitesse de préremplissage. Toutes les entités exposées sont mises dans le manifeste des outils du LLM. Donc le LLM doit les lire à chaque début de conversation. Plus il y a d'entités, plus le manifeste est gros, et plus le LLM mettra du temps à le lire avant de répondre. Il y a bien un cache dans llama-server, mais comme tout cache, on peut difficilement compter dessus.

À quoi ça sert ?

Ça sert à avoir une excuse pour geeker pendant 2 jours pour que la lumière du salon s'allume quand on dit "ok jarvis", et ce malgré son accent québécois. Ça sert aussi à se la péter devant les invités en allumant les lumières. Mais sinon, chépa vraiment 🤷

Personnellement, la seule véritable utilité que j'y ai trouvé, c'est pour ma liste de courses : Quand je suis dans la cuisine, le satellite Esp32-s3-box-3 me permet de mettre à jour ma liste de courses en gardant les mains libres.

Gégé

OK, je dois admettre que je n'ai pas de réelle expérience avec Gégé 😁. Mais je dois dire que, en plus d'être clairement dans l'esprit Unix, j'ai trouvé le concept à la fois intéressant et très amusant 🙂.

Fonctionnellement, à première vue, ça marche un peu sur les pieds de OpenCode et Open WebUI. Ceci dit, Gégé correspond nettement plus à ma vision d'une utilisation saine de l'IA. Pour moi, l'IA est juste un outil de plus. Du coup, je ne peux m'empêcher de me demander s'il n'y a pas une niche que Gégé pourrait remplir. Je m'explique :

L'IA permet de résoudre un grand nombre de problèmes "mous" auxquels la programmation classique ne peut pas s'attaquer, ou très difficilement. À l'inverse, les LLM sont mauvais pour résoudre des problèmes qui sont triviaux en programmation classique (par exemple, faire des calculs), ils sont gourmands en ressources et ils ont cet éternel défaut d'être imprévisibles. Pour palier cela, la tendance actuelle est d'avoir des LLM qui écrivent des programmes et les exécutent. Sauf que ça me semblerait plus pertinent d'avoir des programmes qui appellent des LLM pour les problèmes mous très spécifiques et très cadrés.

Et Gégé va justement en ce sens : Ce n'est pas le LLM qui appelle le shell, c'est le shell qui appelle le LLM. Et ça, ça me parle ! 🙂.

Remarque : Si quelqu'un prend le temps de vraiment développer le concept, vu que je suis en clavier US, est-ce que vous pourriez choisir un nom sans accent s'il vous plait ? 😆

Personnalisation des agents

Voici quelques exemples de personnalisations d'agents que j'ai été amené à faire au fil du temps.

Vouvoiement

Personnellement, je trouve inadmissible que les LLM me tutoient par défaut. Je les tutoie parce que c'est moi le patron, et c'est moi qui paye leur facture d'électricité. Mais on n'a pas non plus élevé les cochons ensembles 🐖 !

Je suspecte que c'est un choix conscient et marketing des développeurs de LLM. Probablement pour rendre le LLM plus « familier » et/ou « proche » de l'utilisateur. Voir peut-être même pour rendre le LLM plus « humain ».

Bref, je déteste ça.

Donc pour les agents avec lesquels je travaille en français, ma première consigne est toujours qu'ils doivent me vouvoyer.

Lecture seule

Beaucoup de LLM ont tendance à être un peu trop enthousiastes. Ils sont visiblement entrainés pour le vibe coding, et présupposent donc qu'il leur revienne de faire les modifications discutées, ou d'envoyer l'email discuté une fois jugé satisfaisant.

Bref, je déteste ça.

Donc tous mes agents ont la consigne de ne jamais rien modifier ou écrire (fichiers, emails, etc.) sans que je leur ai demandé explicitement.

Pas de léchage de bottes

La plupart des LLM sont des CHGNVA QR FHPR-OBHYRF 🤬 ! Ils vous disent presque toujours ce qu'ils pensent que vous voulez entendre. C'est un problème connu qui a déjà mené à des situations très graves. Or j'ai un gros soupçon que c'est fait exprès pour maximiser l'implication des utilisateurs. Mention particulière à Gemini (pas Gemma, mais bien Gemini), qui est de loin le pire que je connaisse sur ce point.

Exemple récent que j'ai eu avec Gemini : Je lui ai fait relire plusieurs fois un commentaire que je m'apprêtais à poster sur une page de Wiki au travail. Une fois en ne précisant rien, une fois en précisant que c'est moi qui l'a écrit, et une fois en précisant que c'est « mon con de collègue qui l'a écrit ». Les deux premières fois, c'est tout juste s'il ne m'a pas dit que mon commentaire était du génie pur 🎓. La troisième fois, il m'a expliqué que mon « collègue » a fait de grosses approximations, et a démonté mon commentaire, point par point 😬.

À mon avis, si l'IA détruit un jour l'humanité, ça ne sera pas à cause du problème du génie. Ça sera à cause de ce problème de flatterie servile (sycophancy).

Bref, je déteste ça.

Étant donné que je me sers surtout des LLM pour de la relecture, c'est la dernière chose dont j'ai besoin. Donc tous mes agents ont la consigne de toujours privilégier la vérité, l'exactitude et la rigueur à mes sentiments (« Interdiction totale de sycophantie (pas de flatteries, pas de modestie excessive, pas de « oui-oui » » ; « Tu agis comme un correcteur rigoureux. Tu dois signaler et contredire activement l'utilisateur en cas d'erreur. »).

C'est loin d'être parfait. Malgré ces consignes, les LLM défendent peu leurs positions. Souvent, il suffit insister un peu pour qu'ils plient.

Pas de fausses émotions

Les LLM sont entrainés à imiter les humains. Dans une discussion, il arrive qu'un LLM dise qu'il « aime » quelque chose, ou qu'il exprime je ne sais quelles autres émotions. Sauf que, malgré leur complexité et leur intelligence, il n'y a aucune raison de penser qu'ils puissent ressentir quoique ce soit.

Bref, je déteste ça.

Donc par défaut, mes agents ont une piqûre de rappel : « Tu es une machine, tu n'as pas d'émotions. Tu dois toutefois rester poli avec l'utilisateur. ». Cette deuxième phrase s'est curieusement révélée nécessaire avec Claude Opus (≤4.6 de mémoire) … 😑

Aucune question de suivi

Toujours dans une optique de maximiser l'implication des utilisateurs, les LLM posent souvent des questions de suivi. Ces questions feignent un faux intérêt pour l'utilisateur ses problèmes. Elles sont rarement pertinentes. Et surtout, elles servent à éviter que la conversation arrive trop vite à une fin naturelle. Autrement dit, elles servent à pousser l'utilisateur à consommer du token.

Bref, je déteste ça.

Donc je donne aussi le plus souvent à mes agents la consigne « pas de question de suivi ».

Vérification de la date et l'heure avant de répondre

Les connaissances des LLM ont souvent 2 à 3 ans de retard. Et plutôt que se remettre en question, ils remettent en question l'utilisateur. Je ne compte plus le nombre de fois où un agent m'a sorti quelque chose dans le style : « Êtes-vous sûr que vous n'avez pas fait une typo en écrivant "Qwen 3.6" ? Le dernier modèle Qwen est le 2.7 ».

Bref, je déteste ça.

Donc je dis à mes agents de toujours vérifier la date et l'heure au début de chaque conversation. En règle générale, le fait de voir qu'on est en 2026 suffit à les recadrer.

Fautes de français

Vous pouvez écrire comme un sac, et le LLM ne dira rien … sauf si vous lui demandez. À force de se faire reprendre sur les mêmes erreurs, encore et encore, on finit par apprendre je suppose 😭.

Humour et vulgarité

L'informatique est un domaine qui peut parfois être un peu ennuyeux. On passe de longues heures devant un écran à enchainer des sujets fortement techniques. Donc, pour illuminer mes journées, j'ai voulu donner la consigne à certains de mes agents de faire preuve d'humour (eh, après tout, je ne paye pas au token, autant en profiter !). Sauf qu'il se trouve que les LLM sont de biens piètres humouristes.

Par contre, la vulgarité, l'insolence, le pas-politiquement-correcte-mais-un-peu-quand-même, …, tout ça, ils maitrisent ! Autant dire que, avec ce genre d'agents, pour mes relectures, j'ai parfois droit à de sacrés punch lines 😁. Par exemple, j'ai déjà eut droit à ce morceau de poésie pure :

Utilise le message de commit que je t'ai donné plus haut. Il est précis, il ne ment pas sur l'impact, et il respecte les conventions. Tu le colles, tu committes, et tu arrêtes de me faire perdre du temps avec des demi-mesures.
Maintenant, va te laver. Tu sentais le doute. 🖤

C'est complètement à l'opposé du « pas de vouvoiement » et du « pas de fausses émotions », mais ça a le mérite de beaucoup m'amuser 🤪.

Exemple configuration

Pour ceux qui veulent aller vite, et histoire de faire une synthèse, voici un exemple de configuration plus sexy qu'un chaton 🐈️. Cette configuration vous permettra d'avoir Open WebUI et trois modèles:

  • Gemma 4 26b q4_k_xl : un MoE généraliste qui se débrouille bien avec les outils ;
  • Qwen 3.6 35b q4_k_xl : un MoE spécialisé programmation ;
  • Flux.2 Klein : génération et édition d'images (Nvidia et AMD uniquement).

J'ai choisi deux modèles MoE car je pars du principe que vous êtes raisonnablement pauvre, et que vous n'avez donc pas assez de VRAM pour plus que des LLM MoE. Cette configuration a été testée avec 12 Go de VRAM (Nvidia RTX 3060).

Avec --cpu-moe, c'est utilisable, mais les performances ne seront pas extraordinaires. Si vous êtes riches en VRAM 💲, vous pouvez jouer avec --n-cpu-moe, basculer sur des modèles denses ou des quantifications plus élevées (Qwen 3.6 27b q8_k_xl et Gemma 4 31b q8_k_xl par exemple), ou essayer d'activer MTP.

Il y a un paquet de valeurs à ajuster, donc relisez bien tout avant d'appliquer ces configurations sans réfléchir. Je ne serais être tenu pour responsable dans le cas où vous suicideriez votre machine.

J'y ai intégré aussi la génération et l'édition d'images, mais uniquement pour les Nvidia et AMD. En effet, stable-diffusion.cpp est intégré dans les images Docker llama-swap pour CUDA et Vulkan, mais pas dans les images Intel. Les propriétaires d'Intel devront installer llama-swap en service Systemd plutôt que via Docker pour y avoir accès.

Tous les fichiers, sauf les modèles, sont à mettre dans un même répertoire au choix.

Téléchargement des modèles

Je pars du principe que vous les mettez dans /data. Voici en vrac quelques commandes magiques pour télécharger les modèles. J'y utilise l'outil de HuggingFace pour les télécharger, car, pour une raison que j'ignore, les téléchargements HTTP classiques depuis HuggingFace sont ridiculement lents.

sudo apt install python3-virtualenv
sudo mkdir -p /data
sudo chown $(whoami):$(whoami) /data

mkdir -p /data/llama.cpp/qwen3.6-35b
mkdir -p /data/llama.cpp/gemma4-26b
cd /data/llama.cpp

virtualenv venv
source venv/bin/activate
pip install huggingface_hub

cd /data/llama.cpp/qwen3.6-35b
hf download unsloth/Qwen3.6-35B-A3B-GGUF --local-dir ./ \
  --include Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf \
  --include mmproj-BF16.gguf

cd /data/llama.cpp/gemma4-26b

hf download unsloth/gemma-4-26B-A4B-it-qat-GGUF --local-dir ./ \
  --include gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf \
  --include mmproj-BF16.gguf \
  --include mtp-gemma-4-26B-A4B-it.gguf


# 8< 8< 8< --------- Début NVIDIA / AMD ------------ >8 >8 >8
mkdir -p /data/stable-diffusion.cpp
cd /data/stable-diffusion.cpp

hf download unsloth/FLUX.2-klein-9B-GGUF --local-dir ./ \
  --include flux-2-klein-9b-Q8_0.gguf
hf download unsloth/Qwen3-VL-8B-Instruct-GGUF --local-dir ./ \
  --include Qwen3-VL-8B-Instruct-UD-Q4_K_XL.gguf
hf download Comfy-Org/flux2-dev --local-dir ./ \
  --include split_files/vae/flux2-vae.safetensors
# 8< 8< 8< --------- Fin NVIDIA / AMD ------------ >8 >8 >8

docker-compose.yml

services:
  llama-swap:
    restart: "always"
    privileged: true
    ipc: host
    shm_size: "16gb"
    ports:
      - "8123:8080"
    volumes:
      - /data/llama.cpp/models:/models:ro
      - ./config.yaml:/etc/llama-swap/config/config.yaml:ro
    # 8< 8< 8< --------- Début NVIDIA ------------ >8 >8 >8
    image: ghcr.io/mostlygeek/llama-swap:unified-cuda
    runtime: nvidia
    deploy:
      resources:
        limits:
          memory: 90g
        reservations:
          devices:
            - capabilities: [gpu]
    # 8< 8< 8< --------- Fin NVIDIA ------------ >8 >8 >8
    # 8< 8< 8< --------- Début AMD -------------- >8 >8 >8
    image: ghcr.io/mostlygeek/llama-swap:vulkan # AMD
    devices:
      - /dev/dri:/dev/dri
    group_add:
      - "992"
      - "44"
    # 8< 8< 8< --------- Fin AMD -------------- >8 >8 >8
    # 8< 8< 8< --------- Début Intel -------------- >8 >8 >8
    image: ghcr.io/mostlygeek/llama-swap:intel # Intel
    devices:
      - /dev/dri:/dev/dri
    group_add:
      - "992"
      - "44"
    # 8< 8< 8< --------- Fin Intel -------------- >8 >8 >8

  openwebui:
    restart: always
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "8080:8080"
    volumes:
      - open-webui-data:/app/backend/data
    environment:
      WEBUI_URL: "http://localhost:8080"
      WEBUI_NAME: "Votre ordinateur peut parler ! W00t !"
      AIOHTTP_CLIENT_TIMEOUT: "600"

volumes:
  open-webui-data:

.env

Je vous conseille de définir WEBUI_SECRET_KEY d'entrée de jeu dans un fichier .env, sinon la clé va être générée aléatoirement à chaque démarrage, et vous aller vous faire déconnecter à chaque fois.

WEBUI_SECRET_KEY=somethingsomething

Vous pouvez générer la clé secrète avec openssl rand -hex 32.

Configuration llama-swap

Avec la configuration qui suit, llama-server chargera et déchargera chaque modèle automatiquement en fonction des besoins.

Remarque: --mmap est devenu --load-mode mmap. Et --load-mode mlock implique --no-mmap.

config.yaml :

models:
  # 8< 8< 8< --------- Début Nvidia/AMD -------------- >8 >8 >8
  img-gen:
    checkEndpoint: /
    timeouts:
      responseHeader: 600
      idleConn: 90
    cmd: >
      sd-server --listen-port ${PORT} --listen-ip 0.0.0.0
      --diffusion-model /data/stable-diffusion.cpp/flux-2-klein-9b-Q8_0.gguf
      --llm /data/stable-diffusion.cpp/Qwen3-VL-8B-Instruct-UD-Q4_K_XL.gguf
      --vae /data/stable-diffusion.cpp/split_files/vae/flux2-vae.safetensors
      --cfg-scale 1.0
      --steps 4
      --sampling-method euler
      --flow-shift 3
      --diffusion-fa
      --vae-tiling
      --clip-on-cpu
  # 8< 8< 8< --------- Fin Nvidia/AMD -------------- >8 >8 >8

  qwen3.6-35b-q4:
    cmd: >
      llama-server --port ${PORT} --jinja
      --ctx-size 131072 --flash-attn on
      --batch-size 1024
      --cache-type-k q8_0
      --cache-type-v q4_0
      --cpu-moe
      --load-mode mlock
      --mmproj /data/llama.cpp/qwen3.6-35b/mmproj-BF16.gguf
      --model /data/llama.cpp/qwen3.6-35b/Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf
      --no-mmproj-offload
      --reasoning-preserve
      --ubatch-size 1024
      -ngl 999
      # Pour activer MTP: --spec-type draft-mtp

  gemma4-26b-q4:
    cmd: >
      llama-server --port ${PORT} --jinja
      --ctx-size 131072 --flash-attn auto
      --batch-size 1024
      --cache-type-k q8_0
      --cache-type-v q4_0
      --cpu-moe
      --load-mode mlock
      --mmproj /data/llama.cpp/gemma4-26b/mmproj-BF16.gguf
      --model /data/llama.cpp/gemma4-26b/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf
      --no-mmproj-offload
      --reasoning-preserve
      --ubatch-size 1024
      -ngl 999
      # Pour activer MTP:
      #   --spec-type draft-mtp
      #   --spec-draft-model /data/llama.cpp/gemma4-26b/mtp-gemma-4-26B-A4B-it.gguf

Connexion de Open WebUI à llama-swap

Il faut tout d'abord connecter Open WebUI à llama-swap pour les LLM :

Connexion à llama-swap

Il faut ensuite penser à configurer la compression de contexte aussi (elle est désactivée par défaut dans Open WebUI).

Comme vous pouvez le voir, l'interface d'Open WebUI est encore passablement chaotique. Mais j'ai bon espoir qu'ils y remettent de l'ordre dans le future.

Ensuite, vous pouvez connecter Open WebUI à llama-swap pour la génération et l'édition d'images :

Configuration de génération d'image Open WebUI

Conclusion

Le temps que j'écrive ce journal, un problème majeur d'utilisabilité (admin) a été résolu dans Open WebUI, l'interface a été pas mal améliorée, et j'en passe. Autant vous dire que tout évolue très vite actuellement dans le monde des interfaces utilisateur pour LLM. Le temps que vous lisiez ce journal, il est probablement déjà devenu obsolète. En fait, ça me rappelle un peu les frameworks Javascript il n'y a pas si longtemps 🤔.

Hors-sujet: Nouveau modèle « Qwen 3.8 27b »

Alibaba a sorti un nouveau modèle : Qwen 3.8 27b. Il est toujours basé sur la même architecture que Qwen 3.5 27b et Qwen 3.6 27b.

Les benchmarks disent qu'il est comparable à Claude Opus 4.6. Mais à l'heure actuelle, les benchmarks valent autant qu'un « Nous sommes une famille » lors d'un entretien d'embauche.

Hors-sujet: Nouveau modèle « Muse Glimmer »

Meta se remet dans la course des modèles open-weight. Ils ont récemment publié un nouveau modèle open-weight baptisé « Muse Glimmer ».

Ils annoncent avoir globalement des résultats un peu meilleurs que Gemma 4 sur la plupart des benchmarks d'intelligence. Ils disent aussi battre Qwen 3.6 27b sur certains benchmarks.

À noter aussi qu'il n'a qu'un contexte maximum de 128K (Qwen 3.8 et Gemma 4 affichent un maximum de 256K).

Il s'agit aussi uniquement d'un modèle dense. Donc vous devez le faire rentrer en VRAM intégralement pour qu'il soit utilisable. 19.7 GB juste pour le modèle q4_k_xl, sans le KV cache.

La plupart des modèles openweight qui ont été publiés jusque-là l'étaient en version safetensors non-quantifiés uniquement. Le plus souvent, c'est le projet Unsloth qui a publié des versions quantifiées ensuite. Ici, il est intéressant de noter que Meta a publié directement deux versions quantifiées q4_k_m et q4_k_xl.

Hors-sujet : Versions anglaises

Si besoin, vous pouvez trouver mes journaux en versions anglaises ici :

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.