19 septembre 2026

Checklist d’audit — Git Worktrees multi-instance

Objectif : auditer le setup actuel et vérifier qu’un nombre arbitraire de worktrees peut tourner simultanément sans collision, contamination croisée ou effet de bord.

Pour chaque point :

  • Vérifier l’implémentation réelle dans le projet
  • Indiquer OK, RISQUE, MANQUANT ou NON APPLICABLE
  • Citer les fichiers/configurations concernés
  • Ne modifier aucun fichier pendant l’audit
  • À la fin, produire une liste priorisée des problèmes trouvés

1. Git / Worktrees

  • Chaque worktree utilise une branche Git distincte
  • Le script empêche de réutiliser accidentellement une branche déjà montée dans un autre worktree
  • Le worktree principal est correctement distingué des worktrees secondaires
  • La suppression d’un worktree ne supprime pas une branche ou des données non fusionnées sans avertissement
  • git worktree prune est géré ou documenté
  • Les worktrees supprimés ne laissent pas d’entrées Git orphelines
  • Les fichiers non trackés importants ne sont pas supposés être partagés entre worktrees
  • Aucun script ne suppose implicitement que le dépôt courant est le worktree principal
  • Les chemins reposent sur la racine réelle du worktree et non sur un chemin absolu codé en dur
  • Les hooks Git éventuellement utilisés fonctionnent depuis tous les worktrees
  • Les scripts savent correctement retrouver le common Git dir et le worktree courant

2. Identité unique du worktree

  • Chaque worktree possède un identifiant unique et stable
  • Cet identifiant est dérivé de manière déterministe ou enregistré explicitement
  • Les noms contenant /, _, espaces ou caractères spéciaux sont normalisés
  • Deux branches dont les noms se ressemblent ne peuvent pas générer le même identifiant
  • Il existe une stratégie pour éviter les collisions après troncature des noms
  • L’identifiant est utilisé partout où une ressource doit être isolée

3. Variables d’environnement

  • Chaque worktree possède son propre .env
  • Le .env n’est jamais partagé par symlink sauf intention explicite
  • Le script initialise correctement un nouveau .env
  • Les secrets communs éventuels sont séparés des valeurs propres au worktree
  • APP_ENV est cohérent
  • APP_URL est propre au worktree
  • Les variables générées automatiquement sont idempotentes
  • Une relance du script ne détruit pas des modifications manuelles du .env
  • Les variables obligatoires manquantes sont détectées avant le démarrage

4. Hostnames / URLs

  • Chaque worktree possède un hostname distinct
  • Aucun hostname ne peut entrer en collision
  • La génération du hostname supporte correctement les noms de branches complexes
  • /etc/hosts, DNS local ou wildcard DNS est correctement géré
  • Les entrées obsolètes peuvent être nettoyées
  • Le reverse proxy route vers le bon worktree
  • HTTP et HTTPS sont gérés de manière cohérente
  • Les certificats locaux éventuels supportent les hostnames générés
  • Les redirects Laravel ne renvoient pas vers le hostname d’un autre worktree
  • Les URLs générées par les jobs/CLI utilisent le bon APP_URL

5. Ports

  • Tous les services nécessitant un port peuvent fonctionner simultanément
  • Le port HTTP est unique
  • Le port Vite est unique
  • Le port WebSocket/Reverb est unique
  • Les ports de debug éventuels sont uniques
  • Les ports des outils annexes sont uniques
  • L’allocation des ports résiste au lancement concurrent de deux créations de worktree
  • Un port déjà occupé est détecté avant démarrage
  • Les ports libérés peuvent être réutilisés sans collision
  • Aucun port fixe caché n’est présent dans les scripts/configurations

6. Base de données

  • Chaque worktree utilise une base isolée
  • Le nom de base est unique
  • Les migrations d’un worktree ne peuvent pas modifier la DB d’un autre
  • migrate:fresh, db:wipe ou équivalent ne peut pas toucher une autre instance
  • Les seeds sont exécutés sur la bonne DB
  • Les tests utilisent une DB propre au worktree
  • Les connexions secondaires éventuelles sont également isolées
  • Les DB de tests sont distinctes des DB de développement
  • La suppression du worktree gère explicitement le devenir de sa DB
  • Une protection existe contre la suppression accidentelle de la DB principale

7. Redis

  • Cache Redis isolé par worktree
  • Sessions Redis isolées
  • Queues Redis isolées
  • Locks Redis isolés
  • Rate limiting isolé si nécessaire
  • Broadcast/pub-sub isolé
  • Horizon utilise le bon namespace
  • Aucun flushdb ou flushall utilisé par un worktree ne peut casser les autres
  • Les clés applicatives possèdent un préfixe unique

