Chapitre 22 / 39

IA on-prem

Le Framework Desktop dans la salle serveur fait tourner les modèles d’IA en local. Quand Rani, Paul ou Jack demandent au système de faire quelque chose — rédiger une lettre, consulter un client, générer un brief — l’IA tourne à Gennevilliers, pas chez un fournisseur cloud. Tout est sur le matériel que Sodimo possède déjà.

Statut : Bloqué — les modèles sont sélectionnés et testés dans sodimo/harness (le dépôt harness de l’OS). Le Framework Desktop est le prérequis pour passer en production.


Trois paliers locaux

Le système choisit le bon palier pour chaque requête. L’utilisateur n’a pas à choisir.

Palier rapide — le palier par défaut pour la plupart des requêtes. Assez rapide pour qu’une réponse arrive en moins de 30 secondes.

Utilisé par :

  • Les briefs du matin (Rani, Paul, Jack)
  • Les briefs d’appel
  • Les alertes churn
  • La liste de prospection quotidienne
  • Les requêtes courantes

Palier qualité — pour les synthèses où la précision est critique et où un temps de réponse plus long est acceptable.

Utilisé par :

  • Le rapport mensuel pour Michel
  • L’analyse du portefeuille de contrats
  • La synthèse des debriefs de visite sur une semaine d’appels

Palier ensemble — pour les analyses à plus hauts enjeux. Trois modèles génèrent des évaluations indépendantes, chacune est mise en débat par un avocat du diable, puis les résultats sont synthétisés en un seul. C’est le palier le plus long.

Utilisé par :

  • L’analyse d’opportunité sur une affaire ou un renouvellement
  • La revue d’un contrat à enjeux élevés avant signature

La sélection des modèles au sein de chaque palier est gérée dans sodimo/harness et testée sur des charges de travail Sodimo réelles avant déploiement. Le palier est le contrat avec l’utilisateur ; le modèle spécifique qui le sous-tend peut être remplacé sans changer la façon dont quiconque travaille.


Le runtime sous-jacent

Les modèles locaux tournent sous llama.cpp, routés via llama-swap pour qu’un seul processus puisse héberger plusieurs modèles et permuter entre eux à la demande. Il y a un seul runtime sur la machine, testé sur le GPU AMD exact, épinglé à une version connue-bonne. Les versions upstream ne s’auto-mettent pas à jour ; un changement de version est une modification délibérée de sodimo/dotfiles.

Image · ghcr.io/mostlygeek/llama-swap:vulkan — posture Vulkan uniquement. L’image n’embarque pas de binaire ramalama ; llama-swap exécute /app/llama-server directement. Les vestiges de compatibilité ROCm (/dev/kfd, HSA_OVERRIDE_GFX_VERSION=11.0.0) restent dans le conteneur pour une réactivation future et sont suivis dans sodimo/dotfiles#14.

Inventaire des modèles (post 2026-04-22) :

AliasModèleRôle
local-taskqwen3-4b (Vulkan GGUF)chat par défaut, usage d’outils
local-heavygpt-oss-120b unsloth UD-Q8_K_XL (GGUF 2 shards, 65k ctx, reasoning_effort=high)raisonnement lourd ; différé jusqu’à l’atterrissage du kernel cmdline harness#11 + téléchargement des 100 Go de GGUF
local-coderqwen3-coder-30b (TTL=600s, groupe heavy)tâches de code
local-embeddingsqwen3-embedding-8b (TTL=0)embeddings RAG (voir OpenWebUI)

Posture runtime Strix Halo

Chaque bloc cmd: de modèle dans llama-swap.yaml porte l’ensemble de flags Strix-Halo obligatoires de kyuz0. Justification par flag :

FlagPourquoi
--no-mmapL’iGPU Strix Halo veut de la VRAM contiguë ; mmap la fragmente
-fa onflash attention
-ngl 999offloader chaque couche sur le GPU
--batch-size 4096 --ubatch-size 512throughput réglé pour Strix Halo
--cache-type-k q8_0 --cache-type-v q8_0cache KV Q8 — ~2x de contexte effectif
--jinjaformat de chat Jinja-templatisé
--direct-io --cache-prompt --cache-reuse 256cache de prompt
--threads 12Strix Halo a 16 cœurs ; 12 pour l’inférence laisse 4 pour l’hôte

La fiche opérationnelle (ports, env, volumes, digest d’image exact) vit dans Référence quadlets → llama-swap. Ce chapitre possède le pourquoi ; le 38 possède le câblage.

Règle du self-proxy. Dans llama-swap.yaml, le proxy: de chaque modèle doit cibler http://127.0.0.1:NNNN, jamais http://llama-swap:NNNN. llama-swap et le llama-server spawné partagent un netns — utiliser le hostname du conteneur force un aller-retour DNS netavark inutile qui produit des 502 sous l’DNAT adguard on-box.

Compromis de driver Vulkan. L’image mostlygeek embarque RADV. kyuz0 recommande AMDVLK pour les charges de travail intensives en prompt / long contexte. Chemin de montée en version = image gateway personnalisée avec amdvlk-2025.Q2.1.rpm en couche. Différé.

Référence kyuz0. Toolbox Strix-Halo épinglée au commit 1421e870… ; notes d’interpolation complètes dans sodimo/dotfiles/docs/kyuz0-toolbox.md, procédure de resync dans docs/resync-runbook.md.


Smoke benchmark — 2026-04-22

