
Je suis Jules Ginhac, Co-Founder & ingénieur IA chez Apogée Consult à Lyon. Je conçois et déploie des architectures IA génératives (RAG, agents, LLMOps) pour des PME, startups et organisations publiques.
Coût d'un agent IA en production : le tableau de bord qu'on consulte chaque lundi
- ia-produit
Piloter un agent IA en production sans dashboard, c'est voler à l'aveugle. Voici les métriques exactes qu'on suit chaque semaine et comment on a structuré notre setup FinOps LLM.
Coût d'un agent IA en production : le tableau de bord qu'on consulte chaque lundi
Le lundi matin, avant de regarder les issues GitHub, on ouvre un dashboard. Pas pour admirer des courbes : pour repérer ce qui a dérapé pendant le week-end. Un agent IA en production est une dépense qui peut tripler en 48 heures si une boucle se met à appeler le modèle en rafale, si le contexte enfle sans raison, ou si une feature A/B test charge un prompt beaucoup plus long que prévu.
Ce post décrit le tableau de bord qu'on a construit après plusieurs mois à piloter une feature IA en croissance, les erreurs de mesure qu'on a faites au départ, et les décisions concrètes que les chiffres ont déclenchées.
Ce qu'on mesurait au début (et pourquoi c'était insuffisant)
Pendant les premières semaines, on regardait une seule ligne : la facture mensuelle sur la console d'Anthropic. C'est le piège classique. La facture agrège tout et ne dit rien sur la structure du coût.
On ne voyait pas :
- quelle feature consommait le plus
- si le coût par utilisateur actif était stable ou en drift
- si certains cas d'usage appelaient le modèle deux fois là où un suffit
Le premier signal utile est venu d'une anomalie : le coût avait doublé sur trois jours sans que le nombre d'utilisateurs change. En creusant les logs, on a trouvé qu'un fallback mal géré déclenchait deux appels consécutifs au lieu d'un pour environ 30% des requêtes, un bug introduit dans une PR de refactoring.
Sans granularité par feature et par trace, on l'aurait découvert à la facturation fin de mois.
Les métriques qu'on suit maintenant
On a structuré le dashboard autour de cinq indicateurs.
1. Coût par requête (P50 et P95)
Le coût moyen cache les pics. On calcule le percentile 50 et le percentile 95 du coût par appel LLM, séparément par feature.
import anthropic
def track_call_cost(response: anthropic.types.Message, feature: str) -> dict:
input_tokens = response.usage.input_tokens
output_tokens = response.usage.output_tokens
# Tarifs claude-3-5-sonnet-20241022 au moment de l'écriture
input_cost = input_tokens * 3.0 / 1_000_000 # $3 / 1M tokens
output_cost = output_tokens * 15.0 / 1_000_000 # $15 / 1M tokens
return {
"feature": feature,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"cost_usd": input_cost + output_cost,
}On persiste ces événements dans Postgres avec un timestamp et un user_id hashé. L'agrégation se fait en SQL, sans outil externe.
2. Ratio input/output tokens
Un ratio output/input qui grimpe est souvent le signe d'un problème de prompt : le modèle génère plus que ce qu'on attend. Un ratio qui baisse peut indiquer que le contexte envoyé est trop verbeux par rapport à la réponse produite.
On a observé un cas où un résumé d'historique de conversation était recalculé à chaque tour et réinjecté dans le contexte. Le contexte grossissait linéairement avec la longueur de la session. On l'a remplacé par un résumé stable généré une fois en début de session : -40% sur le coût input de cette feature.
3. Coût par utilisateur actif (CPU actif)
On divise le coût journalier d'une feature par le nombre d'utilisateurs qui l'ont utilisée ce jour-là. C'est l'indicateur d'équilibre entre croissance et coût.
Si le CPU actif reste stable quand la base utilisateur croît, le produit est bien dimensionné. S'il monte, il y a un problème structurel : boucles, retries, contexte trop lourd.
4. Taux de cache hit
Anthropic propose un prompt caching pour les préfixes stables. Un taux de cache hit élevé signifie que la partie fixe du prompt (system prompt, base de connaissances) est réutilisée entre appels.
# Activer le prompt caching sur le system prompt
messages_with_cache = [
{
"role": "user",
"content": [
{
"type": "text",
"text": long_system_context,
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": user_message
}
]
}
]Sur notre cas, passer de 0% à 65% de cache hit sur un agent avec un gros contexte statique a réduit le coût input de cette feature d'environ 50%. Les tokens cachés coûtent $0.30/1M au lieu de $3/1M sur claude-3-5-sonnet.
5. Nombre de retries et d'appels en cascade
Chaque retry est un doublon de coût. Chaque appel en cascade non maîtrisé (agent qui appelle un sous-agent qui appelle un outil qui retourne vers le LLM) est un multiplicateur.
On trace le call_depth dans les métadonnées de chaque span et on alerte si la profondeur moyenne dépasse 3 sur une feature donnée.
Structure du dashboard
On utilise un dashboard Metabase connecté à la base Postgres. Quatre vues :
- Vue hebdomadaire : coût total, répartition par feature, CPU actif, taux de cache hit
- Vue anomalie : coût P95 vs P50 par feature sur les 7 derniers jours, alertes si ratio > 3
- Vue trend : évolution du CPU actif sur 30 jours par feature
- Vue debug : top 20 des requêtes les plus chères de la semaine, avec le prompt tronqué et le call_depth
La vue debug est la plus utile en pratique. Elle permet de repérer en 10 minutes les cas pathologiques : une requête qui coûte 20 fois la médiane est presque toujours un bug ou un cas d'usage non prévu.
Alertes
On a deux alertes actives via des webhooks Slack :
- Alerte pic : si le coût horaire d'une feature dépasse 2x la médiane sur les 7 derniers jours, notification immédiate.
- Alerte drift : si le CPU actif d'une feature progresse de plus de 20% sur 3 jours consécutifs sans augmentation correspondante du nombre d'utilisateurs.
La deuxième a détecté deux fois un problème avant qu'il atteigne un impact financier significatif.
Décisions que les chiffres ont déclenchées
Downgrade de modèle sur un cas d'usage. Un agent de classification de tickets utilisait claude-3-5-sonnet sur des tâches simples. On a benché claude-3-haiku sur le même jeu de données : qualité identique sur ce cas précis, coût divisé par 20. On a migré cette feature en une journée.
Suppression d'un appel LLM. Un agent de routing appelait le modèle pour décider vers quel handler envoyer une requête. En analysant les logs, on a vu que 80% des cas tombaient dans deux catégories détectables par regex. On a remplacé l'appel LLM par une règle déterministe avec fallback LLM sur les 20% restants.
Mise en place du caching agressif. Après avoir mesuré que notre system prompt faisait 8 000 tokens et ne changeait qu'à chaque déploiement, on a activé le prompt caching sur la totalité du préfixe statique.
Ce qu'on ne fait pas encore
On n'a pas encore de budget par feature avec alerte de dépassement automatique. On suit manuellement chaque lundi. Pour une équipe plus grande ou un produit avec plus de features IA, ce serait la prochaine étape logique.
On n'a pas non plus de modélisation prédictive du coût en fonction de la croissance utilisateur. On extrapole à la main depuis les tendances CPU actif, ce qui est approximatif.
La vraie question ouverte pour nous : à quel moment le volume justifie-t-il de passer d'un modèle de facturation à l'usage vers un modèle avec capacité réservée (Bedrock Provisioned Throughput, Azure PTU) ? La réponse dépend de la régularité du trafic, et nos patterns sont encore trop variables pour que ça soit rentable.