8. Cache Laravel

  • CACHE_PREFIX est unique
  • php artisan cache:clear ne vide pas les données des autres worktrees
  • Le config cache est local au worktree
  • Le route cache est local au worktree
  • Le view cache est local au worktree
  • bootstrap/cache n’est pas partagé
  • storage/framework/cache n’est pas partagé

9. Sessions / Cookies

  • Nom du cookie de session unique
  • Domaine du cookie cohérent avec le hostname
  • Les sessions d’un worktree ne sont pas réutilisées dans un autre
  • Les cookies CSRF/XSRF ne provoquent pas de collisions
  • Les cookies d’authentification persistante sont isolés
  • Sanctum fonctionne correctement avec les domaines générés
  • Les domaines stateful Sanctum sont correctement définis

10. Queues

  • Chaque worktree possède sa queue ou son namespace
  • Un worker d’un worktree ne peut pas consommer les jobs d’un autre
  • Les failed jobs restent identifiables par worktree
  • Horizon sépare correctement les environnements
  • Les workers sont arrêtés lors de la suppression/arrêt d’un worktree
  • queue:restart d’un worktree ne perturbe pas les autres
  • Les jobs sérialisés ne dépendent pas d’un chemin absolu d’un autre worktree

11. Scheduler / Cron

  • Le scheduler ne s’exécute pas involontairement dans tous les worktrees
  • Une stratégie explicite détermine quels worktrees peuvent lancer le scheduler
  • withoutOverlapping() ne partage pas accidentellement ses locks entre instances
  • onOneServer() a le comportement attendu
  • Les tâches ayant des effets externes ne sont pas dupliquées
  • Les cron système éventuels ne pointent pas vers un ancien worktree

12. Storage / fichiers locaux

  • Chaque worktree possède son propre storage/
  • storage/app n’est pas partagé involontairement
  • storage/logs est distinct
  • storage/framework est distinct
  • Les uploads locaux ne contaminent pas les autres worktrees
  • Les fichiers temporaires sont isolés
  • Les exports/imports sont isolés
  • Les storage:link pointent vers le bon worktree
  • Les symlinks sont vérifiés après création
  • Aucun chemin absolu vers un ancien worktree n’est stocké

13. Logs

  • Les logs permettent d’identifier le worktree d’origine
  • Deux worktrees n’écrivent pas dans le même fichier de log sauf intention explicite
  • Les logs des workers sont distinguables
  • Les logs du scheduler sont distinguables
  • Les logs du reverse proxy permettent d’identifier l’instance
  • Le nom du worktree est injecté dans le contexte de logging si utile

14. Composer / PHP

  • Chaque worktree possède son propre vendor/
  • composer.lock correspond bien à chaque branche
  • Aucun vendor partagé ne peut être écrasé par un autre worktree
  • La version PHP utilisée est cohérente
  • Les extensions PHP requises sont disponibles
  • Les scripts Composer post-install ne touchent pas des ressources globales dangereuses
  • Les caches Composer éventuellement partagés sont uniquement des caches de téléchargement

15. Node / npm / Vite

  • Chaque worktree possède son propre node_modules
  • Le lockfile propre à la branche est respecté
  • Le serveur Vite possède un port unique
  • HMR fonctionne avec le hostname du worktree
  • Les URLs HMR/WebSocket sont correctes
  • Les fichiers générés dans public/build sont locaux au worktree
  • Un npm install dans un worktree ne modifie pas les dépendances d’un autre
  • Les caches npm/pnpm/yarn partagés ne contiennent que des artefacts sûrs à partager

16. Docker si utilisé

  • COMPOSE_PROJECT_NAME est unique par worktree
  • Aucun container_name fixe ne crée de collision
  • Les networks sont correctement namespacés
  • Les volumes sont correctement namespacés
  • Les bind mounts pointent vers le worktree courant
  • Les ports exposés sont uniques
  • docker compose down d’un worktree ne détruit pas les containers d’un autre
  • docker compose down -v ne supprime pas des volumes partagés importants
  • Les services volontairement partagés sont clairement identifiés
  • La destruction d’un worktree ne détruit jamais l’infrastructure commune

17. Services externes

Vérifier particulièrement :

  • Email
  • SMS
  • Stripe/paiements
  • Webhooks
  • OpenAI / LLM
  • Telegram
  • Slack
  • APIs métier
  • S3 / object storage
  • Search engines
  • OAuth providers

Pour chacun :

  • L’environnement de développement utilise sandbox/test quand disponible
  • Un worktree secondaire ne peut pas déclencher involontairement une action production
  • Les callbacks utilisent le hostname du bon worktree
  • Les webhook URLs peuvent distinguer les worktrees
  • Les idempotency keys ne créent pas de collisions
  • Les emails/SMS de test sont interceptés si nécessaire

