SFT et modèle de conversation XTML¶
L'ajustement supervisé¶
Le SFT établit la politique de démarrage à froid (cold-start policy) sur laquelle le RL pourra travailler.
Ce qui change par rapport aux générations précédentes¶
Le pipeline est celui de Kimi K2 et K2.5, mais le jeu de données est substantiellement élargi, en particulier sur les tâches agentiques complexes.
La chaîne de production des données :
1. Synthèse de trajectoires par des modèles spécialisés
de la série Kimi précédente
│
▼
2. Vérification multi-étapes
│
▼
3. Annotation avec humain dans la boucle
│
▼
4. Sérialisation au format XTML
La boucle générationnelle
Les données de SFT de K3 sont produites par les modèles Kimi antérieurs. C'est devenu la norme : chaque génération sert de professeur à la suivante.
Le risque associé — la dérive et l'amplification des biais du professeur — est mitigé par la vérification et l'annotation humaine, mais pas éliminé. Le rapport ne le discute pas.
Ce que le SFT installe¶
Le rapport énumère trois capacités : raisonnement adaptatif, appel d'outils précis, exécution robuste dans des scénarios agentiques long-horizon.
La quantification commence ici¶
Point important
La QAT MXFP4 est appliquée dès le stade SFT, et se poursuit dans tout le post-entraînement, RL compris. Le modèle ne découvre donc jamais la quantification après coup : il apprend avec elle.
Détail en Quantification et modèle brouillon.
Le format XTML¶
eXtensible Token Markup Language. C'est la contribution la moins spectaculaire du rapport, et l'une des plus réutilisables.
Les trois objectifs de conception¶
| Objectif | Énoncé |
|---|---|
| Extensibilité | De nouvelles capacités doivent s'introduire par des formats de messages rétrocompatibles, pas par des révisions du gabarit — un seul gabarit doit servir toute la génération du modèle |
| Faible taxe d'alignement | Le format doit s'apprendre avec un minimum de données supervisées, pour qu'un modèle pré-entraîné légèrement affiné puisse passer directement au RL |
| Convivialité de décodage | La structure doit admettre des encodeurs simples, des analyseurs en flux et des contraintes grammaticales |
La syntaxe¶
Trois jetons réservés remplacent les chevrons XML :
| Jeton | Rôle |
|---|---|
[open] |
Ouverture d'élément |
[sep] |
Séparateur attributs / contenu |
[close] |
Fermeture d'élément |
[end_of_msg] |
Marqueur d'arrêt de génération |
Un élément s'écrit :
[open]tag attr="valeur"[sep] … contenu … [close]tag[sep]
Pourquoi des jetons plutôt que des caractères
C'est isomorphe à du XML, mais chaque frontière structurelle est un jeton spécial explicite. Deux gains concrets :
- Suppression de l'ambiguïté de tokenisation aux frontières. Avec des
balises textuelles, la façon dont
<tool_call>est découpé en jetons dépend du contexte qui le précède. Avec un jeton réservé, jamais. - Décodage sous contrainte simplifié. Un analyseur grammatical peut opérer directement au niveau des jetons, sans reconstruire le texte.
L'anatomie du contexte¶
┌──────────────────────────────────────────────────────────┐
│ OPTIONS GLOBALES │
│ • déclaration d'outils (type="tool-declare") │
│ • effort de raisonnement (type="thinking-effort") │
├──────────────────────────────────────────────────────────┤
│ MESSAGES D'ENTRÉE │
│ system / user / assistant / tool │
│ ┌ (option intercalée : chargement dynamique d'outils) ┐ │
├──────────────────────────────────────────────────────────┤
│ OPTIONS PONCTUELLES │
│ • tool_choice │
│ • response_format │
└──────────────────────────────────────────────────────────┘
Le placement est dicté par le cache KV
| Type | Position | Raison |
|---|---|---|
| Globales | Avant tout | Changent rarement ; les modifier invalide le cache de toute façon |
| Ponctuelles | Après tout | Un changement par requête laisse l'historique en cache intact |
| Intercalées | Au milieu | Permet d'ajouter des outils sans reconstruire le contexte précédent |
Le chargement dynamique d'outils en découle : les outils récupérés en
cours de conversation sont annoncés par un message tool-declare
supplémentaire, et l'outillage disponible s'élargit sans invalidation.
Les canaux¶
Le corps d'un message assistant est organisé en canaux, concept inspiré du format Harmony d'OpenAI :
| Canal | Contenu |
|---|---|
think |
La trace de raisonnement |
response |
La réponse visible par l'utilisateur |
tools |
Les appels d'outils |
Un seul gabarit, deux modes
Les deux modes de génération sont sélectionnés uniquement par le préfixe de génération :
[open]think[sep]→ mode réflexion ;[open]response[sep]→ mode instruction.
Pas de gabarits séparés. C'est une application directe du principe d'extensibilité.
La réflexion préservée¶
Kimi K3 ne supporte que le preserved thinking : en mode réflexion, le
canal think est toujours conservé dans l'historique, même vide, pour que
le modèle observe une structure de message cohérente d'un tour à l'autre. En
mode instruction, les messages historiques ne contiennent que response et
tools.
Conséquence pratique impérative
L'API exige que le message assistant complet soit renvoyé tel quel dans
messages, y compris reasoning_content et tool_calls. Ne renvoyer que
content dégrade le comportement — c'est d'ailleurs une limitation
explicitement reconnue par Moonshot.
L'appel d'outil¶
Dans le canal tools, chaque appel porte deux attributs :
tool: le nom de l'outil ;index: numérote les appels parallèles au sein d'un même message.
Chaque message de résultat répète la paire tool/index et suit l'ordre de
son appel : l'association résultat ↔ appel est donc non ambiguë, même avec des
appels parallèles.
Les arguments typés
Les arguments de type chaîne apparaissent en texte brut ; seules les valeurs d'autres types JSON sont sérialisées de façon compacte.
Conséquence, énoncée par le rapport : free-form text such as code is therefore a first-class citizen rather than an escaped JSON string.
Concrètement, un bloc de code n'a plus besoin d'échapper ses guillemets, ses retours à la ligne et ses antislashs. C'est un gain de robustesse et de jetons.
Un bloc de repli JSON pur couvre les entrées dont les arguments ne se décomposent pas en blocs typés. Il n'apparaît que dans les jetons d'entrée, jamais en sortie du modèle, et sa perte est masquée pendant l'entraînement.
L'effort de raisonnement comme message¶
L'effort n'est pas un paramètre de décodage ni un budget de jetons exposé.
C'est un message d'option globale de type thinking-effort, inséré après la
déclaration d'outils et avant les messages d'entrée, qui énonce le niveau
demandé en langage naturel et agit comme une contrainte de génération.
Le schéma réserve quatre niveaux — low, medium, high, max — dont
Kimi K3 supporte un sous-ensemble (low, high, max d'après la carte de
modèle).
Le principe généralisé
C'est l'implémentation commune de toutes les options : tool_choice,
response_format et thinking-effort sont chacune traduites en une courte
instruction en langage naturel placée dans le contexte, plutôt qu'en syntaxe
dédiée.
Justification donnée : parce que le modèle pré-entraîné suit déjà bien ce genre d'instruction, de nouvelles options peuvent être introduites avec peu ou pas d'entraînement supplémentaire — l'incarnation directe du principe de faible taxe d'alignement.
Vérification de compréhension¶
Pourquoi masquer la perte sur le bloc JSON de repli ?
Parce qu'il ne doit jamais apparaître en sortie du modèle. Le laisser dans la perte apprendrait au modèle à le produire, alors qu'il n'existe que pour ingérer des entrées mal formées venues de l'extérieur.
Quel est l'intérêt concret de « un seul gabarit pour toute la génération » ?
Un changement de gabarit invalide toutes les données de SFT existantes, tous les caches KV en production, et tous les clients déjà déployés. En concevant l'extensibilité dans le format, Moonshot peut ajouter des capacités à K3.1, K3.2… sans rien casser.
Chapitre précédent : Extension du contexte à 1 M · Chapitre suivant : Apprentissage par renforcement