Série : Apprendre à coder avec l’IA

Aujourd'hui, chaque appelant LLM a reçu des garde-fous sur la longueur des prompts

Un pic de coût, un audit, et des garde-fous de longueur de prompt pour chaque appelant LLM.

Chaque appelant LLM, un seul garde-fou Budgets de tokens par appelant, hiérarchie de troncature, télémétrie, application par CI NON PLAFONNÉ 5 appelants, aucun plafond de longueur PLAFONNÉ budgétisé, journalisé, borné LLMCallGuard Tronquer et journaliser Repli grossier Échouer bruyamment Barrière de régression CI
Un garde-fou partagé transforme une amplification de coût silencieuse en un échec bruyant et borné.

Un prompt non plafonné est une attaque d'amplification de coût qui n'attend qu'à se produire. RakuAI traite la discipline de longueur des prompts comme une infrastructure de sécurité — appliquée au niveau du build, et non laissée à la mémoire.

Le runtime appelle des LLM à davantage d’endroits que ce que je surveillais. Il y a l’endroit évident (le XRAssistantService qui pilote les expériences AR à commande vocale). Il y a le moins évident (la couche de comportement des PNJ, où le cerveau d’un agent dans le monde est en partie un appel LLM). Il y a le moins évident de tous (les outils de développement qui ont commencé à s’infiltrer dans le build du runtime : validateurs de schéma, analyseurs de débogage, un résumeur automatique de playtest).

Chacun de ces endroits a été écrit indépendamment au cours des six derniers mois. Chacun a été relu au moment de la PR. Aucun d’entre eux, jusqu’à ce week-end, n’avait de discipline uniforme sur la longueur des prompts.

Ce billet explique pourquoi c’est devenu un problème, ce que l’audit a trouvé, et à quoi ressemblent les nouveaux garde-fous.

Comment le problème a émergé

Le déclencheur a été un contrôle de routine du suivi des coûts. En examinant la dépense quotidienne d’API LLM, un environnement de développement précis dépensait quinze fois plus que les autres. Même nombre d’agents, même charge de test, même modèle. Quinze fois le coût.

Remonter la trace du pic a mené au chemin de code du résumeur de playtest. Le résumeur prend une session de playtest terminée, empaquette l’état de scène pertinent dans un prompt, l’envoie à un LLM cloud, et récupère une analyse structurée de la session. La fonction avait été écrite quand les scènes étaient petites. « L’état de scène pertinent » était un résumé structuré de ce qui s’était passé. Avec le temps, le résumé avait grossi. Un environnement de développement précis avait fait tourner de longs playtests qui produisaient d’énormes résumés. Le résumeur envoyait ces résumés sans contrôle au modèle. Le modèle facturait au token.

Le correctif pour cet appelant unique était évident. Plafonner la longueur du résumé. Tronquer si nécessaire. Journaliser quand la troncature se produit.

La question plus profonde était celle que j’ai posée ensuite : combien d’autres appelants dans le runtime ont la même vulnérabilité ? Je suis allé vérifier. L’audit a été le vrai travail.

Ce que l’audit a trouvé

Onze endroits dans le runtime appellent un LLM. Sur ces onze :

  • Deux avaient des contrôles de longueur explicites et un comportement raisonnable en cas de dépassement (tronquer, journaliser, réessayer avec un contexte plus petit). Ceux-là étaient corrects.
  • Cinq n’avaient aucun contrôle de longueur. Ils envoyaient tout ce que l’appelant leur donnait.
  • Trois avaient un contrôle de longueur mais il était trop généreux pour être utile (cent mille tokens, bien au-delà de tout usage sensé mais bien en deçà du catastrophique).
  • Un était un chemin de débogage qui n’aurait jamais dû se trouver dans le binaire de production ; il avait un contrôle de longueur mais celui-ci était facilement contournable via un indicateur de débogage.

Les cinq sans contrôle de longueur étaient les urgents. Ils couvraient : le résumeur de playtest (déjà identifié), le cerveau de comportement des PNJ (potentiellement énorme si un PNJ pouvait observer une scène complexe), le validateur de schéma (pouvait recevoir un fichier arbitraire), le descripteur d’état du monde (pouvait décrire un morceau de monde arbitrairement grand), et l’un des outils de diagnostic destinés aux développeurs.

Chacun de ces cas avait atterri dans une PR séparée. Chaque PR avait été raisonnable prise isolément. La discipline agrégée de « chaque appelant LLM a un plafond de longueur » n’avait été le travail de personne. Alors personne ne l’avait fait.

À quoi ressemblent les garde-fous

Une petite pièce d’infrastructure a atterri samedi matin et a été appliquée à chaque appelant tout au long de la journée.

Un utilitaire partagé LLMCallGuard. Chaque endroit qui dialogue avec un LLM passe désormais par cet utilitaire au lieu de construire les requêtes directement. L’utilitaire prend un modèle de prompt, une charge utile de contexte, et un modèle cible. Il applique un plafond de longueur (configurable par modèle, avec des valeurs par défaut sensées basées sur la fenêtre de contexte documentée du modèle). Il journalise la longueur réelle du prompt utilisé au niveau INFO pour le cas budgétisé, au niveau WARNING quand il doit tronquer, et au niveau ERROR quand la troncature ne parvient pas à respecter le plafond.