18. OAuth / authentification externe

  • Les redirect URIs supportent les différents hostnames
  • Google/GitHub/etc. acceptent les URLs générées
  • Les secrets ne sont pas recopiés inutilement
  • L’état OAuth/state/PKCE reste associé au bon worktree
  • Un login initié sur WT-A ne termine pas sur WT-B

19. Services partagés

Pour chaque service partagé, documenter explicitement :

  • MySQL
  • Redis
  • Mailpit/Mailhog
  • Elasticsearch/Meilisearch
  • MinIO
  • RabbitMQ
  • reverse proxy
  • autres

Puis vérifier :

  • Ce qui est partagé
  • Ce qui est namespacé
  • Ce qui est totalement isolé
  • Ce qui se passe lors d’un reset
  • Ce qui se passe lors de la suppression d’un worktree

20. Tests automatisés

  • Deux suites de tests peuvent tourner simultanément
  • Elles n’utilisent pas la même DB
  • Elles ne partagent pas les mêmes fichiers temporaires
  • Elles ne partagent pas les mêmes ports
  • Elles ne détruisent pas les données de l’autre
  • Les tests browser/E2E ciblent le bon hostname
  • Dusk/Playwright/Cypress peuvent tourner en parallèle
  • Les screenshots et artefacts de tests sont séparés
  • Les mails/jobs/fakes sont correctement isolés

21. Commandes destructives

Chercher toutes les commandes du type :

  • rm -rf
  • DROP DATABASE
  • db:wipe
  • migrate:fresh
  • cache:clear
  • redis flush*
  • docker compose down -v
  • suppression de volumes
  • suppression de répertoires
  • suppression de hosts/vhosts

Pour chacune :

  • La cible est explicitement limitée au worktree courant
  • Le script refuse les valeurs vides
  • Le script refuse /
  • Le script refuse le repository principal si ce n’est pas explicitement demandé
  • Une variable vide ne peut jamais élargir une commande destructive
  • Une protection existe contre les erreurs de quoting shell

22. Concurrence du script de création

Simuler deux commandes lancées exactement en même temps.

Vérifier :

  • Allocation atomique des ports
  • Allocation atomique des DB
  • Allocation atomique des hostnames
  • Écriture concurrente dans /etc/hosts
  • Écriture concurrente dans nginx
  • Création concurrente de fichiers registry
  • Absence de race condition
  • Utilisation de lock/flock si nécessaire

23. Idempotence

Pour toutes les commandes principales :

  • create
  • setup
  • start
  • stop
  • restart
  • remove
  • cleanup

Vérifier :

  • Une deuxième exécution ne casse rien
  • Une exécution interrompue peut être reprise
  • Un état partiellement créé est détecté
  • L’outil sait distinguer déjà fait de erreur
  • Un rollback ou cleanup est possible après erreur

24. Processus orphelins

Après arrêt/suppression d’un worktree :

  • Aucun serveur PHP orphelin
  • Aucun php artisan serve
  • Aucun worker queue
  • Aucun Horizon
  • Aucun scheduler
  • Aucun Vite
  • Aucun WebSocket server
  • Aucun processus Node
  • Aucun container Docker
  • Aucun tmux/screen/process manager associé inutilement

25. Process management

  • Le PID de chaque service est traçable
  • Un stop ne tue pas un processus appartenant à un autre worktree
  • Aucun pkill php / killall node générique dangereux
  • Les processus sont identifiés par PID, working directory ou identifiant spécifique
  • Le redémarrage cible uniquement le worktree courant

26. Nginx / reverse proxy

  • Un fichier vhost distinct existe par worktree
  • Les noms de fichiers sont uniques
  • La configuration est validée avant reload (nginx -t)
  • Une erreur dans un worktree ne casse pas les autres vhosts
  • Le nettoyage retire uniquement le vhost concerné
  • root pointe vers le bon /public
  • PHP-FPM reçoit le bon SCRIPT_FILENAME
  • Les hostnames wildcard et regex ne routent pas vers la mauvaise instance

27. WSL

  • Les chemins Windows/WSL ne sont pas mélangés de façon fragile
  • Aucun chemin /mnt/c/... codé en dur si non nécessaire
  • Les permissions Linux sont conservées
  • Les symlinks fonctionnent correctement
  • Les scripts fonctionnent après redémarrage WSL
  • Les IP dynamiques WSL ne sont pas supposées fixes
  • Les processus lancés sont bien visibles/arrêtables depuis WSL
  • Le navigateur Windows peut résoudre les hostnames utilisés

28. Multitenancy Laravel

