Méthode · IA
Le coût d’un agent IA : mesurer et réduire la facture de jetons
Pourquoi la facture d’un agent grandit avec chaque tour, comment la mesurer à partir du champ usage, et les leviers qui comptent : cache de prompt, modèle adapté, contexte maîtrisé.
Comment un LLM est facturé
Les API de LLM facturent au jeton (token), avec deux tarifs :
- les jetons d’entrée : tout ce qui est envoyé au modèle (consignes, historique, documents, résultats d’outils) ;
- les jetons de sortie : ce que le modèle produit, raisonnement compris quand il est activé. Ils coûtent plusieurs fois plus cher que l’entrée.
Le prix dépend aussi du modèle : un grand modèle peut coûter plusieurs fois le prix d’un petit, pour chaque jeton. Les ordres de grandeur, et non les tarifs exacts qui changent souvent, suffisent pour raisonner.
Pourquoi un agent coûte plus cher
Un appel unique (résumer un texte) envoie une fois l’entrée. Un agent fonctionne en boucle : à chaque tour, le harness renvoie toute la conversation au modèle, qui n’a pas de mémoire. Chaque fichier lu, chaque sortie de commande est donc refacturé à tous les tours suivants.
Tour 1 : consignes (8 000) + demande (200) → 8 200 jetons envoyés
Tour 2 : tout le tour 1 + appel d'outil + résultat (3 000) → 11 400 jetons envoyés
Tour 3 : tout le tour 2 + lecture d'un fichier (6 000) → 17 600 jetons envoyés
...
Tour 20 : la conversation entière, renvoyée une vingtième fois → 120 000 jetons envoyés
Total facturé en entrée sur 20 tours : la somme de tous ces envois, bien plus que 120 000. Conséquence : le coût d’une session croît bien plus vite que sa durée. Une session de vingt tours peut coûter des dizaines de fois une session de deux tours, et une sortie d’outil volumineuse lue au début pèse sur toute la suite.
Mesurer avant d’optimiser
Chaque réponse de l’API indique sa consommation exacte dans un champ usage :
"usage": {
"input_tokens": 1830,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 41200,
"output_tokens": 640
} Journaliser ce champ pour chaque appel, avec la tâche concernée, permet de savoir où va l’argent :
import json
# Tarifs symboliques par million de jetons : remplacer par la grille réelle du fournisseur et du modèle.
TARIF = {"entree": 1.0, "ecriture_cache": 1.25, "lecture_cache": 0.1, "sortie": 5.0}
def cout(usage):
return (
usage.get("input_tokens", 0) * TARIF["entree"]
+ usage.get("cache_creation_input_tokens", 0) * TARIF["ecriture_cache"]
+ usage.get("cache_read_input_tokens", 0) * TARIF["lecture_cache"]
+ usage.get("output_tokens", 0) * TARIF["sortie"]
) / 1_000_000
# journal.jsonl : une ligne par appel, avec la tâche et le champ usage de la réponse
par_tache = {}
for ligne in open("journal.jsonl"):
appel = json.loads(ligne)
par_tache[appel["tache"]] = par_tache.get(appel["tache"], 0) + cout(appel["usage"])
for tache, total in sorted(par_tache.items(), key=lambda t: -t[1]):
print(f"{tache:30} {total:8.4f}") Le classement révèle presque toujours une ou deux tâches qui représentent l’essentiel de la dépense : c’est là qu’il faut agir, pas ailleurs.
Le cache de prompt
Puisque chaque tour renvoie le même début de conversation, les fournisseurs proposent un cache de prompt : un préfixe déjà vu récemment est relu pour une petite fraction du prix normal, et traité plus vite. Dans l’exemple ci-dessus, 41 200 jetons sur 43 030 viennent du cache.
- Le cache porte sur un préfixe exact : consignes et outils stables en tête, ce qui change à la fin.
- Modifier les consignes, l’ordre des outils ou un horodatage en tête de prompt invalide tout ce qui suit.
- Le cache expire après quelques minutes d’inactivité (durée variable selon le fournisseur et l’option choisie).
Sur un agent, c’est en général le levier le plus rentable : il ne change rien au résultat et réduit fortement la facture d’entrée.
Le bon modèle pour chaque tâche
Utiliser le plus grand modèle partout est confortable, et cher. Une répartition par tâche est plus juste :
| Tâche | Modèle adapté |
|---|---|
| Classer, extraire un champ, router une demande | Petit modèle rapide |
| Résumer, reformuler, répondre sur un document fourni | Modèle intermédiaire |
| Planifier, coder, raisonner sur plusieurs fichiers | Grand modèle |
Les traitements sans urgence (classer des milliers de demandes la nuit) passent par l’API de traitement par lots des fournisseurs, sensiblement moins chère qu’un appel interactif. Un changement de modèle se valide sur un jeu d’évaluation : moins cher n’a d’intérêt que si la qualité tient.
Un contexte maîtrisé
Chaque jeton évité dans le contexte est évité à tous les tours suivants. Les techniques de l’article Gérer la fenêtre de contexte sont aussi des économies :
- sorties de commandes bornées (
tail,--oneline,grep) plutôt que complètes ; - lecture d’une plage de lignes plutôt que du fichier entier ;
- instructions permanentes courtes, procédures dans des skills chargés à la demande ;
- exploration volumineuse confiée à un sous-agent qui ne renvoie que sa conclusion ;
- une nouvelle session par sujet, plutôt qu’une conversation qui s’allonge sans fin ;
- une longueur de sortie plafonnée, adaptée à la tâche.
Les leviers par ordre d’impact
- Mesurer par tâche : on n’optimise que ce qu’on voit.
- Activer et préserver le cache de prompt : préfixe stable, changements en fin.
- Réduire le contexte : sorties bornées, lectures ciblées, sessions courtes.
- Choisir le modèle par tâche, validé par une évaluation.
- Passer en lot ce qui n’est pas urgent.