Encore de l’IA, désolé mais c’est le mot valise à la mode … mais là, on parle d’IA à la maison, celle qui tourne sur une machine, dans mon lab, et qui n’envoie rien à personne. Vous le savez, je suis transparent là-dessus depuis le début : j’utilise Mistral et Claude (anciennement Mammouth) pour accélérer, corriger, relire le blog, et pour coder mes petits outils. Mais au fond, ça me titille depuis un moment (et j’expérimente depuis plusieurs mois) : Est-ce qu’un simple Mac mini peut faire tourner un modèle correct ? Et surtout, est-ce que je peux me passer d’un abonnement et garder mes données chez moi (souveraineté, quand tu nous tiens ^^) ?
Alors j’ai décidé d’apprendre pas à pas, et de vous embarquer avec moi dans une petite série d’articles. J’ai d’ailleurs déjà fait une première install un peu à l’arrache il y a quelques jours, ça marchait… mais j’avais envie de comprendre en détail ce que je faisais. Donc j’ai tout effacé, et on repart de zéro, proprement. Au programme de ce premier chapitre : Ollama pour faire tourner le modèle, Open WebUI pour avoir une jolie interface de chat façon ChatGPT, mais chez soi, et SearXNG pour que tout ce petit monde puisse chercher sur le web.
Beaucoup de gens se diront qu’on connait tous ça depuis des années … mais encore une fois, à défaut d’une vraie formation, un série d’articles qui rentrent pas à pas dans le mise en oeuvre d’une infra sur lequel on va progresser tous en semble me parait une bonne idée aujourd’hui. Ne serait-ce que pour maîtriser toute la terminologie et les concepts associés à ce domaine encore nouveau mais qui amorce déjà une vraie rupture technologique et sociétale.
Le plan de bataille
L’idée est simple, et elle parlera à tous ceux qui font de l’infra : on sépare le moteur et l’interface. D’un côté, jarvis, mon Mac mini M4, fait tourner le modèle avec Ollama ; de l’autre, viviane, mon hôte Docker habituel, sert l’interface Open WebUI en Docker Compose. Comme ça, les 16 Go de jarvis sont réservés au modèle, et l’interface vit tranquillement avec le reste de mes conteneurs.