Puisque l’application est multi-tenant :

  • L’identification du tenant ne dépend pas accidentellement du hostname de worktree
  • Le hostname de développement et le hostname métier du tenant sont correctement distingués
  • Les domaines tenant sont isolés entre worktrees
  • Les tenants créés dans WT-A ne sont pas visibles dans WT-B sauf intention explicite
  • Les DB tenant sont correctement isolées
  • Les queues tenant-aware ne traversent pas les worktrees
  • Les caches tenant-aware incluent aussi l’identité du worktree si nécessaire
  • Les cookies multi-tenant ne fuient pas entre instances
  • Les URLs générées pour les tenants utilisent le domaine attendu
  • Les commandes artisan tenant-aware utilisent la DB du worktree courant

29. Agents IA travaillant simultanément

Si plusieurs Claude Code / Codex / agents travaillent en parallèle :

  • Un agent = un worktree
  • Un agent = une branche
  • Les responsabilités sont suffisamment séparées
  • Deux agents ne modifient pas volontairement le même fichier sauf nécessité
  • Les agents n’exécutent pas de commandes Git sur le mauvais worktree
  • Les agents vérifient pwd, branche et git status avant opérations importantes
  • Aucun agent ne fait git reset --hard sans contrôle
  • Aucun agent ne fait git clean -fd sans contrôle
  • Aucun agent ne force-push une branche partagée
  • Les commits sont fréquents et atomiques
  • Chaque agent laisse le worktree dans un état récupérable
  • Les conflits sont résolus lors de l’intégration, pas masqués

30. Intégration entre branches

  • Une stratégie d’intégration est définie : merge, rebase ou cherry-pick
  • Les branches sont régulièrement resynchronisées avec la branche de référence si nécessaire
  • Les modifications de migrations concurrentes sont détectées
  • Les modifications concurrentes de composer.json sont détectées
  • Les modifications concurrentes de package.json sont détectées
  • Les modifications concurrentes de .env.example sont détectées
  • Les modifications concurrentes des configs centrales sont détectées
  • Une suite de tests complète est lancée après intégration

31. Observabilité

Idéalement, pouvoir exécuter une commande donnant :

Worktree      Branch          Host                       DB              HTTP  Vite  Status
main          main            huynh-yvon.team-of.test    app_main        80    5173  UP
auth          feat/auth       auth.team-of.test          app_auth        ...   ...   UP
billing       feat/billing    billing.team-of.test       app_billing     ...   ...   DOWN

Vérifier :

  • Branche
  • chemin
  • hostname
  • DB
  • ports
  • PID/processus
  • containers
  • état nginx
  • état queue
  • état Vite

32. Nettoyage complet

Lors de la suppression d’un worktree, vérifier explicitement le traitement de :

  • worktree Git
  • branche Git
  • DB
  • DB de tests
  • .env
  • hostname
  • /etc/hosts
  • vhost nginx
  • certificat local
  • Redis keys
  • queues
  • workers
  • scheduler
  • Docker containers
  • Docker networks
  • Docker volumes
  • logs
  • fichiers temporaires
  • PID files
  • storage
  • node_modules
  • vendor

Pour chaque ressource, préciser si elle est :

  • supprimée automatiquement ;
  • conservée volontairement ;
  • supprimée uniquement avec --purge.

33. Protection du worktree principal

  • Le worktree principal est explicitement identifiable
  • Les commandes destructives ont des protections supplémentaires sur celui-ci
  • Impossible de supprimer le worktree principal par erreur
  • Impossible de supprimer sa DB par erreur
  • Impossible de supprimer son vhost par erreur
  • Impossible de le traiter comme une instance temporaire sans confirmation explicite

34. Scénarios à réellement tester

Ne pas se limiter à lire le code. Si possible, tester :

  • Créer WT-A
  • Créer WT-B
  • Créer WT-C
  • Démarrer les trois simultanément
  • Ouvrir les trois applications simultanément
  • Authentification différente dans chaque instance
  • Écrire des données différentes dans chaque DB
  • Lancer des migrations différentes
  • Lancer des queues simultanément
  • Lancer Vite simultanément
  • Exécuter les tests simultanément
  • Redémarrer WT-B sans toucher A/C
  • Supprimer WT-B sans toucher A/C
  • Recréer WT-B
  • Lancer simultanément deux créations de worktree
  • Tuer brutalement un setup en cours puis le relancer
  • Redémarrer WSL puis relancer les worktrees
  • Vérifier qu’aucun processus/port/configuration fantôme ne subsiste

35. Recherche de dépendances cachées

Effectuer une recherche globale dans le repository pour détecter :

  • localhost
  • 127.0.0.1
  • ports codés en dur
  • noms de DB codés en dur
  • noms Redis codés en dur
  • domaines codés en dur
  • chemins absolus
  • chemins vers /home/...
  • chemins vers /mnt/c/...
  • APP_URL
  • VITE_
  • REDIS_
  • DB_
  • QUEUE_
  • SESSION_
  • CACHE_
  • BROADCAST_
  • REVERB_
  • SANCTUM_
  • noms de containers
  • noms de volumes Docker

