Retour au Blog
tech/

Le pipeline sft-wagmi : volontairement rudimentaire

Comment nous fine-tunons Wagmi aujourd'hui : du JSONL dans un dossier, un lanceur Python qui enchaîne des sous-processus, des notebooks en file de secours, et sans posture MLOps industrielle.

Le dépôt DealExMachina/sft-wagmi concentre notre flux de supervised fine-tuning (SFT) pour Wagmi, le petit assistant qui soutient une partie de ce site. Le dépôt jumeau DealExMachina/dexm-one-page produit les données d'entraînement. Cet article décrit la colle entre les deux — et pourquoi nous assumons le qualificatif rudimentaire.

Pour le récit produit (RAG + SFT + autotune sur un 1,5B CPU), lire d'abord Dresser un petit modèle sur CPU. Ici on reste près du disque et du shell.


1. Ce que « rudimentaire » veut dire

Pas « cassé » — peu de surface. Pas de feature store, pas de service de lignée, pas d'opérateur Kubernetes pour les jobs d'entraînement. À la place :

  • Fichiers plats : train.jsonl, eval.jsonl et metadata.json sous sft-wagmi/data/. Du JSONL au format conversation, suffisant pour Unsloth et assimilés.
  • Un orchestrateur fin : scripts/pipeline.py fait des contrôles préalables, appelle éventuellement npm run dataset:wagmi:refresh dans un clone de dexm-one-page voisin, puis enchaîne baseline.py, train.py, autotune.py, eval_sft.py, eval_sft_rag.py et export_gguf.py lorsque vous passez --all. C'est surtout du subprocess.run et des vérifications de chemins — pas un moteur de workflow.
  • Notebooks en repli : si une étape .py manque, le lanceur peut tenter jupyter nbconvert --execute sur le notebook correspondant. C'est un shim de compatibilité, pas un objectif d'architecture.
  • Secrets par variables d'environnement : HF_TOKEN, OPENAI_API_KEY, éventuel .env à la racine du dépôt — rien de plus sophistiqué.

L'objectif est d'obtenir un comportement crédible pour un petit modèle et un seul produit, pas de remporter un appel d'offres « plateforme ML ».


2. D'où viennent les données

La génération de référence vit dans dexm-one-page :

npx tsx scripts/generate-wagmi-sft-dataset.ts

Ce script parcourt le blog, wagmi-skills.md, ai.txt, des notes Obsidian optionnelles (OBSIDIAN_VAULT_PATH + wagmi_sft: true), et des lignes de garde-fous synthétiques, puis écrit datasets/wagmi-sft/*.jsonl. Les volumes et histogrammes de tags se trouvent dans datasets/wagmi-sft/metadata.json après chaque exécution — considérez ce fichier comme la vérité, pas un tableau figé dans un README.

La copie vers sft-wagmi/data/ se fait à la main ou via npm run dataset:wagmi:sync / dataset:wagmi:refresh depuis dexm, selon l'organisation de vos arborescences. Le pipeline suppose que les trois fichiers sont présents avant l'entraînement.


3. Ce que fait vraiment pipeline.py

Ordre approximatif avec python3 scripts/pipeline.py --all :

  1. Preflight — Vérifie data/*.jsonl, la présence des scripts Python, alerte si HF_TOKEN ou OPENAI_API_KEY manque.
  2. Sync — Si ../dexm-one-page existe, lance npm run dataset:wagmi:refresh ; sinon message de skip.
  3. Baselinebaseline.py (ou notebook) : mesurer le modèle de base avant SFT.
  4. Traintrain.py (ou train.ipynb) : Unsloth + LoRA sur Qwen2.5-1.5B-Instruct (profils small vs auth via MODEL_PROFILE).
  5. Autotune — Boucle juge-then-correct optionnelle (autotune.py) ; exige une API de modèle fermé capable. Coûteuse et normative — nous l'utilisons avec parcimonie.
  6. Eval / eval-rag — Évaluations scriptées avec et sans contexte de retrieval.
  7. Exportexport_gguf.py : fusion de l'adaptateur, conversion GGUF, quantification pour inférence CPU (Ollama ou outillage type llama.cpp).

--dry-run n'imprime que les commandes ; pratique sur une machine neuve.


4. Limites assumées

  • Couplage aux chemins : la synchro par défaut suppose dexm-one-page à côté de sft-wagmi. On renomme ou déplace les clones, on ajuste les chemins ou on synchronise à la main.
  • Mentalité « une machine » : nous tournons sur un seul hôte GPU (Spaces Hugging Face, L40 loué, etc.), pas une file de jobs autoscalée.
  • L'autotune n'est pas de la science : les scores dépendent du modèle juge et des prompts ; c'est une boucle heuristique, pas un benchmark publié.
  • Dérive documentaire : le README de sft-wagmi peut afficher d'anciens volumes de lignes. Régénérez metadata dans dexm après chaque gros changement de contenu.

5. Pourquoi rester à ce niveau

Pour un modèle instruct 1,5B cantonné à la voix publique d'une entreprise, un pipeline rudimentaire se modifie vite : on édite le générateur en TypeScript, on réexporte le JSONL, on réentraîne, on pousse un adaptateur. La complexité que nous n'ajoutons pas (pour l'instant) est celle que nous n'avons pas à exploiter à minuit.

Quand le coût de coordination dépassera le coût de quelques étapes manuelles, nous ferons monter certains blocs en dureté. D'ici là, voici la forme honnête du système : fichiers dedans, poids dehors, avec un court script Python qui tient la liste des commandes.


Pour aller plus loin : README sft-wagmi · generate-wagmi-sft-dataset.ts · Article stack + ingestion Obsidian · RAG + SFT + autotune