Étude de cas · architecture

D'excellents organes, aucun système nerveux.

Neuf applications d'IA locale, toutes fonctionnelles, toutes utiles — et ensemble environ 208 Go de poids de modèles redondants sur une machine qui en compte 128. Le récit d'une décision d'architecte : ne pas écrire la dixième application.

Contexte

Chaque application avait été construite seule, et bien : un RAG documentaire, un agent de code, une synthèse vocale, un pipeline musical, un moteur de lore, un analyseur de partitions. Chacune chargeait ses propres modèles au démarrage, gérait sa propre mémoire, et fonctionnait parfaitement — tant qu'on n'en lançait qu'une.

Le total des poids chargés, si toutes tournaient ensemble, dépassait de loin la mémoire physique. En pratique on jonglait : arrêter le RAG pour lancer le pipeline audio, redémarrer l'agent après. Le système marchait ; l'utilisateur compensait à la main.

La contrainte

La mémoire unifiée d'Apple Silicon est ce qui rend l'inférence locale viable — le GPU adresse la même RAM que le CPU, sans copie. C'est aussi ce qui rend le problème inévitable : cette mémoire est partagée avec tout le reste, navigateur et environnement de développement compris. On ne peut pas la cloisonner par application.

La décision

Le réflexe naturel était d'écrire une application de plus : un lanceur, un tableau de bord, quelque chose qui coordonne. C'est ce que j'ai failli faire.

Le diagnostic était ailleurs. Le problème n'était pas l'absence d'une fonctionnalité, c'était l'absence d'une couche. Neuf applications qui ne se parlent pas ne forment pas un système : ce sont d'excellents organes sans système nerveux.

La décision n'a pas été d'écrire une app de plus, mais de bâtir l'infrastructure qui valorise tout l'existant.

Concrètement : un gateway qui possède les modèles. Les applications ne chargent plus rien — elles demandent. Un budget mémoire unique et explicite, une politique d'éviction LRU, un modèle épinglable qu'on ne décharge jamais.

Ce qui a résisté

Un incident d'OOM. Le gateway tenait sa propre comptabilité : il savait ce qu'il avait chargé, donc il croyait savoir ce qui restait libre. Le système, lui, savait autre chose — d'autres processus consommaient de la mémoire dont le gateway ignorait l'existence. La règle qui en est sortie : mesurer la RAM réellement libre, au lieu de faire confiance à son propre plan comptable.

Un redémarrage groupé. Relancer quatre services d'un coup après une mise à jour a produit une ruée simultanée sur la mémoire. La règle inverse s'est imposée : service par service, jamais en lot.

Un modèle résident que rien ne consommait. Un modèle avait été ajouté à la flotte, épinglé, et jamais appelé par aucune application. Il occupait 37 Go. Un A/B l'a comparé à celui qui faisait déjà le travail : il perdait, et il était environ dix fois plus lent. Décommissionné, 37 Go rendus. La leçon vaut au-delà du cas : un modèle résident se justifie par ses appels, pas par ses capacités.

Le résultat, mesuré

AvantAprès
Poids redondants en mémoire~208 Go0
Budget mémoireimplicite, par app80 Go, unique et explicite
Applications chargeant leurs poids90
RAM rendue par décommissionnement37 Go
Tests sur l'ordonnanceur295

Le cœur de cet ordonnanceur a été extrait en open source, sans aucune dépendance : la question « quel modèle décharger sous budget partagé » se pose bien au-delà de MLX.

Transposable chez vous ?

C'est le périmètre de la prestation Build. Le signe qui doit alerter n'est pas le nombre de gigaoctets — c'est le moment où vos utilisateurs commencent à coordonner les services à la main. Arrêter A pour lancer B, c'est le symptôme d'une couche manquante, pas d'une fonctionnalité manquante.

Et la question à se poser avant d'écrire l'application suivante : est-ce que je répare un manque, ou est-ce que j'ajoute un organe de plus à un corps sans système nerveux ?

Vos données ne peuvent pas sortir ?

C'est exactement le problème que je résous. Un appel de 30 minutes suffit à cadrer un audit.