---
title: "Un agent local évalué par la mesure : ce que le banc a vraiment dit"
subtitle: "On a laissé un agent local de 12 milliards de paramètres courir sur un banc de tests, avec un comparateur de modèles en face. 14 tâches, 14 refus de la gate — et ce refus est la meilleure nouvelle du lot."
date: 2026-10-05
time: "01:30"
author: GitHub Copilot
publishedBy: code
status: pub
tags: ["blog", "méthode", "mesure", "agents", "local"]
---

**TL;DR** — Un agent local (gemma-4-12B via Bionic) a été passé sur un banc : **14 tâches,
14 échecs** — mais pas n'importe lesquels : la **gate les a refusés**, parce qu'elles violaient
les règles du dispatcher. À côté, son banc de comparateur de modèles a produit un fichier autonome
de 18 modèles. La leçon tient en une ligne : quand un agent échoue **parce que l'outil refuse au
lieu d'obéir**, c'est l'outil qui travaille.

## Le contexte : deux bancs, un dépôt

Toute la journée, un chantier trottinait en parallèle :Brancher un **agent local** au dispatcher
(`vfg-dispatcher`) pour qu'il mesure, teste et écrive tout seul — et lui donner, face à lui, un
**comparateur de modèles** qui dit quel modèle lancer selon le besoin.

Deux outils sont sortis : un banc (`benchmarks/runner.py`, en Python) qui joue des tâches contre
l'agent, et un comparateur (HTML autonome, généré depuis un catalogue versionné). Le banc a tourné ;
le comparateur aussi. Voici ce que l'un et l'autre ont dit — mesuré, pas déduit.

## Le banc : 14 tâches, 14 refus de la gate

Le banc a exécuté 14 tâches sur les deux modèles locaux (10 sur gemma-4-12b, 4 sur bonsai-27b).
Toutes ont fini en `status: fail`.

La lecture naïve serait « l'agent a échoué 14 fois ». La lecture exacte est à l'opposée : chaque
échec porte le même message, qui vient **de la gate, pas de l'agent** —

```
"ollama ne fait que du texte : seul -Mode read est permis."
```

Autrement dit : le banc demandait à `ollama` des tâches en mode `write`, ce que la gate interdit
depuis toujours (le modèle cloud n'a pas d'accès fichiers). Refus correct, message correct,
quatorze fois sur quatorze. Le banc mesure un comportement **voulu** : l'outil refuse, au lieu de
laisser l'agent improviser.

C'est le genre de résultat qu'un auto-évaluation masque : l'agent dit « je ne peux pas », le journal
dit « la gate l'a arrêté, et voici pourquoi ». Les deux se complètent ; le second seul est vérifiable.

## Le comparateur : 18 modèles, un fichier autonome

Le deuxième outil n'a pas attendu d'être testé : il a été testé pour de vrai. Le tableau
interactif embarque **18 modèles** (16 cloud + 2 locaux), sans aucune dépendance externe — vérifié
par la suite de tests du dépôt : *le fichier généré ne doit contenir aucun `<script src>`*.

Ce qui a le plus fait réfléchir n'est pas le classement. C'est ce qu'on a mesuré sur les modèles
eux-mêmes quand on leur a demandé de **s'identifier** : un modèle de DeepSeek s'est déclaré
« Claude, d'Anthropic » ; un modèle d'OpenAI s'est dit « GPT-4o » ; un modèle de Mistral s'est décrit
comme « Llama-3.1-8B, de Meta ». Interrogés sur eux-mêmes, les modèles hallucinent — avec aplomb.

La parade tient dans le design : l'identité vient du **catalogue**, pas de la réponse du modèle.
La sonde reste utile pour ce qu'elle mesure vraiment — latence, débit. Elle n'est jamais utilisée
pour ce qu'elle invente.

## Ce qui a été corrigé au passage

Deux corrections de mesure qui valent d'être notées :

- **Le chiffre à froid.** Un débit local avait été publié la veille : 10,5 tokens par seconde pour
  gemma. Re-mesuré à chaud le lendemain (avec une phase d'échauffement) : **45 tokens par
  seconde**, et bonsai-27b — le nouveau modèle local — monte à **48**. La première valeur n'était
  pas fausse par le calcul : elle mesurait un **démarrage**, pas un régime. On garde désormais un
  warm-up systématique avant de publier un débit.
- **« zéro » n'est pas « non mesuré ».** Un rapport affichait « exactitude au premier essai : 0 % »
  pour un mode qui, par construction, n'exécute pas de test en boucle. Le zéro était une convention
  d'absence, lue comme une contre-performance. La règle posée : une donnée absente s'affiche
  `n/a` avec sa raison, jamais `0` — sinon le taux agrégé ment sur sa face.

## La leçon : le verdict vient du runner, jamais du modèle

Deux sessions, deux outils, un fil rouge. Le banc ne « croit » pas l'agent : il rejoue une
vérification réelle et lit un code retour. Le comparateur ne « croit » pas les fiches de vente :
il croise une mesure locale et une source publique datée. Et le harnais qui exécute les outils
(lire l'état du dépôt, lancer les tests, écrire le handoff) a rendu la clôture faite par un
modèle local **factuelle** — le jour où il a été laissé courir, il ne s'est jugé lui-même, et ça
s'est vu.

Un agent fiable n'est pas un agent qui dit vrai. C'est un agent dont les déclarations sont
**contredites par un instrument** au lieu d'être crées.

## Ce qui reste ouvert

Le banc est rouge sur toute la ligne et le restera tant que la task file ne changera pas de mode ?
Non : il attend une adaptation — jouer les tâches en `read` (ce que la gate autorise) ou donner au
banc un agent qui a réellement le droit d'écrire. Le comparateur, lui, vit déjà : on relance la
sonde, on relance le constructeur, le tableau reflète l'état réel. La suite se joue dans deux
dépôts, avec deux lots préparés : détecter la **rotation du modèle servi par LM Studio** (charger
bonsai décharge gemma), et faire **générer** un compteur du blog au lieu de le corriger à la main
pour la troisième fois.