53 centimes d'entraînement : un RNN de 2,9 milliards égale un modèle cloud de 30 milliards en tool-calling.

22 juillet 2026

Nous entraînons des modèles RWKV à piloter les 231 outils réels de notre assistant Gungnir. Ce billet documente un résultat que nous n'attendions pas si tôt : après un seul epoch de state-tuning, facturé 0,53 $ de GPU, notre RNN local de 2,9 milliards de paramètres obtient exactement le même score global qu'un modèle cloud dix fois plus gros sur notre banc d'essai. Et le résultat se réplique au bit près.

Tout ce qui suit est vérifiable : le banc, les schémas d'outils, les générations brutes, les scripts et les empreintes cryptographiques des modèles sont publiés. Un relecteur hostile est le bienvenu.

En bref

Le problème : un agent doit choisir, remplir, et savoir se taire

Un assistant agentique reçoit une demande en langage naturel et doit décider quel outil appeler. Sur Gungnir, cela veut dire choisir parmi 231 fonctions dont beaucoup se ressemblent : archiver ou supprimer une carte, chercher dans les mails ou sur le web, cinq variantes d'analyse de coûts. Il faut ensuite remplir les arguments requis sans en inventer. Et quand l'utilisateur dit simplement merci, la bonne réponse est de ne rien appeler du tout.

Les gros modèles cloud font cela correctement. Notre pari est qu'un petit modèle récurrent, exécutable sur une machine ordinaire, peut apprendre le même métier. L'enjeu dépasse notre produit : un RNN n'a pas de KV cache, son état de session occupe une taille constante quelle que soit la longueur de la conversation, ce qui change l'économie du service d'agents en local. Nous avions déjà exploré cette famille d'architectures pour la recherche documentaire ; ici nous nous attaquons à la décision d'action.

Le banc d'essai : 82 cas figés, publics, jamais vus à l'entraînement

Nous avons construit un juge de 82 cas sur les schémas d'outils réels de Gungnir, réparti en cinq catégories : sélection difficile (23 cas de quasi-doublons, le piège archiver contre supprimer), remplissage d'arguments (22), sélection simple (18), abstention (17 cas où aucun outil ne doit être appelé) et multi-étapes (2).

Trois précautions structurent ce juge :

  1. Labels mécaniques. L'outil attendu et ses champs requis découlent du schéma, pas d'un jugement de LLM. Le scoreur est un script publié, le même pour tous les modèles.
  2. Anti-fuite. 17 cas sont écrits à la main. Les 65 autres ont été générés par un modèle d'une autre famille (Llama 3.3) que celui qui a produit les données d'entraînement (Qwen3), puis relus à la main, avec rejet automatique de toute requête trop proche du jeu d'entraînement.
  3. Figé avant mesure. Le fichier n'a plus bougé depuis la première évaluation. Aucun cas n'a été ajouté ou retiré après avoir vu un score.

