Vérifier que les outils partent dans un ordre stable
Depuis le ticket 53, mm envoie les outils de chaque requête triés par nom, pour qu'un serveur llama.cpp puisse réutiliser le prompt qu'il a mis en cache à la question précédente. Ce guide montre comment vérifier cette modification : rien n'a régressé, l'ordre est stable, et combien de lecture de prompt elle économise sur votre serveur et votre modèle.
Il suppose un clone de mm, la chaîne d'outils Go, python3, et un Docker Model Runner ou un llama-server lancé avec un modèle qui sait appeler des outils.
1. Lancer la suite de tests
Depuis la racine du dépôt :
go test ./...
Chaque paquet doit répondre ok. Lancez ensuite les tests de cette modification seuls, plusieurs fois — le bug était aléatoire, un seul passage au vert ne prouve pas grand-chose :
go test ./internal/engine -run 'TestToolsGoOut|TestStableTools' -count=10 -v
| Test | Ce qu'il vérifie |
|---|---|
TestToolsGoOutInTheSameOrderOnEveryQuestion |
Douze questions à travers le vrai fournisseur llamacpp, face à un faux serveur qui enregistre les requêtes : chacune porte les sept outils triés par nom. |
TestStableToolsSortsByName |
La liste sort triée. |
TestStableToolsLeavesTheOriginalAlone |
La tranche qui appartient à Genkit n'est pas réordonnée sur place. |
TestStableToolsIgnoresTheIncomingOrder |
Toutes les rotations des mêmes outils donnent la même liste. |
TestStableToolsWithNoneOrOne |
Une requête sans outils (la requête de résumé de /compact) n'est pas modifiée. |
Pour voir le premier test attraper le bug, retirez la ligne params.Request.Tools = stableTools(params.Request.Tools) de internal/engine/engine.go et relancez-le : il échoue avec des lignes comme question 2 sent the tools as [plan fetch_content bash …], want [bash edit_file …]. Remettez la ligne.
2. Construire le binaire d'avant et celui d'après
go build -o /tmp/mm-after .
git worktree add /tmp/mm-before-src 1fa0c42
(cd /tmp/mm-before-src && go build -o /tmp/mm-before .)
git worktree remove /tmp/mm-before-src
1fa0c42 est le dernier commit sans le correctif ; n'importe quel commit plus ancien convient aussi.
3. Lancer le proxy d'enregistrement
Dans un second terminal, lancez tests/tool-order/proxy.py devant le serveur de modèles :
python3 tests/tool-order/proxy.py --port 18770 --upstream http://localhost:12434
Il affiche proxy on http://127.0.0.1:18770 -> http://localhost:12434, puis une ligne par requête dès que mm interroge le modèle.
4. Préparer une configuration qui passe par le proxy
sed -e 's#^baseUrl:.*#baseUrl: http://127.0.0.1:18770/engines/v1#' \
-e 's#^fallback:.*#fallback: ""#' \
-e "s#^skillsDir:.*#skillsDir: $PWD/skills#" \
agent.dmr.yaml > /tmp/agent.proxy.yaml
fallback: "" garde mm sur le proxy même si la sonde est lente. Le dossier des skills est rendu absolu parce qu'en mode éditeur un chemin relatif est lu depuis le dossier du fichier de configuration : sans cela, read_skill manquerait et la requête ne serait pas celle de tous les jours.
5. Poser les mêmes questions avec chaque binaire
tests/tool-order/session.py pose plusieurs questions dans une même session, par le protocole des éditeurs (mm -acp) :
python3 tests/tool-order/session.py /tmp/mm-before /tmp/agent.proxy.yaml \
"Answer in one short sentence: what is Go?" \
"Answer in one short sentence: what is Rust?" \
"Answer in one short sentence: what is Zig?" \
"Answer in one short sentence: what is Odin?"
Chaque question affiche question N: {'stopReason': 'end_turn', …}. Relancez la même commande avec /tmp/mm-after.
Gardez des questions courtes, qui ne modifient aucun fichier : une question qui fait écrire un fichier au modèle demande une autorisation à l'éditeur, et le script n'a personne pour la donner.
6. Lire les lignes du proxy
Chaque ligne correspond à une requête :
#6 tools=f2f7a1fa ['bash', 'edit_file', 'fetch_content', …] cache_n=3381 prompt_n=20 prompt_ms=253
| Champ | Signification |
|---|---|
tools= |
Une empreinte du bloc des outils, tel qu'il a été envoyé. |
[…] |
Les noms des outils, dans l'ordre d'envoi. |
cache_n |
Les tokens du prompt repris du cache du serveur. |
prompt_n |
Les tokens du prompt qu'il a dû relire. |
prompt_ms |
Le temps passé à les relire. |
Ce qu'il faut regarder :
- Après la modification,
tools=a la même valeur sur toutes les lignes, et à partir de la deuxième questionprompt_nreste petit (la nouvelle question et la réponse précédente) pendant quecache_ncouvre le reste du prompt. - Avant la modification,
tools=change d'une question à l'autre, et à chaque changementprompt_nremonte à la taille du prompt entier.
La première question d'une session lit le prompt entier dans les deux cas, sauf si le serveur garde encore le même préfixe d'une session précédente.
Mesuré le 30/09/2026 avec Mellum 12B sur Docker Model Runner, quatre questions, agent.dmr.yaml (six outils, skills non chargés) :
| Question | Avant : prompt_n / prompt_ms |
Après : prompt_n / prompt_ms |
|---|---|---|
| 1 | 3355 / 6776 | 3355 / 7141 |
| 2 | 3401 / 7186 | 20 / 253 |
| 3 | 107 / 713 (l'ordre coïncidait par hasard avec la question 1) | 20 / 257 |
| 4 | 110 / 707 | 20 / 252 |
Le gain grandit avec la session : avant la modification, une question dont l'ordre des outils change relit tout l'historique, pas seulement les outils.
7. Vérifier le reste à la main
La modification touche toutes les requêtes : vérifiez une fois que l'usage ordinaire fonctionne toujours avec le nouveau binaire et votre configuration habituelle.
- Une question qui demande un outil :
/tmp/mm-after -config agent.dmr.yaml -p "list the files of this directory"lancebashet répond. - Une longue session qui se compresse :
/compactécrit toujours un résumé (sa requête n'a pas d'outils et n'est pas touchée). - Un serveur MCP, si vous en utilisez un : ses outils sont triés avec les outils intégrés et toujours appelés par leur nom.
Variantes
- llama-server : lancez le proxy avec
--upstream http://localhost:8080, et partez deagent.llamacpp.yamlavecbaseUrl: http://127.0.0.1:18770/v1. mmou le serveur dans un conteneur : utilisez--upstream http://host.docker.internal:12434pour un Docker Model Runner sur l'hôte.- Un autre modèle : la taille des prompts change, pas le schéma. Les chiffres ci-dessus sont un exemple, pas un objectif.
Voir aussi
- Pourquoi l'ordre compte : fournisseurs — explication
- Les clés de configuration utilisées ici : référence de la configuration
- Poser une seule question : exécuter une invite unique
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
|