Un Mac mini, vraiment ?
Sur un PC classique, le modèle doit tenir dans la mémoire de la carte graphique (la VRAM), souvent 8 ou 12 Go sur une carte grand public. Sur un Mac Apple Silicon, la mémoire est unifiée : le CPU et le GPU piochent dans la même RAM. Mon M4 a 16 Go, et le GPU peut en utiliser une bonne partie pour le modèle. Ce n’est pas une bête de course (on en reparlera dans un chapitre dédié au matériel), mais pour démarrer, c’est largement suffisant.
À garder en tête : un modèle de 8 milliards de paramètres, quantifié en 4 bits, pèse environ 5 Go. Il faut ajouter la mémoire du contexte (la « mémoire de travail » de la conversation en général sur Mac Mini on estime que 8k ou 12k est pas mal), et laisser de la place à macOS. Sur 16 Go, un 8B est confortable, un 14B commence à être juste.
Ollama : natif, et pas en conteneur (bouh …)
Moi qui adore les conteneurs, ça me fend le cœur, mais là, non. Sur Mac, Docker tourne dans une petite VM Linux qui n’a pas accès au GPU Apple (à voir avec le nouveau Containerization Framework de Switft depuis Mac OS 26 et 27 ^^). Un Ollama en conteneur ne tournerait que sur le CPU, autant dire au ralenti (je n’ai pas fait de bench, mais à mon avis en inférence ça doit être horrible). Installé en natif, Ollama utilise le GPU via Metal, l’API graphique d’Apple. Donc Ollama en natif, installé avec Homebrew, et tout le reste en Docker sur une deuxième machine (Viviane pour les intimes ^^)
Pour l’install, j’ai voulu le faire entièrement en ligne de commande, pour vraiment comprendre l’outil. Ollama propose aussi une application à télécharger (le fichier Ollama.dmg), c’est le chemin le plus simple si vous débutez, mais vous passerez à côté de pas mal de choses.
Un gros log des commandes, pour les affreux nerds que nous sommes :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
jarvis@jarvis:~$ brew install ollama ==> Downloading bottle manifests ✔︎ Bottle Manifest ollama (0.40.13) Downloaded 50.5KB/ 50.5KB ==> Would install 1 formula: ollama 0.40.13 ==> Would install 2 dependencies for ollama: mlx 0.32.4 mlx-c 0.8.0 ==> Do you want to proceed with the installation? [y/n] ==> Fetching downloads for: ollama ✔︎ Bottle mlx-c (0.8.0) Downloaded 230.4KB/230.4KB ✔︎ Bottle mlx (0.32.4) Downloaded 76.9MB/ 76.9MB ✔︎ Bottle ollama (0.40.13) Downloaded 22.0MB/ 22.0MB ==> Installing dependencies for ollama: mlx and mlx-c ==> Installing ollama dependency: mlx (0.32.4) ==> Pouring mlx--0.32.4.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/mlx/0.32.4: 466 files, 240MB ==> Installing ollama dependency: mlx-c (0.8.0) ==> Pouring mlx-c--0.8.0.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/mlx-c/0.8.0: 40 files, 1MB ==> Installing ollama ==> Pouring ollama--0.40.13.arm64_golden_gate.bottle.tar.gz 🍺 /opt/homebrew/Cellar/ollama/0.40.13: 15 files, 66.9MB ==> Cleanup Removing: /Users/jarvis/Library/Caches/Homebrew/ollama--0.40.11_1... (0B) Removing: /Users/jarvis/Library/Caches/Homebrew/ollama_bottle_manifest--0.40.11_1... (0B) Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`. Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`). ==> Caveats ==> ollama To start ollama now and restart at login: brew services start ollama Or, if you don't want/need a background service you can just run: OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve jarvis@jarvis:~$ ollama --version Warning: could not connect to a running Ollama instance Warning: client version is 0.40.13 jarvis@jarvis:~$ brew info ollama ==> ollama ✔: stable 0.40.13 (bottled), HEAD Create, run, and share large language models (LLMs) https://ollama.com/ Installed (on request) From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/ollama.rb License: MIT ==> Installed Versions ollama ✔ 0.40.13 (15 files, 66.9MB) [Linked] ==> Dependencies Required (1): mlx-c ✔ Recursive Runtime (11): all installed ✔ ==> Options --HEAD Install HEAD version ==> Caveats To start ollama now and restart at login: brew services start ollama Or, if you don't want/need a background service you can just run: OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve ==> Downloading https://formulae.brew.sh/api/formula/ollama.json ==> Analytics install: 64,763 (30 days), 148,266 (90 days), 658,044 (365 days) install-on-request: 64,616 (30 days), 148,003 (90 days), 657,473 (365 days) build-error: 78 (30 days) jarvis@jarvis:~$ ollama serve Couldn't find '/Users/jarvis/.ollama/id_ed25519'. Generating new private key. Your new public key is: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF+grw+2UviL3vfeaZNzWTh42lX0wS07Hirf0usfAUeB time=2026-10-31T11:05:57.129+01:00 level=INFO source=routes.go:2474 msg="server config" env="map[HTTPS_PROXY: HTTP_PROXY: LLAMA_ARG_FIT: LLAMA_ARG_FIT_TARGET: NO_PROXY: OLLAMA_CONTEXT_LENGTH:0 OLLAMA_CREATE_REMOTE:false OLLAMA_DEBUG:INFO OLLAMA_DEBUG_LOG_REQUESTS:false OLLAMA_EDITOR: OLLAMA_FLASH_ATTENTION:false OLLAMA_GO_TEMPLATE:true OLLAMA_GPU_OVERHEAD:0 OLLAMA_HOST:http://127.0.0.1:11434 OLLAMA_IGPU_ENABLE: OLLAMA_KEEP_ALIVE:5m0s OLLAMA_KV_CACHE_TYPE: OLLAMA_LLM_LIBRARY: OLLAMA_LOAD_TIMEOUT:5m0s OLLAMA_MAX_LOADED_MODELS:0 OLLAMA_MAX_QUEUE:512 OLLAMA_MAX_TRANSFER_STREAMS:4 OLLAMA_MODELS:/Users/jarvis/.ollama/models OLLAMA_NOHISTORY:false OLLAMA_NOPRUNE:false OLLAMA_NO_CLOUD:false OLLAMA_NUM_PARALLEL:1 OLLAMA_ORIGINS:[http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://* vscode-file://*] OLLAMA_REMOTES:[ollama.com] OLLAMA_SCHED_SPREAD:false http_proxy: https_proxy: no_proxy:]" time=2026-10-31T11:05:57.130+01:00 level=INFO source=routes.go:2476 msg="Ollama cloud disabled: false" time=2026-10-31T11:05:57.131+01:00 level=INFO source=images.go:1033 msg="total blobs: 0" time=2026-10-31T11:05:57.131+01:00 level=INFO source=images.go:1042 msg="total unused blobs removed: 0" time=2026-10-31T11:05:57.132+01:00 level=INFO source=routes.go:2536 msg="Listening on 127.0.0.1:11434 (version 0.40.13)" time=2026-10-31T11:05:57.132+01:00 level=INFO source=runner.go:60 msg="discovering available GPUs..." (...) |
Premier modèle
Pour démarrer, j’ai choisi qwen3:8b, de chez Alibaba (5,2 Go). Il sait raisonner (le mode « thinking ») et appeler des outils, et il tient largement dans 16 Go. Ceux qui font du Docker vont se sentir à la maison :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
ollama pull qwen3:8b ollama list NAME ID SIZE MODIFIED qwen3:8b 500a1f067a9f 5.2 GB 30 hours ago ollama show qwen3:8b Model architecture qwen3 parameters 8.2B context length 40960 embedding length 4096 quantization Q4_K_M template source go_template Capabilities completion tools thinking levels false, true default true Parameters top_k 20 top_p 0.95 repeat_penalty 1 stop "<|im_start|>" stop "<|im_end|>" temperature 0.6 License Apache License Version 2.0, January 2004 ... du -sh ~/.ollama/models 4.9G /Users/jarvis/.ollama/models |
Et la première conversation, avec les statistiques :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
ollama run qwen3:8b --verbose >>> Bonjour ordinateur, comme t'appelles tu ? Thinking... Okay, the user asked, "Bonjour ordinateur, comme t'appelles tu ?" which means "Hello computer, what is your name?" in French. First, I need to respond in French since the user used that language. The user might be testing if I can switch languages or is more comfortable in French. I should start by greeting them back in French: "Bonjour !". Then, I need to state my name. My name is Qwen, but I should mention that I'm a large language model developed by Alibaba Cloud. However, the user might not know about Alibaba Cloud, so I should keep it simple. Wait, the user might just want to know my name without the company info. Maybe I should say "Je m'appelle Qwen" and then add that I'm a large language model. But I need to check if the user is looking for more details. Alternatively, maybe they just want a friendly response. Let me make sure to keep it conversational. Also, since the user used "ordinateur" (computer), they might be thinking of a traditional computer, but I'm an AI. I should clarify that I'm an AI assistant. So the response should be: "Bonjour ! Je m'appelle Qwen, je suis un assistant d'intelligence artificielle développé par Alibaba Cloud. Comment puis-je vous aider aujourd'hui ?" That covers my name, my role, the company, and offers help. It's friendly and informative without being too technical. I think that's a good approach. ...done thinking. Bonjour ! Je m'appelle Qwen, je suis un assistant d'intelligence artificielle développé par Alibaba Cloud. Comment puis-je vous aider aujourd'hui ? 😊 total duration: 18.208201708s load duration: 2.170792ms prompt eval count: 21 token(s) prompt eval duration: 182.323ms prompt eval rate: 115.18 tokens/s eval count: 362 token(s) eval duration: 18.018955s eval rate: 20.09 tokens/s |
À la fin de chaque réponse, Ollama affiche ses stats. Les deux chiffres à regarder : prompt eval rate (la vitesse de lecture de votre question) et eval rate (la vitesse d’écriture de la réponse, en tokens par seconde).
Pendant que le modèle est chargé, dans un autre terminal :
|
1 |
ollama ps |
La colonne PROCESSOR doit afficher « 100% GPU ». Si vous voyez du CPU, c’est que le modèle ne tient pas entièrement en mémoire GPU. Les colonnes SIZE et CONTEXT montrent la mémoire prise et la taille du contexte.
|
1 2 3 |
jarvis@jarvis:~$ ollama ps NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL qwen3:8b 500a1f067a9f 5.6 GB 100% GPU 8192 llamacpp 2 minutes from now |
Le mode interactif, plein de petites surprises
Dans ollama run, les commandes commencent par un / :
|
1 2 3 4 5 6 7 8 9 |
/? #la liste des commandes /show info #comme ollama show /show parameters #les paramètres en vigueur /set parameter num_ctx 8192 #le contexte de la session /set nothink #coupe le raisonnement de Qwen3 /set think #le remet /set verbose #les stats après chaque réponse /save qwen3-test #enregistre la session comme un nouveau modèle /bye #on sort |
Le test qui m’a le plus appris : poser la même question avec et sans raisonnement.
On peut aussi s’en servir directement depuis le shell, sans mode interactif, et là ça devient scriptable :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
jarvis@jarvis:~$ ollama run qwen3:8b --think=false "Explique la mémoire unifiée en trois phrases." La mémoire unifiée est un type de mémoire qui combine les ressources de la mémoire vive (RAM) et de la mémoire flash dans un seul espace de stockage. Elle permet aux appareils de stocker et d'accéder aux données de manière plus efficace, en réduisant la latence et en améliorant la performance. Cela est particulièrement utile dans les appareils comme les smartphones et les tablettes. jarvis@jarvis:~$ ollama run qwen3:8b "Explique la mémoire unifiée en trois phrases." Thinking... Okay, the user is asking to explain unified memory in three sentences. Let me start by recalling what unified memory is. I think it's a concept in computing where the CPU and GPU share the same pool of memory. That's different from traditional systems where they have separate memory spaces. So the first sentence should introduce this shared memory model. Next, I need to mention the benefits. Unified memory allows data to be accessed by both the CPU and GPU without needing to copy it back and forth. That reduces latency and improves efficiency. That's a good point for the second sentence. For the third sentence, I should talk about the implications. It simplifies programming because developers don't have to manage memory allocation between different components. Also, it's more efficient for tasks that require frequent data exchange between CPU and GPU, like machine learning or real-time rendering. I need to make sure each sentence is concise and covers a distinct aspect: definition, benefits, and implications. ...done thinking. La mémoire unifiée est un modèle de mémoire partagée entre le processeur (CPU) et la carte graphique (GPU), permettant aux deux composants d'accéder directement aux mêmes données sans duplication. Elle réduit la latence et optimise l'efficacité en évitant les transferts coûteux de données entre des espaces de mémoire distincts. Ce concept simplifie le développement d'applications complexes, comme l'intelligence artificielle ou le rendering 3D, en offrant une gestion unifiée des ressources. |
Le point qu’on peut retenir ici c’est que le mode « reasoning » est globalement beaucoup plus performant qu’une réponse « sans réflexion » …. ben en fait c’est comme les humains, si on parle sans réfléchir, on a vite fait de dire des co…neries. Et bien c’est vrai pour l’IA et ça se vérifie pour les petits modèles aussi bien que pour les gros ! N’accordez jamais une confiance aveugle à une réponse de l’IA, aussi bluffante soit-elle (et même en code … des exemples perso tous les jours). Le revers de la médaille c’est que ça met plus de temps, forcément.
Gérer ses modèles
|
1 2 3 4 |
ollama stop qwen3:8b # décharge de la mémoire ollama ps # vide ollama cp qwen3:8b qwen3-copie # copie, sans dupliquer les couches ollama rm qwen3-copie |
Sans stop, Ollama décharge le modèle tout seul après 5 minutes d’inactivité. C’est pour cela que vous pouvez trouver bizarre qu’après une longue période d’inactivité il mette du temps à répondre à sa première question.
Mon propre modèle, avec un Modelfile
Là, je suis surpris par la ressemblance avec Docker : une base, des réglages, un nom.
|
1 2 3 4 5 6 7 8 9 10 11 |
mkdir -p ~/ollama-modeles && cd ~/ollama-modeles cat > Modelfile <<'EOF' FROM qwen3:8b PARAMETER num_ctx 8192 PARAMETER temperature 0.6 SYSTEM """Tu réponds en français, de façon concise et technique.""" EOF ollama create jarvis-qwen3 -f Modelfile ollama list ollama show jarvis-qwen3 --modelfile ollama run jarvis-qwen3 --verbose |
Mon petit modèle à moi, avec son prompt système spécifique ^^
Ollama est aussi accessible via API, un nouveau monde s’ouvre à vous !
Ollama, c’est surtout une API sur le port 11434. C’est elle qu’utiliseront Open WebUI, et plus tard … (teasing). Elle existe en deux saveurs : l’API native d’Ollama, et une API compatible OpenAI, que la plupart des outils savent déjà utiliser, forcément.
|
1 2 3 4 5 6 7 8 9 10 |
curl -s http://127.0.0.1:11434/api/version curl -s http://127.0.0.1:11434/api/tags | jq '.models[].name' curl -s http://127.0.0.1:11434/api/ps | jq # API compatible OpenAI curl -s http://127.0.0.1:11434/v1/models | jq curl -s http://127.0.0.1:11434/v1/chat/completions -H 'Content-Type: application/json' -d '{ "model": "qwen3:8b", "messages": [{"role": "user", "content": "Dis bonjour en chinois pour voir."}] }' | jq -r '.choices[0].message.content' |
你好 🙂
Les réglages qui changent pas mal le comportement
Par défaut, Ollama n’écoute que sur la machine et utilise un contexte de 4096 tokens. C’est trop court dès qu’on colle un texte un peu long ou des résultats de recherche. On arrête le serveur (Ctrl+C) et on le relance avec nos réglages :
|
1 2 3 4 5 |
OLLAMA_HOST=0.0.0.0:11434 \ OLLAMA_CONTEXT_LENGTH=8192 \ OLLAMA_FLASH_ATTENTION=1 \ OLLAMA_KV_CACHE_TYPE=q8_0 \ ollama serve |
En clair : OLLAMA_HOST le fait écouter sur toutes les interfaces, pour qu’Open WebUI puisse le joindre depuis viviane ; OLLAMA_CONTEXT_LENGTH passe le contexte par défaut à 8192 pour tous les clients ; OLLAMA_FLASH_ATTENTION et OLLAMA_KV_CACHE_TYPE stockent la mémoire du contexte en 8 bits au lieu de 16, soit environ moitié moins de mémoire pour le contexte.
Attention : Ollama n’a AUCUNE authentification. À partir de là, n’importe quelle machine du réseau peut utiliser votre modèle. Et on n’expose évidemment rien sur Internet (ou alors vous savez ce que vous faites ^^).
Pour les mises à jour, RTFM… ou plus simplement : brew upgrade ollama (sur mac en tout cas …)
Open WebUI : l’interface, en Docker Compose sur viviane
Bon, Ollama c’est bien, mais discuter avec un modèle dans un terminal, ça dure deux minutes mais guère plus 🙂 . Open WebUI, c’est l’interface web façon ChatGPT (qu’on connaît depuis le début en 2022), open source, qui se branche sur l’API d’Ollama. On revient en terrain connu, là : Docker Compose, sur la machine Viviane (qui tourne sous Debian Trixie).
Un dossier, un compose, un .env, rien de plus :
|
1 2 3 |
cidou@viviane:~$ mkdir openwebui && cd openwebui cidou@viviane:~/openwebui$ vi docker-compose.yml cidou@viviane:~/openwebui$ vi .env |
Le docker-compose.yml :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
services: open-webui: image: ghcr.io/open-webui/open-webui:${OWUI_VERSION:-v0.12.5} container_name: open-webui restart: unless-stopped ports: - "${WEBUI_PORT:-3000}:8080" environment: - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://192.168.100.3:11434} - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY} - WEBUI_NAME=${WEBUI_NAME:-Jarvis} - TZ=Europe/Paris volumes: - open-webui-data:/app/backend/data volumes: open-webui-data: |
Et le .env qui va avec :
|
1 2 3 4 5 |
OWUI_VERSION=v0.12.5 WEBUI_PORT=3000 OLLAMA_BASE_URL=http://192.168.100.3:11434 WEBUI_NAME=Jarvis WEBUI_SECRET_KEY= # à remplir avec la sortie de : openssl rand -hex 32 |
Quelques choix assumés : l’image est épinglée sur une version (v0.12.5) et pas sur :main, qui bouge tout le temps, on ne se refait pas ; la WEBUI_SECRET_KEY est fixe ; les données (comptes, conversations, réglages) vivent dans un volume nommé ; et OLLAMA_BASE_URL pointe vers jarvis. Évidemment, le .env ne part jamais dans un dépôt Git (et ne se colle pas dans un article de blog non plus, hein ^^).
Et ils sont … partis !
|
1 2 3 4 5 6 |
cidou@viviane:~/openwebui$ docker compose up -d [+] up 22/22 ✔ Image ghcr.io/open-webui/open-webui:v0.12.5 Pulled 59.6s ✔ Network openwebui_default Created 0.0s ✔ Volume openwebui_open-webui-data Created 0.0s ✔ Container open-webui Started 0.7s |
Une minute de téléchargement, et direction http://viviane:3000.
Premier réflexe, TRÈS important : créer son compte tout de suite. Le premier inscrit devient administrateur. Et dans la foulée, on coupe les inscriptions : Panneau d’administration, Réglages, Général, « Enable New Sign Ups » sur Off.