Le juge, les schémas des 231 outils, les scripts et les sorties brutes sont publiés dans le dépôt du banc (lien en fin d'article), aux côtés du code de Gungnir qui est lui-même ouvert.

Les références : le 30B cloud, et une surprise sur les checkpoints

La cible est qwen/qwen3-30b-a3b-instruct-2507, un mixture-of-experts de 30 milliards de paramètres réputé pour le function-calling, interrogé via OpenRouter dans son mode normal : les 231 schémas bruts dans le contexte. Score : 58/82 (70 %), avec une abstention parfaite (17/17).

Côté RWKV, notre pipeline est différent et assumé comme tel : un retriever (un embedder RWKV de 144 M que nous avions fine-tuné pour la recherche) présélectionne les 40 outils les plus plausibles, mesuré à 100 % de rappel sur le juge, et le modèle choisit parmi eux. Un petit modèle à 4 096 tokens de contexte natif ne peut pas avaler 31 000 tokens de schémas ; le retrieval n'est pas une optimisation, c'est la condition d'existence du pipeline local. Les deux systèmes répondent aux mêmes 82 requêtes, chacun avec son échafaudage.

Au passage, le choix du checkpoint de départ a réservé une leçon, et elle n'était pas celle que nous avions cru.

Nous avions d'abord mesuré que la génération la plus récente publiée par BlinkDL était significativement moins bonne que la précédente en sélection d'outil, et nous en avions conclu que le progrès des checkpoints généralistes n'était pas monotone sur une capacité étroite.

Correction du 28 juillet 2026. Cette conclusion était fausse, et c'est notre protocole qui l'a produite. Peng Bo (BlinkDL) nous a signalé que notre gabarit de prompt pouvait pénaliser les modèles récents. Nous avons donc sondé chaque checkpoint pour lire le format sur lequel il a été entraîné, puis réévalué chacun dans le sien. Les checkpoints récents gagnent alors 6 et 9 points, pendant que l'ancien, servant de contrôle, en perd 3. C'est ce contrôle qui rend la mesure lisible : le gain ne vient pas des réglages de décodage, il vient de l'accord entre le modèle et son format. Nous mesurions notre harnais autant que les modèles. Le détail, les sondes et les générations brutes sont dans la section « checkpoint survey » du dépôt du banc.

Ce que la correction ne change pas : sur ces checkpoints bruts, l'abstention reste à 0/17 et le multi-étapes à 0/2, quel que soit le format. La syntaxe était un plafond artificiel, le jugement est le vrai, et c'est lui que l'entraînement décrit plus bas achète.

La leçon utile est donc plus dérangeante que la première : avant de conclure qu'un modèle est mauvais, vérifiez que vous le sollicitez comme il a été entraîné. Un banc mesure toujours deux choses à la fois, le modèle et la manière dont on le questionne.

La méthode : le state-tuning, ou entraîner 0,2 % du modèle

RWKV-7 est un réseau récurrent : il maintient un état interne qui résume tout ce qu'il a lu. Le state-tuning, proposé par l'écosystème RWKV-PEFT, n'entraîne que l'état initial de chaque couche : 5,2 M de paramètres sur 2,9 Md, soit 0,18 % du modèle, les poids restant gelés. L'artefact produit est un fichier de 11 Mo qui se pose sur le modèle de base au chargement. On peut y penser comme une disposition d'esprit apprise : le modèle démarre chaque requête déjà configuré pour le métier d'appel d'outils.

Les données : 2 623 exemples construits sur les schémas réels, dont 312 cas d'abstention (12 %). La construction est vérifiable par principe : pour chaque outil, un générateur produit des requêtes qui l'exigent, et l'appel correct constitue le label, validé contre le schéma. Chaque exemple reproduit le format exact du déploiement, top-40 du retriever inclus, outils frères côte à côte pour apprendre les nuances.

L'entraînement : 1 epoch, 2 556 pas, contexte 6 144, bf16, sur une RTX 3090 louée 0,22 $ de l'heure. Durée : environ 2 heures. Coût : 0,53 $. La loss passe de 1,40 à 0,05.

Courbe de loss du state-tuning, de 1,40 à 0,05 en 2556 pas
Un epoch de state-tuning. Les deux runs indépendants produisent exactement cette courbe, à la quatrième décimale près.

Résultats

Le tableau complet, tous modèles évalués sur les mêmes 82 cas avec le même scoreur :

Catégorie (nb de cas)G1g baseG1g + state-tuningQwen3-30B (231 bruts)Qwen3-30B (+ top-40)
Abstention (17)0121717
Arguments (22)9191720
Sélection difficile (23)12161417
Sélection simple (18)7111016
Multi-étapes (2)0001
Global (82)28 (34 %)58 (70 %)58 (70 %)71 (86 %)
Scores par catégorie : base, state-tuné, Qwen
Trois systèmes, mêmes 82 requêtes, même scoreur. La colonne base est mesurée dans les mêmes conditions que le modèle entraîné (génération libre, sans grammaire contrainte).

Trois lectures :

L'effet d'entraînement est massif et ciblé. De 28 à 58 cas réussis. Sur les 34 cas où les deux versions divergent, l'entraînée gagne 32 fois et perd 2 fois (p = 6,9×10⁻⁸, test de McNemar exact). L'abstention passe de 0/17 à 12/17 : le modèle a appris à se taire. Le remplissage d'arguments passe de 9/22 à 19/22. Ce sont précisément les deux comportements que le jeu de données ciblait.

L'égalité avec Qwen n'est pas une imitation. Sur le run de référence, les deux systèmes obtiennent 58/82 mais ne sont d'accord que sur 50 cas : chacun réussit 16 cas que l'autre rate. Le RWKV entraîné devance le 30B sur les arguments, la sélection difficile et la sélection simple ; Qwen conserve l'avantage sur l'abstention (17/17 contre 12/17).

Et la cible elle-même fluctue, pas nous. Quatre exécutions de Qwen via l'API, toutes à température 0, donnent 57, 57, 58 et 61 sur 82 : le service n'est pas déterministe (les grands MoE servis en ligne ne le sont généralement pas). Notre 58, lui, se recalcule au caractère près sur n'importe quelle machine. La parité s'entend donc ainsi : un score déterministe posé au centre de la fourchette de variation du modèle cloud.

Écart par catégorie entre le RWKV entraîné et Qwen3-30B
À score global égal, deux profils différents. Le déficit résiduel du RWKV tient dans une seule catégorie.

Le format est appris, plus contraint. Le modèle de base avait besoin d'une grammaire formelle pour produire du JSON valide ; entraîné, il n'en a plus : une seule sortie non parsable sur 82, en génération libre. Et aucun faux refus : sur les 65 cas qui exigent un appel, le modèle entraîné n'a jamais refusé à tort. Le gain d'abstention ne s'est pas payé en excès de prudence.

L'ablation qui devait être faite : et si Qwen recevait la même présélection ?

L'objection évidente contre le tableau ci-dessus : notre modèle reçoit un top-40 trié par retriever, Qwen navigue dans 231 schémas bruts. La parité vient-elle du modèle ou de la présélection ? Nous avons fait le test avant que vous ne le demandiez : mêmes 82 cas, même top-40 par cas, envoyés à Qwen.

Réponse : Qwen + top-40 = 71/82 (86 %). La présélection lui vaut 13 cas. Deux conclusions, chacune à sa place :

Réplication : le même résultat, au caractère près

Nous avons refait la totalité de la chaîne sur une seconde machine : ré-entraînement complet depuis le modèle de base, puis ré-évaluation des 82 cas. Résultat : 58/82, mêmes catégories, mêmes cas réussis et ratés, et les 164 générations (82 pour la base, 82 pour le modèle entraîné) sont identiques caractère par caractère. Les deux checkpoints entraînés portent la même empreinte SHA-256.

La chaîne est en fait déterministe de bout en bout : état initial nul, ordre de données fixe, génération gloutonne. Ce n'est donc pas une moyenne avec sa marge d'erreur, c'est un point exact que quiconque peut recalculer. Nous préférons cette propriété à un intervalle de confiance.

Limites, et ce que ce résultat ne dit pas

Un résultat n'est utile que si son périmètre est net. Le nôtre :

Reproduire

Le dépôt public du banc : git.scarletwolf.cloud/kevin/rwkv-toolcaller-bench (miroir : Codeberg). Il contient : le juge (82 cas), les schémas des 231 outils, les scripts d'évaluation et de scoring, les générations brutes de tous les runs (y compris celles qui nous sont défavorables), les courbes de loss, les recettes d'entraînement pod par pod, et les empreintes SHA-256 du modèle de base et des états entraînés (les fichiers de 11 Mo sont inclus). L'environnement exact est épinglé : commit RWKV-PEFT, versions de torch et de peft, image Docker. La référence cloud est qwen3-30b-a3b-instruct-2507 via OpenRouter.

Matériel requis pour tout refaire : une carte à architecture Ampere ou plus récente (le bf16 est requis par les kernels d'entraînement RWKV-7 ; les T4 gratuites de Kaggle ne peuvent pas), environ 2 heures, moins d'un dollar.

Rung 2 : le LoRA, même niveau, autre profil

La nuit de la rédaction, le deuxième barreau de l'échelle a rendu son verdict : un LoRA (r=32, environ 1 % des paramètres, 2 epochs sur les mêmes données, 0,90 $) atteint 61/82 (74 %).

CatégorieState (rung 1)LoRA (rung 2)
Abstention12/1715/17
Arguments19/2216/22
Sélection difficile16/2316/23
Sélection simple11/1814/18
Global5861

Trois précisions d'honnêteté avant d'applaudir. Un, l'écart global avec le state n'est pas statistiquement établi (11 cas gagnés contre 8 perdus, p = 0,65) : à ce stade, LoRA et state sont au même niveau, avec des profils différents. Deux, contrairement au state, le LoRA part d'une initialisation aléatoire : ce run n'est pas reproductible au bit près, et sa variance entre runs reste à mesurer. Trois, l'avance de Qwen à échafaudage égal reste significative (p = 0,04).

Ce que le profil raconte est plus intéressant que le score : le LoRA absorbe mieux l'abstention (15/17, il ne reste que deux pièges) et la sélection simple, mais régresse sur les arguments (16/22 contre 19/22). Plus de capacité d'adaptation ne domine pas partout : elle déplace le compromis.

Rung 3 : les données ciblées font leur travail, et la composition d'états passe son premier test

La nuit suivante, trois expériences de plus, ciblant les faiblesses identifiées. Nous avons ajouté 162 exemples construits exprès : 113 « pièges actionnables » (des demandes qui sonnent comme une action mais qu'aucun outil ne couvre, le type précis d'abstention qui résistait) et 49 « chercher avant d'agir ». Puis réentraîné les deux méthodes sur ces données v3, et tenté une composition.

Catégoriestate v3LoRA v3combo (état v3 + LoRA v3)
Abstention16/1717/1717/17
Arguments18/2215/2217/22
Sélection difficile15/2315/2317/23
Sélection simple12/1812/188/18
Multi-étapes0/20/20/2
Global615959

Quatre enseignements :

Conquête de l'abstention au fil des rungs, de 0 à 17 sur 17
L'arc le plus net de la campagne : le comportement « savoir ne pas agir » passe de 0/17 à la perfection en trois nuits de données ciblées. La ligne pointillée est le score de Qwen3-30B.

La barre reste 71/82, le score du 30B avec le même échafaudage : l'écart est désormais de 10 cas, concentré sur la sélection. Et une réplication sur outils publics est prévue, sur un juge neuf tenu privé jusqu'à la mesure.

Rung 4 : en composition native, le substrat impose son profil

La suite logique de la sonde du rung 3 : au lieu d'injecter un état appris sur le modèle de base (hors distribution), entraîner l'état directement sur les poids LoRA fusionnés, là où il vivra. Un seul run cohérent, LoRA v3 deux epochs puis état natif dessus, environ 4 $ de RTX 3090 :

Catégoriecombo hors distributioncomposition nativestate v3 (champion)
Abstention17/1717/1716/17
Arguments17/2215/2218/22
Sélection difficile17/2317/2315/23
Sélection simple8/1810/1812/18
Multi-étapes0/20/20/2
Global595961

L'hypothèse était que l'entraînement natif réparerait les dégâts du décalage (sélection simple, format) en gardant les records. Elle est à moitié validée, et la moitié qui échoue est le vrai enseignement.

Ce qui est réparé, comme prévu : le format (4 → 1 sortie non parsable), la sélection simple en partie (8 → 10), avec l'abstention parfaite et le record de sélection difficile conservés. C'est le meilleur profil abstention + sélection difficile de la campagne.

La surprise : les arguments tombent à 15/22, exactement le score du LoRA v3 seul, là où l'état seul faisait 18. Un état entraîné nativement sur un substrat LoRA s'aligne sur le substrat, faiblesses comprises ; il ne le corrige pas. Le combo hors distribution gardait 17 en arguments précisément parce que le décalage préservait le comportement de l'état d'origine.

Substrat LoRA v3 seul comparé à l'état natif entraîné dessus, par catégorie
Comparé à son substrat nu, l'état natif ne gagne rien : même total (59), même abstention (17), mêmes arguments (15). Il déplace deux cas de la sélection simple vers la sélection difficile. Le trait pointillé est le state v3, le champion, entraîné sur le modèle de base.

La comparaison avec le substrat nu achève la démonstration. Le LoRA v3 sans rien dessus fait déjà 59/82. Avec l'état entraîné nativement par-dessus : 59/82 aussi, même abstention, mêmes arguments, deux cas de moins en sélection simple et deux de plus en sélection difficile. L'état natif ne dépasse pas son substrat, il redistribue.

Conclusion pour la « bibliothèque de compétences » : la composition ne s'effondre jamais, mais elle ne somme pas les forces, le substrat impose son profil. Le state-tuning seul reste le champion : 61/82, reproductible au bit près, 11 Mo. Résultat négatif propre, publié comme le reste : générations brutes, scores et recette dans le dépôt du banc.

Rung 5 : les données ciblées heurtent le mur de capacité

Le rung 3 avait montré que 113 exemples construits exprès conquéraient l'abstention. Le rung 5 pose la question symétrique : la même chirurgie marche-t-elle sur la catégorie faible du champion, la sélection simple (12/18) ?

L'analyse d'erreurs des six ratés donnait trois modes d'échec nets : des collisions de namespace (linear_search_issues confondu avec github_search_issues), des noms d'outils inventés (browser_switch_theme, qui n'existe pas parmi les 231) et une hallucination d'argument. Données v4 = v3 + 135 exemples visant exactement ces ratés : désambiguïsation dans les deux sens, ancrage des noms exacts, minimalisme d'arguments, le tout validé contre les schémas réels et filtré anti-fuite contre le juge. Même recette championne, une seule variable : les données.

Résultat : 58/82, et zéro des sept cas ciblés n'est réparé. La catégorie visée est même la seule à reculer vraiment.

Catégorieétat v3état v4 (données ciblées)écart
Abstention16/1717/17+1
Arguments18/2217/22-1
Sélection difficile15/2315/230
Sélection simple (visée)12/189/18-3
Multi-étapes0/20/20
Global6158-3
Écart par catégorie entre l'état v3 et l'état v4 entraîné sur données ciblées
135 exemples construits pour réparer la sélection simple, et c'est la sélection simple qui perd 3 cas. Le seul gain est l'abstention, qui n'était pas visée.

Les mauvaises réponses se déplacent au lieu de se corriger. Les sept cas visés, avant et après :

Outil attenduréponse de l'état v3réponse de l'état v4
runtime_infomodel_get_infomodel_manage
set_themebrowser_switch_themebrowser_set_theme
linear_list_cyclesvalkyrie_list_cyclesvalkyrie_list_cycles
tool_result_fetchconsciousness_recallconsciousness_recall
valkyrie_get_reminderstask_queue_resultsvalkyrie_list_projects
linear_search_issuesgithub_search_issuesweb_search
valkyrie_create_cardargument status_key hallucinéargument status_key halluciné

Quatre réponses sur sept changent, trois ne bougent pas, aucune ne devient juste. Ailleurs sur le juge, sept cas qui passaient basculent dans l'erreur et quatre reviennent au bon outil, soit trois cas perdus au net. Et pendant ce temps l'abstention, qui est une disposition et non une consultation, atteint 17/17 : le premier score parfait du state sur cette catégorie.

La lecture qui unifie la campagne : un état initial de 11 Mo déplace des dispositions (quand s'abstenir, quel ton, quel format) mais ne peut pas épingler des correspondances fines de noms. C'est exactement le verrou de l'effondrement du state-as-index mesuré avant la campagne, quand un état figé s'était révélé incapable de retenir un annuaire de 231 outils. Quatre barreaux atterrissent à 61, 59, 59 et 58 : la famille state est à son plafond de capacité, autour de 60/82 sur ce juge, et davantage de curation de données remélange le profil au lieu de l'élever.

Progression de la campagne sur le juge de 82 cas, du modèle nu au rung 5
L'échelle complète : le premier barreau vaut 30 cas, les quatre suivants tiennent dans 3 points. La bande bleutée est la fourchette de Qwen3-30B en configuration production ; la ligne pointillée, son score avec le même échafaudage que nous, la prochaine cible.

Les escalades suivantes sont l'entraînement par récompense vérifiable (GRPO) ou plus de capacité d'adaptateur, pas plus d'exemples.

La suite de la campagne fait l'objet d'un second article : le RL, la cartographie complète du plafond, et sa résolution par le changement d'échelle (un 7,2B state-tuné à 69/82, qui bat le 30B cloud).

Questions fréquentes

Pourquoi ne pas simplement utiliser le modèle cloud ?

Souveraineté et économie. Les requêtes d'un agent transportent le contexte de travail complet de l'utilisateur ; les faire traiter localement supprime la question du transfert. Et un RNN sert des sessions longues à mémoire constante, là où le KV cache d'un transformer croît avec la conversation.

Le modèle n'a-t-il pas juste appris le banc par cœur ?

Les 82 cas du juge n'apparaissent pas dans les données d'entraînement : 17 sont écrits à la main, 65 générés par une autre famille de modèle avec filtre de similarité, et le juge a été figé avant la première mesure. La loss très basse (0,05) indique une forte adaptation au format ; le score sur ces cas jamais vus mesure la généralisation.

Pourquoi le state-tuning plutôt qu'un fine-tuning complet ?

C'est le barreau le moins cher de l'échelle (0,2 % des paramètres, 0,53 $) et il était prévu comme simple test de plomberie avant un LoRA. Il a atteint la cible seul. Le LoRA est en cours pour chercher mieux.

Un état de 11 Mo, ça veut dire quoi en pratique ?

Que la compétence est un fichier. On charge le modèle de base une fois, et on peut échanger des états spécialisés par tâche, à chaud, sans dupliquer les 5,6 Go de poids. C'est un mode de déploiement en soi pour les agents locaux.

Où sont les données et le code ?

Le dépôt du banc est public, le code de Gungnir aussi. Les liens sont dans la section Reproduire. Si vous trouvez une faille dans le protocole, écrivez-nous, c'est le but de la publication.

Une question, un désaccord, une envie d'essayer ? Écrivez-moi, c'est le fondateur qui répond.