Identifier tout élément qui devrait dépendre de l’identité du worktree mais ne le fait pas.

Rapport final attendu

À la fin de l’audit, produire exactement ces sections :

1. Verdict global

Donner une note sur 10 pour la robustesse du système multi-worktree.

2. Problèmes critiques

Uniquement les problèmes susceptibles de :

  • corrompre des données ;
  • toucher un autre worktree ;
  • toucher la production ;
  • supprimer des ressources ;
  • provoquer des comportements non déterministes.

3. Risques moyens

Problèmes pouvant provoquer des collisions, bugs ou difficultés d’exploitation sans risque majeur de perte de données.

4. Améliorations mineures

Ergonomie, observabilité, nettoyage, simplification.

5. Éléments correctement implémentés

Lister ce qui a réellement été vérifié dans le code et qui est robuste.

6. Tests manquants

Lister les scénarios qui ne sont pas couverts actuellement.

7. Ressources non isolées

Créer un tableau :

RessourceIsoléePartagée volontairementRisque
DB
Redis
Cache
Sessions
Queues
Storage
Logs
HTTP
Vite
WebSockets
Scheduler
Services externes

8. Top 10 des actions recommandées

Classer par priorité réelle.

Règle importante pour cet audit

Ne suppose pas qu’un mécanisme fonctionne parce qu’un fichier ou une variable existe.

Trace réellement le flux :

création du worktree → génération config → démarrage → runtime → arrêt → suppression

et cherche particulièrement :

  • les ressources globales cachées ;
  • les variables partagées ;
  • les chemins absolus ;
  • les ports fixes ;
  • les commandes destructives ;
  • les race conditions ;
  • les processus orphelins ;
  • les effets de bord externes.

Le but n’est pas de confirmer que le système semble correct.

Le but est d’essayer activement de trouver une façon de le casser.

J’ai fait tourner un LLM 27B à 45 tokens/s sur une RTX 5060 Ti 16 Go

Prism LM ous offre un Qwen 27B 9x plus petit

Faire tourner un modèle de 27 milliards de paramètres en local avec seulement 16 Go de VRAM, ça paraît ambitieux.

C’est pourtant ce que j’ai réussi à faire avec Bonsai 2 27B de PrismML, une NVIDIA RTX 5060 Ti 16 Go, Windows et WSL2.

C’est intéressant pour un développeur car Qwen est considéré comme un remplaçant de Claude en open weight. Imaginez pouvoir coder avec un nombre de tokens infini !

Le résultat obtenu est plutôt impressionnant :

Prompt :     181.2 tokens/s
Génération : 45.4 tokens/s  << très bon en fait

Le modèle GGUF ne pèse qu’environ 7,2 Go, malgré ses quelque 26,9 milliards de paramètres.

Dans cet article, je ne vais pas détailler toutes les tentatives qui ont échoué. Je vais directement montrer la configuration qui a fonctionné.


La configuration utilisée

Ma machine :

  • Windows
  • WSL2
  • Ubuntu 26.04
  • NVIDIA RTX 5060 Ti
  • 16 Go de VRAM
  • 32 Go de RAM
  • CUDA Toolkit 13.4
  • Bonsai 2 27B PQ2_0

Premier test depuis WSL :

nvidia-smi

La RTX 5060 Ti est correctement reconnue avec environ 16 Go de VRAM.

Attention aux versions CUDA

Chez moi, nvidia-smi indique :

CUDA Version: 13.1

alors que :

nvcc --version

renvoie :

Cuda compilation tools, release 13.4, V13.4.92

Ce n’est pas contradictoire.

nvidia-smi donne une information liée au support CUDA du driver NVIDIA alors que nvcc indique la version du CUDA Toolkit réellement installé.


Installer CUDA sous WSL2

J’ai d’abord essayé d’installer l’ensemble du toolkit CUDA.

Mais sur Ubuntu 26.04, cela entraînait des dépendances supplémentaires, notamment autour des outils graphiques NVIDIA et de Nsight.

Pour compiler et exécuter Bonsai, je n’avais pas besoin de tout cela.

L’installation minimale qui a fonctionné :

sudo apt install -y cuda-compiler-13-4 cuda-libraries-dev-13-4

J’ai ensuite ajouté CUDA à mon environnement :

echo 'export PATH=/usr/local/cuda-13.4/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-13.4/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc

Puis :

nvcc --version

À ce stade, CUDA était opérationnel.


Installer Bonsai 2 27B

Point important : je n’ai finalement pas installé llama.cpp séparément.