De bout en bout via OpenWebUI → LiteLLM → llama-swap → llama-server sur le Framework Desktop de dev (même silicium que le harness cible) :

  • local-task (qwen3-4b) : 53 tok/s sur RADV Vulkan, 11,5 s à froid, 0,08–0,20 s à chaud.
  • local-heavy (gpt-oss-120b) : différé — téléchargement de 100 Go de GGUF + verrou kernel cmdline (harness#11).

Référence croisée : OpenWebUI.


Politique d’escalade et compteur d’utilisation

Routage par défaut

Chaque invocation IA route d’abord vers la stack on-prem. Le mapping alias-vers-palier est fixe :

AliasPalierUsage par défaut
local-taskRapideChat, usage d’outils, courtes rédactions
local-heavyQualitéRaisonnement, synthèse, rapports pour le conseil
local-coderQualitéGénération et revue de code
cloud-heavyCloud (Claude Opus)Escalade opt-in uniquement

cloud-heavy n’est pas un repli que le système atteint seul ; c’est un opt-in délibéré par invocation. L’appelant doit passer escalate: "cloud-heavy" dans l’invocation d’outil. Rien n’escalade silencieusement.

Déclencheurs d’escalade

Quatre conditions justifient une escalade explicite vers cloud-heavy :

  1. Flag explicite — l’invocation de skill, d’agent ou d’outil porte escalate: "cloud-heavy". C’est le déclencheur principal ; les autres sont de la documentation, pas de l’automatisation.
  2. Débordement de fenêtre de contexte — le prompt assemblé dépasse la fenêtre de contexte du modèle local (65 k tokens pour local-heavy ; moins pour les autres paliers). L’appelant est responsable de détecter ce cas avant le dispatch.
  3. Régression qualitative sur une classe de tâches — une classe de tâches spécifique (ex. revue de contrat juridique) a été caractérisée comme nécessitant une sortie de qualité cloud et est marquée dans le manifeste du skill. Le skill définit escalate: "cloud-heavy" inconditionnellement pour cette classe.
  4. Exigence réglementaire — livrables d’audit ou avis juridiques où l’opérateur a besoin d’une version de modèle documentée et émise par un fournisseur cloud pour des raisons de chaîne de custody.

Aucun autre déclencheur n’est reconnu. « C’était lent » ou « la réponse locale semblait courte » ne sont pas des déclencheurs d’escalade ; ce sont des retours pour le processus de sélection de modèle dans sodimo/harness.

Compteur d’utilisation

Chaque requête qui route via llama-swap émet une ligne dans run_ledger via l’outil Worker ledger_write (voir ch42 Ce à quoi l’IA peut accéder). Le schéma de ligne correspond exactement au Principe 2 :

ChampValeur pour les exécutions locales
modelalias (local-task, local-heavy, local-coder)
providerlocal
tokens_innombre de tokens du prompt depuis llama-server
tokens_outnombre de tokens de complétion
latency_msdurée horloge murale du dispatch au premier token
cost_eur0 — les exécutions on-prem ne portent aucun coût par token
cost_eur_if_cloudcontrefactuel : ce qu’aurait coûté le même prompt+complétion au tarif Claude Opus

La colonne cost_eur_if_cloud est le chiffre des économies. Cumulée sur toutes les exécutions locales, SUM(cost_eur_if_cloud) - SUM(cost_eur) donne le titre. Une seule requête SQL contre run_ledger.

Les lignes d’escalade cloud enregistrent le coût réel dans cost_eur ; cost_eur_if_cloud égale cost_eur pour ces lignes (pas d’économie contrefactuelle sur une exécution cloud).

Surface

Le compteur cumulatif — total des exécutions, total des tokens de prompt, total des tokens de complétion, coût cloud implicite économisé — est affiché sur la tuile launchpad (ch61 Launchpad) et sur un graphique des 7 derniers jours sur une page adjacente aux avancements. La surface exacte est un choix ouvert de l’opérateur ; suivi comme point de suite.

L’export Grafana / Prometheus / K3s-style est différé. Le registre contient déjà les données ; un dashboard Grafana nécessiterait un bridge D1-vers-Prometheus sans valeur analytique supplémentaire à la v1. Réévaluation suivie comme Pivot 5a.

Pointer. → Principe 2, ch15 Principes de conception pour la logique de routage ; ch42 Ce à quoi l’IA peut accéder pour le schéma ledger_write ; ch55 D-188 (routage local-first), D-189 (compteur d’utilisation).


Quand le système escalade vers le cloud

Deux déclencheurs envoient une requête unique à Claude (Anthropic) plutôt qu’à la stack locale :

  1. L’encours du client dépasse 5 000 € — enjeux plus élevés, exigence plus haute pour le brouillon.
  2. Le modèle local signale un faible niveau de confiance dans sa propre réponse.

Dans les deux cas, l’escalade est automatique, journalisée et visible sur le dashboard des coûts. L’escalade vers le cloud utilise uniquement Claude, par conception. Aucun autre fournisseur cloud n’est branché.


Où tourne l’IA — et pourquoi

Faire tourner l’IA sur le Framework Desktop signifie que les données ne quittent jamais Gennevilliers. Les informations clients, les soldes AR, les détails des contrats et le contenu des emails restent dans le bâtiment. Le matériel est un coût unique que Sodimo a déjà absorbé ; l’usage quotidien de l’IA n’entraîne aucune facturation à la requête.

Objectif : 70 % des requêtes IA traitées en local. Le dashboard suit cela en temps réel.