Des budgets par appelant. Chaque appelant LLM du runtime a désormais un budget de tokens explicite par appel. Le budget est inférieur à la fenêtre de contexte du modèle parce que nous voulons laisser de la marge pour la réponse et parce que nous voulons échouer bruyamment avant que le modèle n’échoue silencieusement. Les budgets vont de quelques milliers de tokens (le validateur de schéma) à vingt mille tokens (le résumeur de playtest, avec un vrai plafond qui empêche l’incident d’origine).

Télémétrie. Chaque appel LLM enregistre désormais la taille du prompt, la taille de la réponse, le modèle, le budget consommé, et la latence. La télémétrie est ce qui me permet de remarquer le prochain incident avant que la facture n’arrive.

Une hiérarchie de modes d’échec. Quand la charge utile d’un appelant dépasse le budget, l’utilitaire tente une séquence de correctifs dans l’ordre. D’abord, il tente une troncature intelligente (préserver le contexte le plus récent, abandonner le plus ancien, garder le prompt système intact). Ensuite, si la troncature intelligente ne suffit toujours pas, il tente une troncature plus grossière (abandonner des sections entières). Enfin, si aucune troncature ne suffit, il échoue bruyamment avec une erreur claire plutôt que d’envoyer une requête surdimensionnée que le modèle rejettera. Cette hiérarchie fait que la plupart des cas se récupèrent avec grâce, que les cas limites échouent de façon observable, et qu’aucun appelant n’envoie jamais de charge utile non plafonnée.

Un contrôle CI qui empêche la régression. Tout nouveau code qui dialogue avec un LLM doit passer par LLMCallGuard. Le scan CI détecte tout nouvel appel LLM direct qui contourne l’utilitaire et signale la PR. Le schéma est le même que celui utilisé par l’audit de sécurité il y a quelques week-ends pour les points de terminaison d’administration : appliquer la discipline au niveau du build, pas à la relecture.

Ce que j’en ai retenu

Trois choses.

Une discipline qui n’est le travail de personne est une discipline qui ne se produit pas. Les onze appelants LLM avaient chacun été raisonnables au moment de la PR. Le comportement collectif n’avait pas été relu parce que personne ne possédait la préoccupation transversale. Le correctif a consisté à faire de cette préoccupation transversale une pièce d’infrastructure par laquelle chaque appelant doit passer, ce qui rend la discipline impossible à oublier.

Le coût est une préoccupation de sécurité. Je considérais les « prompts non plafonnés » comme une préoccupation de correction ou de robustesse. Le pic de coût de l’environnement de développement m’a appris à y penser comme à une préoccupation de sécurité. Un attaquant capable d’influencer le contenu d’un appel LLM depuis le runtime peut faire grimper le coût arbitrairement pour l’opérateur. Les mêmes défenses (plafonds de longueur, application de budget, télémétrie) protègent à la fois contre les défaillances de robustesse et contre les attaques d’amplification de coût.

Auditer selon une cadence. L’audit d’aujourd’hui a trouvé cinq vulnérabilités que la relecture de PR avait manquées. Le prochain audit en trouvera d’autres. La discipline de faire tourner un passage d’audit planifié sur une préoccupation transversale précise est la seule méthode fiable que j’ai trouvée pour faire remonter ce que la relecture manque.

Ce que partenaires et constructeurs devraient en retenir

Si vous faites tourner quoi que ce soit qui appelle un LLM depuis du code de production et que vous n’avez pas audité chaque appelant pour la discipline de longueur de prompt, faites-le. L’audit est petit. Les découvertes sont probables. Le coût de le faire aujourd’hui est bien plus petit que le coût de le faire après un incident.

Si vous êtes un laboratoire d’IA qui construit des agents appelant d’autres LLM, la métrique à optimiser est « l’agent signale-t-il quand il construit un prompt non plafonné ». La plupart des agents ne le font pas. Ceux qui le font sont ceux en qui j’ai confiance pour le travail sérieux.

Si vous évaluez un moteur pour un partenariat et que ce moteur s’intègre à des LLM cloud, posez la question sur la discipline de longueur de prompt. La bonne réponse est « chaque appelant passe par un utilitaire partagé, avec des budgets par appelant, avec de la télémétrie, avec un garde-fou CI ». La mauvaise réponse est « nous n’avons pas encore rencontré ce problème ».

Samedi après-midi. Chaque appelant LLM du runtime passe désormais par le même garde-fou. La prochaine cadence d’audit est au calendrier.

Retour à la construction.

Construisez sur un runtime qui protège chaque appel LLM

RakuAI est le runtime spatial natif IA que votre modèle pilote en production — avec des budgets de longueur de prompt, de la télémétrie et une application CI intégrés à la frontière. Découvrez ce qu'une infrastructure disciplinée débloque pour votre stack.

← Tous les articles