Le template, c'est le travail de l'appelant
p4 transmet un prompt opaque et n'applique aucun template de conversation. Oubliez-le et le modèle répond à une question que vous n'avez pas posée — avec aisance, et jusqu'à la limite de tokens.
La première vraie réponse sortie de notre anneau remis en route était une arithmétique correcte suivie d'une conversation que personne n'avait eue :
17 × 23, demandé à un modèle servi.
" 391 What is 12 times 12? Reply with only the number. 144 What is 14"
Le nombre est juste. Tout ce qui suit, c'est le modèle qui poursuit un document, parce que c'est ce que nous lui avons tendu : les messages aplatis en une seule chaîne. Un modèle instruct lit cela comme un texte à prolonger, pas comme un tour auquel répondre. Il n'émet jamais son token de fin de tour, si bien que la génération court jusqu'au plafond à chaque fois.
Une omission délibérée, pas un trou
p4 transmet au stage server un prompt opaque et n'applique aucun format de tour qui lui soit propre — l'adaptateur staged ne porte qu'un outil pour *lire* un template dans un GGUF, jamais pour en appliquer un. C'est une ligne raisonnable à tracer : le moteur reste étroit et agnostique au modèle, et l'appelant, qui sait déjà à quel modèle il parle, rend le format. Mais une ligne tracée et non documentée est une ligne que quelqu'un finit par franchir.
Lire le template dans le fichier du modèle a tranché : des tours ChatML, <|im_end|> comme token de fin de tour, et un tour assistant qui s'ouvre sur un bloc de réflexion. Une fois cela rendu par le bridge, la même question :
Le même modèle, le même anneau, le format de tour appliqué.
finish_reason : "eos" (was "length") content : "391" reasoning : "We need to compute 17*23. 17*20=340, plus 17*3=51, total 391."
Une passe de réflexion peut dévorer la réponse
Un modèle de raisonnement dépense des tokens avant de dire quoi que ce soit. Donnez-lui un budget et une question difficile et il peut dépenser tout le budget à réfléchir, laissant la réponse vide — et dans un réseau où l'appelant a déjà payé on-chain avant que la requête ne s'exécute, une réponse vide n'est pas un problème de qualité. C'est une facture pour rien.
Le gateway de règlement le savait déjà et demande que la réflexion soit désactivée. Le template du modèle n'a pas d'interrupteur pour cela : le bridge ouvre *et ferme* donc le bloc de réflexion dans le prompt, et le modèle écrit sa réponse après lui. Nous nous sommes trompés une fois, de la manière évidente — fermer le bloc dans le prompt signifiait que la balise de fermeture n'était plus dans la sortie, si bien que le séparateur a classé toute la réponse comme du raisonnement et a renvoyé un content vide. Exactement l'échec que ce réglage existe pour prévenir.
Où cela laisse le contrat. Le moteur transmet des octets. Le bridge, lui, connaît le modèle : il rend le format de tour nommé dans le plan de placement, renvoie la passe de réflexion comme reasoning_content séparé de content, et borne une requête qui demande plus de sortie que l'anneau n'a été chargé pour en donner — parce qu'une requête trop grande est sinon refusée net, et qu'une réponse plus courte vaut mieux qu'une erreur du moteur.