Le projet Bonsai fournit les scripts et binaires dont il a besoin.

J’ai simplement cloné le dépôt :

cd ~
git clone https://github.com/PrismML-Eng/Bonsai-demo.git
cd Bonsai-demo

Puis lancé :

./setup.sh

Après téléchargement et préparation de l’environnement :

Setup complete!
BONSAI_FAMILY=bonsai2
BONSAI_MODEL=27B

Le dépôt contenait notamment son propre :

bin/cuda/llama-cli

C’est donc cette version qui est utilisée pour Bonsai.


Un modèle 27B dans seulement 7,2 Go

Le modèle installé est :

Ternary-Bonsai-2-27B-PQ2_0.gguf

Il se trouve dans :

~/Bonsai-demo/models/bonsai2-gguf/27B/

Le serveur rapporte environ :

26 895 998 464 paramètres
7 195 047 936 octets
PQ2_0
2.13 bpw

Donc environ 26,9 milliards de paramètres dans un fichier de seulement 7,2 Go.

C’est précisément ce qui rend ce type de modèle intéressant pour les GPU disposant de 16 Go de VRAM.


Lancer Bonsai 2

Le lancement est très simple :

cd ~/Bonsai-demo
./scripts/run_llama.sh

Le script sélectionne automatiquement le modèle et le backend CUDA.

Une fois chargé, on peut directement discuter avec le modèle depuis le terminal.


45 tokens/s sur une RTX 5060 Ti

C’était évidemment le test que j’attendais le plus.

Sur ma RTX 5060 Ti 16 Go :

Prompt: 181.2 t/s
Generation: 45.4 t/s

Il faut distinguer les deux valeurs.

181 tokens/s correspondent essentiellement au traitement du prompt initial.

45 tokens/s correspondent à la génération des nouveaux tokens.

Pour l’utilisation quotidienne, c’est surtout le deuxième chiffre qui compte.

Et à environ 45 tokens par seconde, l’utilisation interactive est très confortable.


Pourquoi Bonsai affiche-t-il son raisonnement ?

Au premier test, j’ai également vu apparaître quelque chose d’assez inhabituel :

[Start thinking]

We need to respond to user...

Puis le modèle analyse la demande avant de produire sa réponse.

Ce n’est pas un bug.

Bonsai 2 fonctionne avec un mécanisme de raisonnement et le script fourni conserve ce comportement.

Dans une interface utilisateur, on pourrait évidemment choisir de masquer cette partie pour ne présenter que la réponse finale.


Transformer Bonsai en serveur API local

Faire discuter le modèle dans un terminal est intéressant.

Mais mon véritable objectif est de pouvoir l’utiliser depuis d’autres applications et notamment depuis des agents de développement.

Bonsai fournit justement llama-server.

Le lancement standard est :

./scripts/start_llama_server.sh

Chez moi, le port 8080 était déjà utilisé par Stremio.

Pas de problème : le script permet de changer le port.

J’utilise donc :

PORT=8081 ./scripts/start_llama_server.sh

On peut vérifier immédiatement l’API :

curl http://localhost:8081/v1/models

Le serveur répond correctement et expose le modèle Bonsai.

J’ai également testé directement l’endpoint :

/v1/responses

avec une requête simple : HTTP 200.


Peut-on utiliser Bonsai avec Codex CLI ?

C’était mon test suivant.

J’utilise régulièrement Codex CLI et l’idée était séduisante :

Codex CLI
    ↓
Bonsai 2 27B
    ↓
RTX 5060 Ti

Autrement dit : remplacer le modèle distant par mon modèle local.

J’ai configuré Codex pour utiliser :

http://localhost:8081/v1

Codex reconnaît effectivement Bonsai.

L’écran indique bien :

model:
/home/yvonh/Bonsai-demo/models/bonsai2-gguf/27B/Ternary-Bonsai-2-27B-PQ2_0.gguf

Mais au premier prompt :

We're currently experiencing high demand,
which may cause temporary errors.

Étrange pour un modèle qui tourne… sur ma propre machine.


Le message « high demand » était trompeur

En regardant directement les logs de llama-server, j’ai découvert la véritable erreur :

unsupported Responses tool type 'namespace' skipped

ainsi que :

unsupported Responses tool type 'web_search' skipped

Et surtout :

Jinja Exception:
System message must be at the beginning.

Le serveur finissait donc par retourner une erreur HTTP 500.

Codex transformait simplement cette erreur en message générique :

We're currently experiencing high demand

Le problème n’était donc absolument pas une saturation de Bonsai.


Pourquoi Codex et Bonsai ne fonctionnent pas encore ensemble ?

Une requête simple sur /v1/responses fonctionne.

Mais Codex n’envoie pas une requête simple.

Un agent de développement manipule des outils, des commandes, des fichiers et différents types de messages.

