FinOps de l'IA agentique : mesurer jetons, coûts et outils par personne, équipe et modèle

Cinquième et dernier article de la série tirée du talk « IA agentique : vers Azure et au-delà ! » (Genève, 29 octobre 2026). Code complet dans aloizeau/agentic-ai-azure-and-beyond-geneve-2026, dossiers
gateway/deployetmonitoring.
La facture Azure donne le coût d'un déploiement Foundry. Elle ne dit pas qui l'a consommé, dans quelle équipe, ni par quel agent ou quel outil. Comme chaque jeton passe par la passerelle (article 1), c'est là que je mesure, avec trois labels CEL, une grille de prix en JSON et un tableau de bord Grafana généré par un script. C'est le tableau de bord « Usage IA » que j'ai montré à la fin de la démo A.
Trois labels sur chaque métrique
agentgateway publie déjà des métriques Prometheus sur les jetons, les requêtes et les appels MCP, mais sans dire qui consomme. Une politique attachée aux deux Gateways, interne et public, ajoute trois attributs à chaque métrique.
Fichier : gateway/deploy/metrics.yaml
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
name: metriques-usage
spec:
targetRefs:
- { group: gateway.networking.k8s.io, kind: Gateway, name: agentgateway-proxy }
- { group: gateway.networking.k8s.io, kind: Gateway, name: agentgateway-public }
frontend:
metrics:
attributes:
add:
- name: user # index 0 : retiré par Terraform si usage_metrics_per_user = false
expression: 'default(jwt.preferred_username, default(jwt.oid, default("agent:" + apiKey.team, "anonyme")))'
- name: team
expression: 'default(apiKey.team, "collaborateurs")'
- name: agent
expression: 'default(mcp.tool.arguments.agent, "-")'
La chaîne de default() se lit de gauche à droite :
| Appelant | user |
team |
|---|---|---|
| Personne avec jeton Entra ID | preferred_username (ou oid s'il manque) |
collaborateurs |
| Agent avec clé virtuelle | agent:<équipe> |
l'équipe, lue dans les métadonnées de la clé |
| Requête refusée avant authentification | anonyme |
collaborateurs |
J'ai voulu le cas anonyme : les 401 de la démo A apparaissent dans le tableau de bord sans propriétaire, et c'est le signal qu'on cherche après une fuite de clé. Le label agent sert quand LibreChat appelle l'outil MCP invoke_agent de kagent ; la passerelle lit l'argument agent de l'appel et sait quel agent a été sollicité.
Un coût calculé à chaque réponse
La passerelle chiffre chaque réponse à partir d'une grille de prix, montée en ConfigMap et référencée par les AgentgatewayParameters des deux Gateways (spec.modelCatalog, voir terraform/modules/platform/gateway.tf). Chaque réponse ajoute son coût au compteur agentgateway_gen_ai_client_cost_usd_total, avec les labels ci-dessus.
Fichier : gateway/deploy/model-costs.json
{
"providers": {
"azure": {
"models": {
"gpt-56-terra-eu": { "rates": { "input": "2.00", "output": "12.00", "cacheRead": "0.20", "cacheWrite": "2.50" } },
"gpt-5.6-terra": { "rates": { "input": "2.00", "output": "12.00", "cacheRead": "0.20", "cacheWrite": "2.50" } },
"gpt-56-luna-eu": { "rates": { "input": "0.20", "output": "1.20", "cacheRead": "0.02", "cacheWrite": "0.25" } },
"gpt-5.6-luna": { "rates": { "input": "0.20", "output": "1.20", "cacheRead": "0.02", "cacheWrite": "0.25" } }
}
},
"kaito": {
"models": { "phi-4-mini-instruct": { "rates": { "input": "0", "output": "0" } } }
}
}
}
Chaque modèle apparaît deux fois, sous le nom de modèle renvoyé par Foundry (essayé en premier) et sous le nom de déploiement envoyé par la passerelle (en repli). Un modèle absent de la grille n'a pas de coût ; le panneau « Réponses sans prix » doit donc rester à zéro.
Les montants sont des chaînes, en USD par million de jetons, avec six décimales au plus, et un champ inconnu est rejeté. Microsoft ne publie pas de prix Data Zone pour ces modèles, et la grille reprend le prix catalogue Global : c'est une estimation, à remplacer par vos prix négociés dès que vous les avez.
Le modèle auto-hébergé est à zéro par jeton, puisqu'on paie le nœud GPU à l'heure. Le lister à 0 le garde hors des « réponses sans prix » et l'affiche à côté de Foundry.
Séparer l'entrée normale de la lecture en cache change beaucoup les chiffres. Sur Terra, une lecture en cache coûte 0,20 $ par million de jetons, contre 2,00 $ en entrée normale. Un agent qui réutilise un long prompt système revient donc bien moins cher que ne le prédirait une grille sans cache.
Les requêtes PromQL du tableau de bord
Le tableau de bord est généré par monitoring/src/usage_dashboard.py : on modifie le générateur, jamais le JSON à la main. Le JSON produit est monitoring/dashboards/usage-ia.json. Voici les requêtes principales, sans les filtres de variables Grafana.
Coût sur la période, et projection à 30 jours au rythme moyen de la période :
sum(increase(agentgateway_gen_ai_client_cost_usd_total[$__range]))
sum(rate(agentgateway_gen_ai_client_cost_usd_total[$__range])) * 2592000
Jetons en entrée et en sortie (c'est un histogramme, on somme donc _sum) :
sum(increase(agentgateway_gen_ai_client_token_usage_sum{gen_ai_token_type="input"}[$__range]))
sum(increase(agentgateway_gen_ai_client_token_usage_sum{gen_ai_token_type="output"}[$__range]))
Personnes actives, sans les agents ni les anonymes :
count(
sum by (user) (increase(agentgateway_gen_ai_client_token_usage_count{
gen_ai_token_type="input", user!~"anonyme|agent:.*|unknown"}[$__range])) > 0
)
Coût par équipe et par déploiement, la vue qui sert à la refacturation :
sum by (team, gen_ai_request_model) (increase(agentgateway_gen_ai_client_cost_usd_total[$__range]))
Latence et temps jusqu'au premier jeton, pour savoir si la bascule vers Luna dégrade l'expérience :
histogram_quantile(0.95, sum by (le) (rate(agentgateway_gen_ai_server_request_duration_bucket[$__rate_interval])))
histogram_quantile(0.95, sum by (le) (rate(agentgateway_gen_ai_server_time_to_first_token_bucket[$__rate_interval])))
Outils MCP appelés, par serveur et par outil :
sum by (server, resource) (increase(agentgateway_mcp_requests_total{method="tools/call"}[$__range]))
Pendant une bascule, la part de gpt-56-luna-eu monte dans le panneau « Coût par déploiement ». On voit sur le même écran l'incident et la facture qui baisse.
Une alerte de budget journalière
Le plafond de l'article 1 arrête la dépense à la minute, et le tableau de bord se consulte après coup. Il manque une alerte à l'échelle de la journée. Ces règles ne sont pas dans le dépôt ; elles se placent à côté de la PrometheusRule des garde-fous (guardrails/deploy/base/observability.yaml), avec le label release: kps sans lequel l'opérateur les ignore.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: budget-ia
labels: { release: kps }
spec:
groups:
- name: budget-ia
rules:
- alert: BudgetJournalierEquipe
expr: sum by (team) (increase(agentgateway_gen_ai_client_cost_usd_total[24h])) > 50
for: 15m
labels: { severity: warning }
annotations:
summary: "{{ $labels.team }} : plus de 50 USD de LLM sur 24 h"
- alert: CoutSansProprietaire
expr: sum(increase(agentgateway_requests_total{status="401"}[1h])) > 100
labels: { severity: warning }
annotations:
summary: "Plus de 100 appels non authentifiés en 1 h : clé ou jeton qui circule ?"
Réglez le seuil équipe par équipe. 50 USD par jour convient à une équipe pilote et serait absurde pour l'assistant de tout le groupe. La seconde alerte correspond à la phrase de la démo A : le lundi matin, on reçoit une alerte plutôt qu'une facture.
Extraire la refacturation mensuelle en PowerShell
Le contrôle de gestion ne lit pas Grafana, et l'API HTTP de Prometheus suffit pour produire un CSV par équipe :
kubectl -n monitoring port-forward svc/prometheus-operated 9090:9090 # autre fenêtre
$q = 'sum by (team) (increase(agentgateway_gen_ai_client_cost_usd_total[30d]))'
$r = Invoke-RestMethod "http://localhost:9090/api/v1/query?query=$([uri]::EscapeDataString($q))"
$r.data.result |
ForEach-Object { [pscustomobject]@{ Equipe = $_.metric.team; CoutUSD = [math]::Round([double]$_.value[1], 2) } } |
Sort-Object CoutUSD -Descending |
Export-Csv refacturation-ia.csv -NoTypeInformation -Encoding UTF8 -Delimiter ';'
Vérifiez d'abord la rétention, que kube-prometheus-stack fixe à 10 jours par défaut. Une fenêtre [30d] sur 10 jours de données renvoie un chiffre faux sans la moindre erreur. Pour refacturer, augmentez prometheus.prometheusSpec.retention ou exportez vers un stockage long terme, Azure Monitor managed service for Prometheus par exemple.
Cardinalité des labels
Le label user crée une série Prometheus par utilisateur, modèle et route, multipliée par le nombre de buckets pour les histogrammes de jetons et de latence. Pour un pilote de quelques dizaines de personnes, c'est négligeable ; pour 3 000 personnes, c'est la métrique la plus chère du cluster.
Le dépôt prévoit la bascule. Avec usage_metrics_per_user = false dans terraform.tfvars (voir terraform/terraform.tfvars.example), Terraform retire l'attribut d'index 0 de la politique, et le tableau de bord garde l'équipe, le modèle, le coût et les outils. Le détail par personne part alors dans les journaux d'accès de la passerelle ou dans les traces OpenTelemetry (Log Analytics), où la cardinalité ne coûte rien. J'y ajouterais un label department tiré d'une revendication du jeton Entra ID, le niveau dont le contrôle de gestion a besoin.
Ce que la passerelle ne voit pas
Les skills kagent lui échappent. Un skill est un dossier d'instructions et de scripts chargé dans l'agent depuis une image OCI ou un dépôt Git ; l'utiliser ne crée aucun appel réseau, et aucune passerelle ne peut le compter. Pour les suivre, il faut instrumenter les agents avec les traces OpenTelemetry de kagent, envoyées vers Application Insights.
Elle ne voit pas non plus un agent qui appelle directement le serveur MCP de son équipe dans le namespace. La correction est celle de l'article 4 : faire passer l'agent par la passerelle avec un RemoteMCPServer et la clé de l'équipe.
Coût du cluster
On me demande à chaque présentation combien coûte le socle, hors inférence. Les chiffres ci-dessous viennent de l'API Azure Retail Prices (Sweden Central, Linux, 730 h par mois, vérifiés en octobre 2026). Ils couvrent les nœuds et le plan de contrôle AKS, sans les disques, le load balancer, les journaux ni la sortie réseau.
| Configuration | Paiement à l'usage | Réservation 3 ans |
|---|---|---|
| PoC : 2 × D4as_v5, plan Free | environ 269 USD / mois | sans objet |
| Production : 3 × D8as_v5, plan Standard (SLA) | environ 879 USD / mois | environ 391 USD / mois |
| Production : 6 × D8as_v5, plan Standard (SLA) | environ 1 685 USD / mois | environ 710 USD / mois |
Le week-end à 60 000 € évoqué en ouverture du talk représente environ 41 mois du cluster de production à six nœuds.
Le code
gateway/deploy/metrics.yamletmodel-costs.json: labels d'usage et grille de prixmonitoring/: le générateur du tableau de bord « Usage IA » et son READMEdocs/architecture/: le schéma du flux des métriquesslides/rex-architectures-ia-entreprise-clair.pdf: les slides du talk
C'est le dernier article de la série. Le dépôt compagnon contient le reste, et se déploie puis se détruit en un après-midi.
About the author
Ayant eu de multiples expériences dans le monde du conseil, j'ai pu acquérir une expertise dans la conception et la construction de services de collaboration d'entreprise. Je suis passionné par les technologies Microsoft et surtout Azure. Aujourd'hui je possède une solide expérience en méthodologie de développement et j'ai mené des équipes de développement technique au succès. Je possède également une solide connaissance de l'infrastructure qui fait de moi une ressource efficace pour mettre en œuvre la transformation numérique vers le cloud Microsoft. Je suis un professionnel efficace et honnête qui aime relever les challenges. Aimant partager mes connaissances; je suis à l'aise en tant que Technical Leader et en tant que membre d'une équipe. Mes compétences techniques sont les suivantes: Azure, DevOps, Architecture Applicative, Développement de solution Cloud Native, écosystème Microsoft... et bien d'autres. Pour voir mes certifications Microsoft : https://www.youracclaim.com/users/antoine-loizeau
Comments
Join the discussionNo comments yet. Be the first to comment.
IA agentique : vers Azure et au-delà !
Série d'article tirée du talk « IA agentique : vers Azure et au-delà ! » (Chalet Azure Romandie, Genève, 29 octobre 2026). Tout le code est dans le dépôt aloizeau/agentic-ai-azure-and-beyond-geneve-2026.