À savoir : il existe bien une variable ENABLE_SIGNUP=false, mais Open WebUI enregistre ce genre de réglage dans sa base au premier démarrage (ils appellent ça une « PersistentConfig »), et ensuite la variable est ignorée. Donc sur une instance déjà lancée, c’est dans l’interface que ça se passe. Si vous voulez vraiment piloter par l’environnement, il faut ajouter ENABLE_PERSISTENT_CONFIG=false, mais alors plus rien de ce que vous réglez dans l’interface n’est sauvegardé … pas cool.
Le modèle jarvis-qwen3, celui créé plus haut avec le Modelfile, est directement proposé dans le sélecteur.
Au passage, le mode « think » de Qwen3 se règle aussi ici : dans un chat, icône des réglages en haut à droite, Advanced Params, paramètre think (Ollama) ; ou par défaut pour un modèle, dans Panneau d’administration, Réglages, Modèles. Et pour une seule réponse, Qwen3 comprend aussi /no_think à la fin du message.
SearXNG : on donne des yeux à notre IA
Un modèle local, c’est bien, mais il ne connaît que ce qu’il a appris pendant son entraînement. Demandez à Qwen3 la dernière version d’Ollama, il vous répondra avec aplomb… une version d’il y a un an (ou il en inventera une). Pour qu’il sache ce qui se passe dans le monde, il lui faut un moteur de recherche. Et pas question d’aller brancher une API Google ou Bing : on reste chez nous, avec SearXNG, un métamoteur open source auto-hébergé qui interroge plein de moteurs à la fois sans vous pister.
SearXNG rejoint la stack d’Open WebUI, dans le même compose. Pas de port publié : Open WebUI le joint par son nom de service, sur le réseau privé de la stack. On ajoute ce service au docker-compose.yml :
|
1 2 3 4 5 6 7 8 9 10 11 |
searxng: image: docker.io/searxng/searxng:${SEARXNG_VERSION:-2026.10.30-ffa88e3ba} container_name: searxng restart: unless-stopped # Pas de port publié : Open WebUI le joint par son nom (http://searxng:8080). environment: - SEARXNG_SECRET=${SEARXNG_SECRET:?SEARXNG_SECRET manquante dans .env} - TZ=Europe/Paris volumes: - ./searxng:/etc/searxng - searxng-cache:/var/cache/searxng |
Sans oublier un depends_on: [searxng] sur Open WebUI et le volume searxng-cache: en bas du fichier. SearXNG publie une image par build, avec un tag daté : on épingle, comme pour Open WebUI.
Deux lignes de plus dans le .env :
|
1 2 |
cidou@viviane:~/openwebui$ echo "SEARXNG_VERSION=2026.10.30-ffa88e3ba" >> .env cidou@viviane:~/openwebui$ echo "SEARXNG_SECRET=$(openssl rand -hex 32)" >> .env |
Et le searxng/settings.yml, réduit au strict nécessaire :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
use_default_settings: true general: instance_name: "Jarvis Search" server: # secret_key fournie par la variable SEARXNG_SECRET (dans .env), jamais ici. limiter: false # usage privé, derrière Open WebUI : pas besoin de Valkey image_proxy: true search: safe_search: 0 default_lang: "fr" formats: - html - json # indispensable : sans json, Open WebUI reçoit un 403 # Moteurs Tor (.onion) : ne se chargent pas sans proxy Tor, on les retire pour des logs propres. engines: - name: ahmia inactive: true - name: torch inactive: true |
Le vrai test, c’est depuis le conteneur Open WebUI, pour vérifier qu’il joint bien SearXNG par son nom :
|
1 |
cidou@viviane:~/openwebui$ docker compose exec open-webui curl -s -o /dev/null -w '%{http_code}\n' "http://searxng:8080/search?q=vblog.io&format=json" |
Un 200, c’est gagné. Un 403, retour au json ci-dessus.
La recherche web dans Open WebUI, en mode automatique
Reste à dire à Open WebUI de se servir de SearXNG. Là encore, ce sont des réglages enregistrés en base, donc on passe par l’interface plutôt que par des variables.
D’abord, brancher le moteur : Panneau d’administration, Réglages, Web Search. On active la recherche web, on choisit searxng comme moteur, on met http://searxng:8080/search comme URL de requête, et on commence avec 3 résultats (chaque résultat est lu et injecté dans le contexte, donc plus de résultats, c’est plus de mémoire consommée et une réponse plus lente).