Dans mon cas, Codex envoyait notamment des tools que le serveur Bonsai ne reconnaissait pas :

namespace
web_search

Le template utilisé par le modèle imposait également une contrainte sur la position du message system.

On se retrouve donc avec :

Codex CLI
     ↓
Responses API
     ↓
tools + instructions
     ↓
incompatibilité de format
     ↓
llama-server
     ↓
HTTP 500

Le problème est donc une incompatibilité entre les protocoles/templates, pas un manque de puissance du modèle.


Prochaine étape : utiliser Aider

Plutôt que de commencer immédiatement à écrire un proxy entre Codex et Bonsai, j’ai décidé de tester une solution plus simple : Aider.

Aider est également un agent de programmation utilisable directement depuis le terminal.

L’idée devient :

Aider
   ↓
API OpenAI-compatible
   ↓
llama-server
   ↓
Bonsai 2 27B
   ↓
RTX 5060 Ti

Je garde volontairement mes différents composants séparés :

/home/yvonh/
├── Bonsai-demo/     # modèle + serveur
├── aider-env/       # Aider
└── team-of/         # projet de développement

L’installation d’Aider se fera dans son propre environnement Python.


Ce que je retiens de cette expérience

Le résultat le plus intéressant n’est finalement même pas la configuration de WSL ou CUDA.

C’est ce chiffre :

Bonsai 2 27B
≈ 26,9 milliards de paramètres
≈ 7,2 Go de GGUF
RTX 5060 Ti 16 Go
≈ 45 tokens/s

Il devient donc parfaitement réaliste de faire tourner un modèle de près de 27 milliards de paramètres sur une carte graphique grand public de 16 Go.

Et avec 45 tokens/s, on ne parle pas seulement d’une démonstration technique.

Le modèle est suffisamment rapide pour être réellement utilisé.


Conclusion

Mon environnement final ressemble aujourd’hui à ceci :

Windows
   │
   └── WSL2
        │
        └── Ubuntu 26.04
             │
             ├── CUDA 13.4
             │
             └── Bonsai-demo
                    │
                    └── Bonsai 2 27B PQ2_0
                           │
                           └── RTX 5060 Ti 16 Go
                                  │
                                  └── ~45 tokens/s

La partie LLM local fonctionne.

La partie serveur API fonctionne.

Le prochain objectif est maintenant plus ambitieux : voir si ce 27B peut réellement remplacer une partie des modèles cloud dans mon workflow de développement.

Et notamment répondre à une question beaucoup plus intéressante que « est-ce que le modèle démarre ? » :

un modèle 27B local à 45 tokens/s est-il suffisamment bon pour travailler comme véritable agent de développement sur une base de code réelle ?

C’est ce que je vais tester avec Aider.


Mots-clés : Bonsai 2 27B, PrismML, RTX 5060 Ti, LLM local, llama.cpp, llama-server, WSL2, CUDA, GGUF, Aider, Codex CLI, IA locale, modèle 27B, NVIDIA 16 Go.

Faire tourner Pi Coding Agent avec un LLM local Bonsai dans WSL

Faire tourner Pi Coding Agent avec un LLM local Bonsai dans WSL

Je voulais utiliser un LLM local pour coder dans WSL, avec un vrai agent capable de lire les fichiers, lancer des commandes et travailler directement sur un projet.

J’ai d’abord testé Aider, mais pour mon besoin, Pi Coding Agent s’est révélé plus adapté grâce à son approche orientée outils.

Voici la configuration qui a fonctionné.

1. Mon setup

J’utilise :

  • WSL
  • un modèle local Bonsai 27B au format GGUF
  • llama.cpp comme serveur local
  • Pi Coding Agent comme agent de code

Mon modèle :

/home/yvonh/Bonsai-demo/models/bonsai2-gguf/27B/Ternary-Bonsai-2-27B-PQ2_0.gguf

Mon binaire llama.cpp :

/home/yvonh/Bonsai-demo/bin/cuda/llama-server

2. Lancer Bonsai avec llama.cpp

Attention c’est le llma.cpp propre à Bonsai, ce n’est pas le llam.cpp que vous voyez habituellement.

La commande fonctionnelle est :

/home/yvonh/Bonsai-demo/bin/cuda/llama-server -m /home/yvonh/Bonsai-demo/models/bonsai2-gguf/27B/Ternary-Bonsai-2-27B-PQ2_0.gguf --ctx-size 32768 --port 8081

Le serveur expose ensuite une API compatible OpenAI sur :

http://localhost:8081/v1

Pour vérifier que le modèle est bien exposé :

curl http://localhost:8081/v1/models

3. Attention à la fenêtre de contexte

Au départ, le serveur tournait avec une fenêtre de contexte de :

