guest@6admin.io:~$

BrainForge : une usine de distillation où les experts IA se disputent

Posez la question “ce code est-il vulnérable ?” à un LLM et il répondra, confiant, dans 100 % des cas. Environ la moitié du temps, il aura raison. BrainForge est un framework de distillation : il transfère le savoir-faire de grands modèles experts dans un petit modèle étudiant. Pour ça, il faut des leçons : des milliers d’exemples vérifiés. Et c’est là que tout se joue, parce qu’un LLM peut se tromper ou pire, réciter ses souvenirs de CVE au lieu d’analyser.

Alors la fabrique de leçons est organisée comme un procès. Des experts témoignent, un avocat du diable les challenge, un juge indépendant tranche : chaque leçon du dataset est vérifiée avant d’enseigner quoi que ce soit. À la sortie, un dataset propre, étiqueté, prêt à fine-tuner un modèle étudiant sur un GPU de gamer.

BrainForge : des robots forgerons forgent des cerveaux lumineux sur une enclume

Le vrai problème : apprendre sans tricher

Pour fine-tuner un modèle sur des tâches de sécurité, il faut des exemples vérifiés. Or les LLM ont tout mémorisé : des milliers de CVE, d’articles et de write-ups datés d’avant leur entraînement. Si je nourris mon dataset de vulnérabilités publiées avant la date de coupure des enseignants, l’étudiant n’apprend pas à analyser du code : il apprend à réciter des souvenirs. Pire, mon benchmark mesurerait du par-cœur, pas de la compétence.

BrainForge refuse ce raccourci. Chaque enregistrement porte deux dates : celle du cas (la date du commit git du fichier source, relevée automatiquement) et la date de coupure des connaissances de chaque enseignant. Si la source précède le cutoff, le record est taggé recitation_risk et les cas récents partent dans un split à part : l’examen final, réservé aux affaires que les professeurs n’ont jamais vues.

Le tribunal

Chaque leçon du dataset est fabriquée dans un atelier qui ressemble à s’y méprendre à une audience. Le flux d’un cas :

Les pièces à conviction. On part de fichiers de code (ou de cas JSON) et tout est noté : dépôt, chemin, langue et surtout la date du commit git, qui devient la date de la source. À côté, des documents (rapports, standards, notes) sont indexés par un petit moteur de recherche vectorielle maison (sqlite + numpy) : c’est le RAG, réservé aux adultes de la pièce (enseignants et juge), jamais fourni à l’étudiant.

Les experts. Selon le mode choisi (cheap, standard, maximum), un à trois enseignants analysent le cas avec son contexte RAG : expert sécurité, expert code, expert généraliste. Chacun rend sa copie en JSON, validée contre un schéma pydantic. Le schéma est injecté dans le prompt système et, si le modèle rend une copie illisible, une boucle de rattrapage lui repasse l’épreuve en joignant sa propre réponse fautive en pièce à conviction (deux essais). Le provider mock peut générer une sortie valide depuis n’importe quel schéma : tout le pipeline tourne sans clé API, à coût zéro.

L’avocat du diable. Le critique adversarial reçoit le même dossier, avec pour consigne de contester. C’est lui qui force la nuance là où un expert seul aurait tranché trop vite.

Le juge. Il voit le cas, le contexte RAG et toutes les dépositions. Son verdict est contraint : confirmed, rejected ou insufficient_information, avec un score de confiance, un CWE si vulnérabilité confirmée et des preuves qu’il doit re-dériver lui-même (pas seulement approuver les experts).

Le greffier. La quality gate filtre la sortie : CWE mal formé, vulnérabilité affirmée sans preuve, verdict confirmé sans CWE, confiance trop basse : rejeté. Et les rejets ne sont pas jetés à la poubelle : ils partent en quarantaine avec leurs motifs, prêts à être étudiés pour une meilleure itération. Chaque record final est un couple question/réponse en JSONL, avec toute la provenance : quels enseignants, quels modèles, quels fournisseurs, taux d’accord, morceaux de RAG utilisés, dates, risque de récitation.

La règle d’or : un juge qui ne connaît pas les témoins

Dans un vrai tribunal, le juge ne doit pas être le cousin des témoins. Ici, c’est codé dans la config : le chargement refuse de démarrer si le juge partage un fournisseur ou une famille de modèle avec un enseignant. Chez moi, le juge est un GLM servi par Zen, les experts sont DeepSeek et Qwen servis par OpenRouter et MLGW. La règle a trois positions (enforced, warn, off) et le défaut est enforced.

Autre garde-fou : le taux d’accord entre experts et juge est calculé et stocké par record. Au-dessus de 95 % d’accord systématique, un avertissement de biais corrélé s’affiche. Dans la démo plus bas, le mock est d’accord à 100 % : l’avertissement apparaît, c’est voulu.

L’examen final : les affaires post-cutoff

Le découpage train/test ne se fait pas au hasard par record : il est groupé par source (dépôt, chemin ou URL), par hachage déterministe. Deux cas du même dépôt ne se retrouvent jamais l’un en entraînement, l’autre en test. Les records à risque de récitation sont exclus du split d’entraînement et les cas post-cutoff forment un split dédié, test_postcutoff : l’examen final. Si moins de 20 cas y survivent, un avertissement le dit crûment.

L’évaluation métier s’y joue : précision du verdict, taux de faux positifs et de faux négatifs, précision des CWE, tout comparé au ground truth du juge. C’est le seul score auquel je crois.

La distillation proprement dite

La distillation se joue ici : le savoir des enseignants, verrouillé dans le dataset vérifié, est transféré à l’étudiant. Côté matériel, QLoRA fait des miracles : base de 8 milliards de paramètres (Qwen3) gelée en 4 bits (NF4), adaptateurs LoRA de rang 16, accumulation de gradients sur 16 pas et ça tient sur une RTX 3080. L’étudiant apprend en format chat : question (le cas), réponse (le verdict du juge). Un réglage assistant_only_loss existe pour ne compter que la réponse de l’assistant (désactivé par défaut).

L’évaluation est double : perplexité du corpus (pondérée par tokens, pas la moyenne des moyennes) pour surveiller l’entraînement et l’évaluation métier décrite plus haut pour juger la compétence réelle. À la fin, l’adaptateur est fusionné dans le modèle et exporté en bf16.

Essayez chez vous

Trois commandes, aucune clé API :

uv run brainforge pipeline run security_dataset --input examples/cases --provider mock
uv run brainforge dataset validate datasets/security_dataset.jsonl
uv run brainforge dataset inspect datasets/security_dataset.jsonl

La première fait tourner le pipeline complet en mock (3 cas acceptés, 0 rejeté), la deuxième valide le dataset produit, la troisième affiche la table de contamination et les accords enseignant/juge, avertissement de biais corrélé compris.

Bilan honnête

Environ 3 100 lignes de Python, plus de 100 tests unitaires, coverage bloquante à 85 %, 10 ADR (dont un sur le pourquoi de chaque choix) et une CI avec les mêmes six gates (lint, secrets, test, config, build, audit) sur trois forges, gitleaks scannant tout l’historique git. Ce n’est pas de la magie : c’est un procès bien organisé, avec un greffier maniaque.

La prochaine fois qu’un LLM vous affirme “ce code est vulnérable”, demandez-lui ses preuves. Ou montez votre propre tribunal.