Ensuite, le piège dans lequel je suis tombé tête la première lors de ma première install : le Function Calling. En mode « Native », c’est le modèle qui doit décider lui-même d’appeler l’outil de recherche, et avec Qwen3 8B, ça ne marchait pas du tout : il me répondait gentiment (en fait pas vraiment ^^) qu’il ne pouvait pas accéder aux URLs. En mode « Default », c’est Open WebUI qui fait le boulot : il génère la requête, interroge SearXNG, lit les pages et injecte les résultats dans la conversation. Le modèle n’a plus qu’à rédiger. Pour un 8B, c’est nettement plus fiable. Donc : Admin Settings, Models, jarvis-qwen3, Advanced Params, Function Calling sur Default. Attention, ce réglage existe à trois niveaux (modèle, chat, utilisateur) et c’est le plus local qui gagne : vérifiez qu’aucun n’est resté sur Native.

Enfin, pour ne pas avoir à cliquer sur « Web Search » dans chaque conversation, on l’active par défaut sur le modèle, toujours dans Modèles, jarvis-qwen3 : on coche Web Search dans les Capabilities, et on l’active aussi dans les Default Features. Désormais, chaque nouvelle conversation avec ce modèle part avec la recherche web allumée.

Et … ça marche 🙂

Un chouïa laborieux 🙂 … et soyons honnêtes : Open WebUI cherche à chaque message, même pour un simple « bonjour ». Une IA qui décide toute seule quand chercher, ce sera pour plus tard, avec un modèle plus costaud… ou un peu plus de bidouille ^^.
Et cadeau bonus : taper # suivi d’une URL dans le message (par exemple #https://docs.ollama.com/faq) demande à Open WebUI de lire la page pour en discuter. Là, ce n’est pas SearXNG qui travaille, mais le « Web Loader » d’Open WebUI.
Les soucis qui m’ont pris du temps …
Parce qu’un tuto sans galère, ça n’existe pas :
– Dans les logs de SearXNG, des erreurs ahmia: can't register engine et torch: can't register engine. Rien de grave : ce sont deux moteurs du réseau Tor, qui ne peuvent pas se charger sans proxy Tor. On les retire avec inactive: true dans le settings.yml.
– Toujours dans les logs, missing config file: /etc/searxng/limiter.toml. Sans gravité non plus, puisque le limiter est désactivé. Un touch searxng/limiter.toml fait taire l’avertissement.
– SearXNG qui répond 403 à Open WebUI : le format json n’est pas activé.
– ENABLE_SIGNUP qui ne fait rien : le réglage est déjà en base, c’est l’interface qui commande.
– Qwen qui jure ne pas pouvoir lire les URLs : Function Calling en Native, à passer en Default.
– Les réponses qui ignorent la moitié des résultats de recherche : contexte trop court, d’où OLLAMA_CONTEXT_LENGTH=8192 côté Ollama.
On passe à la suite ?
Pour résumer ce premier chapitre : un Mac mini qui fait tourner Qwen3 8B sur son GPU grâce à Ollama, une interface Open WebUI en Docker Compose sur mon autre petit serveur, et un SearXNG à côté pour que tout ce petit monde puisse chercher sur le web, sans qu’aucune donnée ne sorte de chez moi (enfin, à part les requêtes de recherche elles-mêmes). Oui c’est laborieux (je l’ai déjà dit), oui tout ça pour ça … mais en fait c’est le début qui ne donne pas grand chose. Les prochains chapitres seront beaucoup plus intéressants, je vous le promets !
Si je vous dis workflow … n8n … notifications d’alerte de logs suspects sur le canal de votre choix … c’est déjà plus sexy, non ? … et toujours via une IA locale bien entendu…
Je suis en train d’écrire les chapitres 2 et 3 qui sortiront au fur et à mesure, sans doute la semaine prochaine.
Références :
– Ollama : https://ollama.com (documentation macOS : https://docs.ollama.com/macos
– FAQ Ollama : https://docs.ollama.com/faq)
– Modèle qwen3 : https://ollama.com/library/qwen3
– Open WebUI : https://docs.openwebui.com
– Open WebUI sur GitHub : https://github.com/open-webui/open-webui
– SearXNG : https://docs.searxng.org
– vBlog.io : Le gardien des parchemins numériques et son UDM Beast !