16384 tokens

Pi envoyait une requête d’environ 17 852 tokens, ce qui provoquait l’erreur :

400 request exceeds the available context size

La solution a été d’augmenter le contexte de llama.cpp :

--ctx-size 32768

Il faut aussi déclarer la même taille côté Pi.

Par exemple dans :

~/.pi/agent/models.json

j’utilise notamment :

{
  "contextWindow": 32768,
  "maxTokens": 4096
}

Il est important de comprendre que modifier uniquement contextWindow dans Pi ne suffit pas.

La vraie limite est définie côté llama.cpp.

4. Installer Pi Coding Agent

Dans WSL :

npm install -g @mariozechner/pi-coding-agent

Puis :

pi --version

Et dans un projet :

cd ~/mon-projet
pi

5. Configurer Bonsai dans Pi

Pi peut utiliser un provider compatible OpenAI.

Le fichier se trouve ici :

~/.pi/agent/models.json

Exemple de configuration :

{
  "providers": {
    "bonsai": {
      "baseUrl": "http://localhost:8081/v1",
      "api": "openai-completions",
      "apiKey": "dummy",
      "models": [
        {
          "id": "/home/yvonh/Bonsai-demo/models/bonsai2-gguf/27B/Ternary-Bonsai-2-27B-PQ2_0.gguf",
          "name": "Bonsai 2 27B Local",
          "reasoning": false,
          "contextWindow": 32768,
          "maxTokens": 4096,
          "cost": {
            "input": 0,
            "output": 0,
            "cacheRead": 0,
            "cacheWrite": 0
          }
        }
      ]
    }
  }
}

Ensuite, dans Pi :

/model

et il suffit de sélectionner :

Bonsai 2 27B Local

6. Pourquoi Pi plutôt qu’Aider dans mon cas

Aider fonctionne très bien pour modifier du code, mais son fonctionnement repose surtout sur un protocole d’édition.

Pi correspond davantage à ce que je cherchais : un agent capable d’utiliser des outils.

Il peut notamment :

  • lire les fichiers
  • inspecter le projet
  • lancer des commandes shell
  • utiliser git
  • lancer des tests
  • modifier des fichiers
  • observer le résultat
  • continuer son travail

Le fonctionnement ressemble davantage à :

LLM
 ↓
appel d’un outil
 ↓
résultat
 ↓
nouvelle décision
 ↓
nouvel appel d’outil

C’est beaucoup plus proche de l’expérience Codex CLI ou Claude Code.

7. Le script que j’utilise

Pour éviter de retaper toute la commande llama.cpp, j’ai créé :

~/start-bonsai.sh

avec ce contenu :

#!/bin/bash

LLAMA_SERVER="/home/yvonh/Bonsai-demo/bin/cuda/llama-server"
MODEL="/home/yvonh/Bonsai-demo/models/bonsai2-gguf/27B/Ternary-Bonsai-2-27B-PQ2_0.gguf"

CTX_SIZE=32768

while [[ $# -gt 0 ]]; do
  case "$1" in
    --ctx)
      CTX_SIZE="$2"
      shift 2
      ;;
    *)
      echo "Option inconnue : $1"
      echo "Usage: $0 [--ctx taille]"
      exit 1
      ;;
  esac
done

"$LLAMA_SERVER" \
  -m "$MODEL" \
  --ctx-size "$CTX_SIZE" \
  --port 8081

Puis :

chmod +x ~/start-bonsai.sh

Je peux maintenant lancer Bonsai simplement :

~/start-bonsai.sh

avec 32768 tokens par défaut.

Ou choisir une autre taille :

~/start-bonsai.sh --ctx 65536

8. Workflow final

Mon workflow est maintenant très simple.

Premier terminal :

~/start-bonsai.sh --ctx 32768

Deuxième terminal :

cd ~/mon-projet
pi

Puis je sélectionne Bonsai dans :

/model

J’obtiens ainsi un agent de code local fonctionnant avec un modèle GGUF, llama.cpp et Pi directement dans WSL.

Conclusion

La combinaison qui fonctionne bien pour moi est :

Bonsai 27B GGUF
        ↓
    llama.cpp
        ↓
API OpenAI compatible
        ↓
Pi Coding Agent
        ↓
fichiers / shell / git / tests

Le point le plus important à retenir concerne la fenêtre de contexte.

Si Pi annonce 32k mais que llama.cpp tourne encore avec 16k, les requêtes échoueront dès que le contexte devient trop gros.

Il faut donc garder les deux configurations cohérentes :

llama.cpp : --ctx-size 32768
Pi        : contextWindow 32768
Pi        : maxTokens 4096

Avec cette configuration, j’ai pu utiliser mon Bonsai local comme véritable agent de développement dans WSL.