01 / Mes articles / Publié
Je me trompais de coupable
Je croyais savoir ce que mon agent me coûtait. J'ai construit un compteur, et mes hypothèses ne tenaient pas.
Rédigé le
J'ai regardé ma facture d'API après quatre jours d'agent personnel, et je me suis mis à chercher un responsable. Je l'ai trouvé sans effort : une tâche qui se réveille toutes les quinze minutes pour vérifier s'il y a une urgence. Quatre-vingt-seize réveils par jour, évidemment que c'était elle.
Elle ne coûtait rien du tout. Voici comment je m'en suis rendu compte, et les deux autres choses que je croyais savoir et qui étaient fausses aussi.
Il n'y avait aucune mesure
Premier constat, gênant : mon outil ne conservait aucune trace de consommation. Ni par tâche, ni par jour, ni du tout. Le fournisseur renvoie pourtant les compteurs à chaque appel, dans la réponse. Ils étaient simplement jetés.
J'ai donc mis un relais local devant l'API. Un petit programme qui écoute sur un port, transmet la requête telle quelle, laisse passer la réponse, et note au passage ce que le fournisseur annonce : tokens d'entrée, tokens de sortie, part servie par le cache, durée, code de retour. Il ne garde ni les questions, ni les réponses, ni la clé. Un port par profil d'agent, ce qui suffit à attribuer la consommation sans jamais lire le contenu.
Une précaution que je recommande si vous faites pareil : ce relais devient un point de passage obligatoire. S'il tombe, l'agent ne fonctionne plus. Redémarrage automatique, et le retour arrière doit tenir en une ligne de configuration.
Je l'ai publié, avec ses limites écrites noir sur blanc : Consulter le code de mon compteur de consommation. Il ne garde que des compteurs : jamais le contenu des échanges, jamais la clé. Le premier enregistrement du compteur date du . Il ne couvre pas les jours précédents.
Première hypothèse fausse : la fréquence
La tâche des 15 minutes a produit 370 déclenchements en quatre jours. Sur ces 370, un seul a dépassé cinq secondes. Ce décompte des réveils est distinct des relevés du compteur API.
Elle est construite sur un motif que je recommande maintenant partout : un script bon marché tourne d'abord, son résultat est comparé à celui du tour précédent, et le modèle n'est réveillé que si quelque chose a changé. Tant que le détecteur ne trouve rien, il n'y a pas d'appel. Pas d'appel court, pas d'appel réduit : pas d'appel.
La fréquence n'est donc pas un coût en soi. Une tâche qui tourne toutes les 15 minutes et une tâche quotidienne coûtent la même chose si toutes les deux ne réveillent le modèle que quand il y a matière. Ce qui coûte, c'est de faire juger une machine qui n'a rien à juger.
Deuxième hypothèse fausse : le catalogue
J'ai mesuré une question triviale, du genre « réponds par le mot A ». Le relevé affichait 14 950 tokens d'entrée. Pour trois mots.
Ce contexte permanent, je l'ai attribué au catalogue de compétences installées : 70 outils, environ 12 familles, dont plusieurs parfaitement inutiles chez moi. J'en ai retiré une, manifestement inutilisable puisqu'elle pilote un système d'exploitation que je n'ai pas. J'ai refait exactement la même mesure.
Le relevé affichait encore 14 950 tokens. Gain : zéro.
J'ai recommencé en retirant les règles de projet, puis en lançant depuis un dossier neutre, sans aucun fichier d'instructions. Le relevé affichait 14 947 tokens. Ce contexte permanent comprend le prompt système de l'outil et la description de ses outils. Je ne peux pas les réduire depuis l'extérieur, et aucun élagage ne m'aurait fait gagner quoi que ce soit.
J'ai remis la famille en place. C'est le point que je tiens à souligner : une expérience qui revient négative est une expérience réussie. Elle m'a évité de retirer des fonctions à mon agent en croyant l'optimiser, et de l'expliquer ensuite par un raisonnement plausible mais faux.
Ce qui coûte réellement
Trois choses, et aucune n'était dans mes hypothèses.
Le nombre d'étapes. Une exécution de mon brief enchaîne 13 appels au modèle. À chaque étape, tout le contexte accumulé repart. 511 963 tokens d'entrée pour une seule exécution. Le chiffre paraît énorme et il l'est : c'est le contexte multiplié par le nombre d'allers-retours.
Ce qui invalide le cache. Le fournisseur facture 10 fois moins cher les tokens déjà vus, à condition que le début de la requête ne change pas. Sur des appels répétés, je mesure 98 % de cache. Sur un appel juste après avoir modifié le fichier de configuration de l'agent, 31 %. Autrement dit : éditer la configuration de son agent coûte plus cher que de le faire tourner. J'ai passé un week-end à corriger ses instructions ligne par ligne, et c'est probablement la chose la plus chère que j'aie faite.
Le travail déterministe confié au modèle. Mon brief demandait de parcourir quatre boîtes mail, de lister les dossiers, de chercher les messages récents, compte par compte. Environ 15 allers-retours pour une opération sans aucune décision. Je l'ai remplacée par un script qui rend le même inventaire en une exécution, 1 000 tokens environ, format fixe. Le modèle ne fait plus la collecte, il fait le tri, qui est la seule chose pour laquelle il est utile.
Ce que les corrections ont donné
J'ai changé trois choses. L'effort de raisonnement, fixé par tâche au lieu d'un réglage global : faible pour du tri, moyen là où il y a du jugement. La collecte des messages, sortie du modèle et confiée à un script qui rend un inventaire en une exécution. Et le profil de voix, relu une fois par semaine au lieu de chaque matin.
Voici d'abord l'historique des durées totales des briefs du matin. Ces durées ne sont pas le temps modèle cumulé mesuré par le compteur.
| Brief matinal | Durée totale |
|---|---|
| 181,7 s | |
| 182,5 s | |
| 181,7 s | |
| 241,6 s | |
| 120,8 s |
Les trois corrections précèdent la dernière ligne. Cet historique ne suffit pas à isoler leur effet : le volume de courrier varie aussi.
Deux exécutions, deux horaires
Côté consommation, je n'ai que deux exécutions mesurées. Elles ont eu lieu le avant corrections, puis le après. Ce ne sont pas deux briefs lancés à la même heure.
| Mesure | Avant | Après |
|---|---|---|
| Appels au modèle | 13 | 12 |
| Tokens d'entrée brute | 511 963 | 383 913 |
| Tokens lus en cache | 469 504 | 348 288 |
| Part lue en cache | 91,7 % | 90,7 % |
| Tokens de sortie | 20 655 | 12 453 |
| Temps modèle cumulé | 97,2 s | 62,1 s |
L'entrée brute comprend les lectures de cache. Ce n'est pas un montant facturé. Après corrections, les 24 requêtes enregistrées comprennent 12 appels au modèle et 12 sondes de capacité sans token. Je ne compare pas ce total aux seuls appels au modèle d'avant.
Mon premier relevé était faux
Mon premier relevé donnait 392 666 tokens d'entrée. J'avais additionné tout ce qui passait dans une fenêtre horaire, sondes et autre tâche comprises. Je m'en suis aperçu en ajoutant l'attribution par tâche : elle rattache chaque appel au travail qui le demandait au lieu de le deviner à l'heure.
La seule exécution concernée représente 383 913 tokens d'entrée brute. La correction retire 2,2 % du premier total. Je ne peux pas écrire un article sur la mesure et laisser une attribution par approximation quand j'ai depuis l'outil qui la mesure vraiment.
La limite de la comparaison
Les heures diffèrent, et les volumes de courrier aussi : 17 messages avant corrections, avec un arriéré de week-end, contre cinq après. Une partie de la baisse vient donc du courrier. Je ne peux pas isoler l'effet de mes corrections avec ces deux exécutions.
L'entrée brute baisse de 25 %, la sortie de 39,7 % et le temps modèle cumulé de 36,1 %. Ce sont des écarts observés, pas une mesure du gain imputable aux corrections. Je ne peux pas attribuer la baisse de la sortie au seul effort de raisonnement.
Le nombre d'appels au modèle a lui aussi changé : 13 avant, 12 après. Il y a donc un appel en moins dans l'exécution observée. Je ne peux plus écrire que mes corrections n'ont supprimé aucune étape, ni conclure que chaque étape a été allégée sans l'avoir mesurée séparément. Le gisement suivant reste de décider quel travail mérite un aller-retour avec un modèle.
Ce que j'aurais dû faire en premier
Mesurer. Avant d'avoir des hypothèses.
J'ai perdu du temps sur deux théories confiantes et fausses, alors que le compteur a tranché en quelques minutes une fois construit. Et il a produit des résultats que je n'aurais pas devinés : à la date de rédaction, 517 sondes de découverte parmi 814 requêtes enregistrées, soit 63,5 %. Ce périmètre est celui du relevé à la rédaction, pas celui de la seule exécution du brief. Ces sondes renvoient un statut 404 ou 200 et ne consomment aucun token. Elles existaient depuis le début sans que rien ne les signale.
Il y a une règle générale là-dedans, et elle dépasse largement les agents. Quand un système a un comportement qu'on ne peut pas observer, on le remplace par une histoire. L'histoire est cohérente, elle désigne un coupable plausible, et elle peut être fausse. La seule façon d'en sortir est de rendre le comportement observable, même grossièrement, même avec un programme de cent lignes écrit en une heure.
La forme, pas le montant
Je n'avais pas de problème de coût. J'avais une forme de consommation qui aurait mal grossi : à dix tâches de plus, chacune enchaînant quinze étapes dont douze sans décision, le montant aurait cessé d'être anecdotique. C'est le bon moment pour corriger la forme, précisément parce qu'il ne fait pas encore mal.
Périmètre de la mesure
511 963 tokens d’entrée brute. Exécution avant corrections : entrée totale, lectures de cache comprises.
20 655 tokens de sortie. Exécution avant corrections : sortie annoncée par le fournisseur.
469 504 tokens lus en cache. Part de l’entrée brute de l’exécution avant corrections.
13 appels au modèle. Exécution avant corrections ; ne compte pas les sondes de capacité.
97,2 secondes de temps modèle. Temps modèle cumulé pour l’exécution avant corrections, distinct de la durée complète du brief.
383 913 tokens d’entrée brute. Une seule exécution après corrections, attribuée par tâche, lectures de cache comprises.
12 453 tokens de sortie. Une seule exécution après corrections, attribuée par tâche.
348 288 tokens lus en cache. Part de l’entrée brute de l’exécution après corrections.
12 appels au modèle. Exécution après corrections ; ne compte pas les sondes de capacité.
12 sondes de capacité. Requêtes de l’exécution après corrections, sans token consommé.
392 666 tokens d’entrée brute. Ancienne attribution erronée : toute la fenêtre horaire, autre tâche et sondes comprises.
517 sondes de découverte. Sondes dans le relevé à la date de rédaction, pas dans la seule exécution du brief.
814 requêtes enregistrées. Dénominateur du relevé des sondes à la date de rédaction.
17 messages. Courrier traité avant corrections selon le brouillon, avec un arriéré de week-end.
5 messages. Courrier traité après corrections selon le brouillon.
370 déclenchements. Décompte des réveils fourni dans le brouillon, distinct des compteurs API.
14 950 tokens d’entrée. Demande minimale avant retrait d’une famille d’outils, relevée dans le brouillon. Même demande après retrait d’une famille d’outils, relevée dans le brouillon.
14 947 tokens d’entrée. Demande depuis un dossier neutre sans règles de projet, relevée dans le brouillon.
98 % de cache. Relevé sur des appels répétés dans l’expérience distincte du brouillon.
31 % de cache. Relevé après modification de la configuration dans l’expérience distincte du brouillon.
1 000 tokens environ. Inventaire produit par le script de collecte selon le brouillon.
181,7 secondes. Durée totale du brief matinal du premier jour décrit dans le brouillon ; pas temps modèle cumulé. Durée totale du troisième brief matinal dans le brouillon.
182,5 secondes. Durée totale du brief matinal du jour suivant dans le brouillon.
241,6 secondes. Durée totale du brief matinal précédent les corrections dans le brouillon.
120,8 secondes. Durée totale du brief matinal après les corrections dans le brouillon.
62,1 secondes de temps modèle. Temps modèle cumulé après corrections indiqué dans le brouillon, distinct de la durée totale du brief.
91,7 % de cache. Part des tokens d’entrée brute déjà lus en cache avant corrections ; arrondi à une décimale.
90,7 % de cache. Part des tokens d’entrée brute déjà lus en cache après corrections ; arrondi à une décimale.
25 % de baisse observée. Écart relatif d’entrée brute entre les exécutions ; ne mesure pas un effet causal des corrections.
39,7 % de baisse observée. Écart relatif de sortie entre les exécutions ; heures et volumes de courrier différents.
63,5 % de sondes. Part des sondes dans les requêtes enregistrées à la date de rédaction ; arrondi à une décimale.
1 appel au modèle en moins. Différence du nombre d’appels au modèle entre les deux exécutions observées.
2,2 % de surestimation du premier relevé. Écart rapporté au premier relevé erroné de la fenêtre horaire, arrondi à une décimale.
24 requêtes enregistrées. Appels au modèle et sondes de capacité de l’exécution après corrections.
36,1 % de baisse observée. Écart relatif du temps modèle cumulé ; ne mesure pas un effet causal des corrections.