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.
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 :
train.jsonl, eval.jsonl et metadata.json sous sft-wagmi/data/. Du JSONL au format conversation, suffisant pour Unsloth et assimilés.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..py manque, le lanceur peut tenter jupyter nbconvert --execute sur le notebook correspondant. C'est un shim de compatibilité, pas un objectif d'architecture.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 ».
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.
pipeline.pyOrdre approximatif avec python3 scripts/pipeline.py --all :
data/*.jsonl, la présence des scripts Python, alerte si HF_TOKEN ou OPENAI_API_KEY manque.../dexm-one-page existe, lance npm run dataset:wagmi:refresh ; sinon message de skip.baseline.py (ou notebook) : mesurer le modèle de base avant SFT.train.py (ou train.ipynb) : Unsloth + LoRA sur Qwen2.5-1.5B-Instruct (profils small vs auth via MODEL_PROFILE).autotune.py) ; exige une API de modèle fermé capable. Coûteuse et normative — nous l'utilisons avec parcimonie.export_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.
dexm-one-page à côté de sft-wagmi. On renomme ou déplace les clones, on ajuste les chemins ou on synchronise à la main.metadata dans dexm après chaque gros changement de contenu.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