Maîtriser le coût d'un agent IA : routage de modèles, budgets d'outils, cache et limites de retry

"OpenAI API Pricing"
À 3 h du matin, un Agent de génération de rapport renvoie une réponse vide. Le statut HTTP est 200, mais le body est vide. La logique de retry ne vérifie que le statut, donc elle continue. Chaque requête envoie 500 tokens d’entrée. Après 1 500 retries, 750 000 tokens sont partis. Le lendemain matin, la facture remet tout le monde d’aplomb.
La cause n’est pas « le modèle était trop cher ». Il manquait trois garde-fous : un circuit breaker, une vérification de budget et une classification des erreurs. Le coût d’un Agent part généralement en vrille de trois façons :
Retries sans limite. Les modes d’échec ne sont pas classés, donc une réponse vide devient une erreur récupérable. Il n’y a pas de circuit breaker, donc même 1 500 échecs consécutifs ne l’arrêtent pas. Chaque retry renvoie tout le contexte, ce qui multiplie le coût par 2 à 5.
Contexte qui gonfle. Une tâche longue tourne 6 heures et l’historique de conversation grimpe à 80K tokens. Sans checkpoint, un échec relance tout depuis le début, et chaque étape est repayée.
Surutilisation du modèle. Toutes les tâches utilisent un frontier model parce qu’il n’y a pas de stratégie de routage. Même une simple classification passe par le chemin le plus cher et gaspille 70% des tokens.
Le contrôle des coûts d’un Agent n’est pas une optimisation isolée. C’est une conception en couches : objets de budget, stratégie de routage, cache hits, circuit breakers de retry, journaux de coût et seuils d’alerte. Le bon réflexe consiste à transformer ces six objets d’ingénierie en tables de décision ou en contrôles exécutables.
Objets de budget : quoi tracer, où le garder, quand couper
Le contrôle des coûts commence par un objet de budget. Une somme totale ne suffit pas. Sans découpage, une anomalie de facture ne vous dit pas quel utilisateur, quelle tâche ou quel outil a brûlé le budget.
Sept couches de budget
Les objets de budget se découpent de la couche la plus large à la plus fine :
| Couche de budget | Objet de budget | Plafond conseillé | Déclencheur d’alerte |
|---|---|---|---|
| Layer 1 | user | Plafond quotidien/mensuel par utilisateur | Alerte si reste < 20% |
| Layer 2 | tenant | Pool de budget séparé par tenant | Alerte si reste < 30% |
| Layer 3 | workflow | Budget séparé par type de workflow | Alerte si reste < 40% |
| Layer 4 | task | Budget séparé par type de tâche | Alerte si reste < 50% |
| Layer 5 | tool | Budget par appel d’outil | Ignorer l’outil au-dessus du plafond |
| Layer 6 | retry | Limite de retries + circuit breaker | Désactiver l’outil après N échecs consécutifs |
| Layer 7 | cache | Suivi du taux de cache hit | Alerte si le taux est sous la valeur attendue |
Les plafonds exacts dépendent du modèle métier et restent de la configuration mutable. La structure en couches est plus stable. Le champ de budget restant doit être écrit dans les journaux de coût pour piloter les alertes et les circuit breakers.
Champs à enregistrer pour chaque couche
Chaque couche de budget doit enregistrer ces champs :
| Nom du champ | Usage | Type | Pourquoi le tracer |
|---|---|---|---|
model | Identifier le modèle | string | Voir si le routage de modèles est raisonnable |
inputTokens | Nombre de tokens d’entrée | integer | Calculer le coût d’entrée |
outputTokens | Nombre de tokens de sortie | integer | Le coût de sortie doit être suivi séparément |
cachedTokens | Tokens touchés par le cache | integer | Mesurer l’économie liée au cache |
costEstimate | Estimation du coût de cet appel | float | Cumuler les coûts en temps réel |
budgetRemaining | Budget restant | float | Base de décision du circuit breaker |
Le coeur de l’objet de budget est la comptabilité par dimension, pas le simple total_cost. En multi-tenant, le coût doit être réparti par tenantId. Avec plusieurs outils, toolName permet d’ouvrir la boîte noire.
Logique de circuit breaker
Quand le budget restant passe sous un seuil, déclenchez le circuit breaker :
def check_budget_before_retry(budget_remaining, retry_cost_estimate):
if budget_remaining < retry_cost_estimate:
return "skip_retry" # Ignorer le retry, il dépasserait le budget
if budget_remaining < threshold: # threshold par exemple 20%
return "wait_approval" # Budget faible, attendre une validation
return "continue"
Le circuit breaker vérifie le budget avant le retry, pas après. Estimez le coût avant chaque retry et arrêtez si la prochaine tentative dépasse le budget. C’est ainsi qu’on évite le cas « réponse vide réessayée 1 500 fois ».
Le futur article sur le context engineering expliquera quels contenus vont dans un prefix stable et lesquels restent des variables d’exécution.
Stratégie de routage de modèles : toutes les tâches n’ont pas besoin du modèle le plus cher
Un Agent de ticket triage ressemble souvent à ceci : 70% des tâches sont de la classification simple, 20% demandent un brouillon de réponse, et seuls 10% doivent monter vers un frontier model avant d’envoyer un email client. Une stratégie de routage peut économiser 40 à 85% de coût.
Routage au niveau modèle
Routez selon la complexité de la tâche :
| Niveau de tâche | Tâches typiques | Niveau de modèle recommandé | Part | Profil de coût |
|---|---|---|---|---|
| 70% - niveau S | Classification, extraction, filtrage, Q&R simple | nano/flash (moins cher) | 70% | Sortie courte, peu de tours, peu d’outils |
| 20% - niveau M | Brouillon, résumé, génération de code, raisonnement moyen | mid-tier (prix moyen) | 20% | Sortie moyenne, outils possibles |
| 10% - niveau L | Revue, architecture, raisonnement complexe, coordination multi-outils | frontier (plus cher) | 10% | Sortie longue, nombreux tours, outils fréquents |
La stratégie de routage tient en trois étapes :
Étape 1 : classer la tâche. Pour chaque workflow, définissez les critères S/M/L : longueur de sortie, nombre d’appels d’outils, profondeur de raisonnement, niveau de risque.
Étape 2 : partir du niveau S par défaut. Ne montez vers M ou L que si la tâche montre des signes de complexité.
Étape 3 : router en cascade. Si le modèle S échoue, montez vers M. Si M échoue, montez vers L. Si L échoue, passez à l’intervention humaine. Vérifiez le budget restant avant chaque montée ; si le budget ne suit pas, sautez l’upgrade.
Routage au niveau service
Le même modèle peut aussi être séparé par latency priority. Les remises et fenêtres de complétion changent ; vérifiez la tarification officielle avant publication.
| Niveau de service | Remise de coût | Délai de complétion | Cas adapté |
|---|---|---|---|
| Realtime API | Aucune remise | Réponse immédiate | Conversation Agent interactive, tâches prioritaires |
| Batch API | 50% cost discount (à revérifier) | 24-hour turnaround (à revérifier) | evals batch, classification, embeddings, traitement de dépôt de contenu |
| Flex Processing | Coût réduit (à revérifier) | Réponse plus lente, indisponibilité occasionnelle | Tâches asynchrones peu prioritaires, model evaluations, data enrichment |
Checklist de routage hors ligne :
- La tâche a-t-elle besoin d’une réponse immédiate ? Oui -> Realtime API, avec routage de modèles.
- Peut-elle attendre 24 heures ? Oui -> Batch API.
- Est-elle peu prioritaire et tolérante aux échecs occasionnels ? Oui -> Flex Processing.
- Est-ce un traitement batch comme eval, classification ou embedding ? Oui -> Batch API.
Niveaux de risque
Les tâches se classent aussi par niveau de risque :
| Niveau de risque | Opération typique | Stratégie de routage | Branche de budget |
|---|---|---|---|
| Risque faible | Classification, extraction, résumé interne | Modèle S + chemin automatique | Pas de validation, plafond plus large |
| Risque moyen | Brouillon de réponse client, suggestion de changement de code | Modèle M + validation optionnelle | Dépassement de budget -> validation possible |
| Risque élevé | Envoyer un email client, facturer, changer l’architecture | Modèle L + validation obligatoire | Attente, refus et timeout deviennent des branches de budget |
L’article Human-in-the-Loop de la même série détaillera l’effet des attentes, refus et timeouts de validation sur les branches de budget.
Contraintes clés
Le routage doit respecter quelques contraintes :
- Ne pas coder les prix en dur : les prix changent. Enregistrez model + pricingVersion au lieu de figer une formule de coût dans la logique métier.
- Vérifier le budget restant : avant une montée de niveau, vérifiez budgetRemaining. Si le budget ne suffit pas, ignorez l’upgrade ou demandez une validation.
- Classifier les erreurs : distinguez les limites récupérables du modèle des erreurs non récupérables de paramètre ou de permission.
Budgets d’appels d’outils : per-tool budget, timeout et limites de retry
Un appel d’outil a un double coût : schema et API. Chaque appel envoie le schema, les paramètres et le contexte de parsing de réponse. L’API externe peut aussi être limitée ou expirer. Ces coûts sont séparés de l’appel au modèle.
Contrôle du budget des outils
Chaque outil doit avoir ses propres contrôles :
| Contrôle | Configuration conseillée | Champ de suivi | Action déclenchée |
|---|---|---|---|
| Per-tool budget | Plafond par appel | tool_cost_estimate | Ignorer l’outil ou dégrader |
| Tool timeout | Timeout de l’API externe | tool_duration | Marquer le timeout comme erreur récupérable |
| Retry limit per tool | Limite de retries par outil | tool_retry_count | Abandonner l’outil, sans boucle de retries |
Les outils qui appellent des API externes, comme recherche, base de données ou service tiers, doivent être comptés séparément. Sinon, l’appel d’outil devient une boîte noire de coût.
Classification des échecs d’outils
Les échecs se divisent en récupérables et non récupérables :
| Type d’échec | Erreurs typiques | Stratégie | Impact coût |
|---|---|---|---|
| Échec récupérable | Timeout réseau, 503 Service Unavailable, 429 Rate Limit | Retry automatique avec exponential backoff et retry-after | Chaque retry renvoie tout le contexte |
| Échec non récupérable | 403 Permission Denied, 400 Bad Request, outil inexistant | Ne pas retry ; injecter l’erreur pour que le modèle décide | Pas de retry, donc pas de gaspillage répété |
La règle : seules les erreurs temporaires dues à des conditions externes se retry. Les erreurs de configuration interne ne se retry pas.
Circuit breaker
Après N échecs consécutifs, désactivez l’outil pour éviter la réponse vide réessayée 1 500 fois :
def circuit_breaker_tool(tool_name, consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "disable_tool" # Désactiver l'outil
return "continue"
L’état du circuit breaker doit aller dans les logs pour expliquer pourquoi l’outil a été désactivé. Après déclenchement, attendez une intervention humaine ou un check de récupération automatique, au lieu de rappeler sans fin un outil instable.
Les bases du tool calling sont couvertes dans Tool Calling. Ici, on ajoute per-tool budget, timeout et limites de retry.
Conception du Prompt Caching : prefix stable, variables et seuil de 1024 tokens
Prompt Caching optimise le coût des tokens d’entrée d’un prompt prefix stable. Ce n’est pas un cache de résultat métier. Il route les requêtes ayant le même prompt prefix vers un serveur qui a récemment traité ce même prefix, ce qui peut réduire la latence et le coût d’entrée.
Prompt Caching n’est pas un cache de résultat
Prompt Caching garde un prompt prefix stable, pas un résultat métier. La différence compte :
- Prompt Caching : cache un prompt prefix stable, comme system prompt ou tool schema. Un hit économise des tokens d’entrée, mais l’inférence est quand même exécutée.
- Cache de résultat métier : garde une sortie complète, comme un résultat d’outil ou une requête de base de données. Un hit retourne directement sans appeler le modèle.
Les objectifs sont différents. Prompt Caching réduit le coût des tokens d’entrée ; le cache métier réduit le coût de l’appel complet. Les deux peuvent coexister : prefix stable côté Prompt Caching, résultats d’outils fréquents côté cache métier.
Exigences de structure
La clé est de séparer le prefix stable des variables d’exécution :
| Type de contenu | Position | Probabilité de cache hit | Exemples |
|---|---|---|---|
| Prefix stable (entre en cache) | Début du prompt | Élevée | System prompt, Tool schema, documents Policy, exemples Few-shot |
| Variables d’exécution (hors cache) | Plus loin dans le prompt | Faible | User input, File fragments, Runtime state (tour actuel, variables temporaires) |
Étapes de conception :
- Placer System prompt, Tool schema et Policy au début : ces contenus restent stables sur plusieurs appels et touchent plus facilement le cache.
- Placer User input, File fragments et Runtime state plus loin : ces contenus changent à chaque appel et ne doivent pas entrer dans le prefix stable.
- Surveiller le Cache hit rate : enregistrer cachedTokens et le total de tokens d’entrée, puis calculer le taux. Au-dessus de 40%, c’est sain ; sous 20%, inspectez la structure du prompt.
Seuil et effet
Le seuil d’activation automatique et les effets de Prompt Caching sont des faits variables ; revérifiez la documentation officielle avant publication :
- Seuil : activation automatique à partir de 1024 tokens (à revérifier).
- Effet : un hit peut réduire coût et latence (revérifier les ratios exacts).
- Vérifier les hits : champ
usage.prompt_tokens_details.cached_tokens.
Les modèles pris en charge, seuils et remises de Prompt Caching peuvent changer. Vérifiez-les sur la page officielle de pricing avant publication. Le principe durable reste le même : contenu statique devant, contenu variable derrière.
Circuit breakers de retry : idempotence, checkpoints et budget restant
Les retries sont une des principales sources de coûts incontrôlés. Un Agent de rapport bloqué sur un outil instable peut renvoyer tout le contexte à chaque échec ; après 1 500 retries, le coût n’a plus grand-chose à voir avec le chemin normal.
Vérifier le budget avant le retry
Vérifiez le budget restant avant de retry, pas après :
def should_retry(error_type, budget_remaining, retry_cost_estimate):
# Classification d'erreur
if error_type in ["403", "400", "tool_not_exist"]:
return False # Erreur non récupérable, pas de retry
# Vérification du budget
if budget_remaining < retry_cost_estimate:
return False # Hors budget, pas de retry
return True # Retry possible
La vérification du budget doit précéder la logique de retry. C’est ce qui évite de brûler le budget quotidien sur une réponse vide retry 1 500 fois.
Idempotence
Un retry ne doit pas répéter un effet de bord, comme envoyer un email ou déclencher une facturation :
- Utilisez un identifiant d’idempotence, par exemple requestId, pour les appels d’outils. Si l’API externe reçoit le même ID, elle doit renvoyer le résultat mis en cache plutôt que retraiter l’opération.
- Écrivez cet ID dans les journaux de coût pour diagnostiquer les appels répétés.
Le principe d’idempotence est simple : la même opération ne doit pas consommer deux fois. Sinon, les retries amplifient à la fois coût et effets de bord.
Sauvegarde d’état (Checkpoint)
Les tâches longues doivent reprendre sans relancer tout le workflow :
- Sauvegardez un checkpoint aux noeuds importants : étapes terminées, état courant, résumé de contexte.
- Après un échec, reprenez depuis le checkpoint au lieu de repartir du début.
- Persistez le checkpoint. Le garder uniquement en mémoire ne suffit pas.
La conception des checkpoints et du thread state est détaillée dans Architecture d’un Agent LangGraph.
Stratégie de retry
Chaque type d’erreur demande une stratégie différente :
| Type d’erreur | Erreur typique | Stratégie de retry | Impact coût |
|---|---|---|---|
| Timeout réseau | Pas de réponse après 10 secondes | Exponential backoff + retry-after, maximum 3 retries | Chaque retry renvoie tout le contexte |
| 503/429 | Service Unavailable, Rate Limit | Attendre la fenêtre de rate limit + retry-after, maximum 3 retries | L’attente ne consomme pas de tokens, le retry oui |
| 403/400 | Permission Denied, Bad Request | Ne pas retry ; injecter l’erreur pour que le modèle décide | Pas de retry, donc pas de coût invalide |
La règle : seules les erreurs récupérables se retry. Ne renvoyez pas indéfiniment une requête invalide au modèle.
Circuit breaker
Après N échecs consécutifs, arrêtez les retries et attendez une intervention :
def circuit_breaker(consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "stop_retry" # Arrêter les retries
return "continue"
La décision du circuit breaker doit être journalisée pour expliquer pourquoi les retries se sont arrêtés. Ensuite, attendez une intervention humaine ou le retour du budget, au lieu de rappeler un outil ou un modèle instable.
Journaux de coût et alertes : quels champs et quels seuils
L’observabilité des coûts est la condition du contrôle des coûts. Si les champs de log sont incomplets, vous ne trouverez pas le problème.
OpenTelemetry trace span attributes
Concevez les journaux de coût sur trois niveaux de spans :
Agent run span (niveau supérieur) :
| Nom du champ | Usage | Type | Pourquoi le tracer |
|---|---|---|---|
runId | Identifier une exécution précise | string | Distinguer plusieurs runs du même workflow |
tenantId | Identifier le tenant | string | Répartir le coût en multi-tenant |
userId | Identifier l’utilisateur | string | Suivre la tendance de coût par utilisateur |
workflowName | Identifier le workflow | string | Suivre les coûts par type de workflow |
totalCost | Coût total estimé | float | Cumuler les coûts en temps réel |
budgetRemaining | Budget restant | float | Base de décision du circuit breaker |
totalRetries | Nombre total de retries | integer | Mesurer l’amplification par retry |
Model call span (span enfant) :
| Nom du champ | Usage | Type | Pourquoi le tracer |
|---|---|---|---|
model | Identifier le modèle | string | Vérifier la pertinence du routage |
pricingVersion | Version de tarification | string | Éviter de coder la formule de coût en dur |
inputTokens | Nombre de tokens d’entrée | integer | Calculer le coût d’entrée |
outputTokens | Nombre de tokens de sortie | integer | Suivre le coût de sortie séparément |
cachedTokens | Tokens touchés par le cache | integer | Calculer l’économie de cache |
costEstimate | Estimation du coût de cet appel | float | Cumuler les coûts en temps réel |
latencyMs | Latence de l’appel | integer | Évaluer si Batch/Flex convient |
Tool call span (span enfant) :
| Nom du champ | Usage | Type | Pourquoi le tracer |
|---|---|---|---|
toolName | Identifier l’outil | string | Identifier l’overhead des appels d’outils |
toolBudget | Plafond de budget outil | float | Base de décision du circuit breaker |
toolTimeout | Timeout de l’outil | integer | Classer les échecs de timeout |
retryCount | Nombre de retries | integer | Mesurer l’amplification par retry |
errorType | Type d’erreur | string | Séparer récupérable et non récupérable |
Ne stockez pas seulement total_cost. Découpez par dimension. Sans ces champs, une anomalie de facture dit seulement « trop cher », pas quel utilisateur, outil ou retry l’a causée.
La conception complète des logs, alertes et reprises d’échec est couverte dans Surveillance et reprise d’un Agent. Cet article ajoute les champs de coût et les objets de budget.
Seuils d’alerte
Définissez les seuils par dimension :
| Dimension d’alerte | Seuil d’alerte | Canal | Action déclenchée |
|---|---|---|---|
| Consommation globale du budget | 70%, 90%, 100% | Slack/email | 70% notifier, 90% dégrader, 100% couper |
| Consommation d’un user/tenant | Plus de 3x la moyenne | Slack/email | Vérifier les appels anormaux |
| Taux d’échec d’un modèle | > 5% | Dashboard | Vérifier routage ou état du service |
| Retries d’un outil | > seuil | Dashboard | Vérifier la stabilité de l’outil |
| Cache hit rate | < valeur attendue | Dashboard | Vérifier la structure du prompt |
Les seuils doivent être dans la logique de coût pour déclencher automatiquement alertes et circuit breakers.
Stratégie de dégradation
Après une alerte, la dégradation peut suivre ces chemins :
| Chemin de dégradation | Méthode | Cas adapté | Impact coût |
|---|---|---|---|
| Dégradation de modèle | Grand modèle -> petit modèle | Taux d’échec élevé sur un modèle | Coût plus bas, qualité possiblement plus basse |
| Dégradation de chemin | Realtime API -> Batch API -> Flex Processing | Budget global consommé trop vite | Plus de latence, coût réduit |
| Dégradation de fonctionnalité | Désactiver les outils non essentiels | Trop de retries sur un outil | Réduit l’overhead des outils |
| Dégradation utilisateur | Rate limit, file d’attente, message « réessayez plus tard » | Consommation anormale d’un utilisateur | Évite qu’un utilisateur brûle le budget |
La dégradation doit vivre dans la logique de budget. Quand une alerte se déclenche, le système doit dégrader automatiquement, pas attendre une intervention manuelle.
Étapes suivantes
Le contrôle des coûts d’un Agent dépend aussi de la surveillance, des appels d’outils et du context engineering :
- Publié : Surveillance et reprise d’un Agent : champs de log, alertes et reprise d’échec. Cet article ajoute les champs de coût et objets de budget.
- Publié : Architecture d’un Agent LangGraph : checkpoints, thread state et reprise pour tâches longues.
- Publié : Tool Calling : bases des appels d’outils. Cet article ajoute per-tool budget, timeout et limites de retry.
- Même série : context engineering : prefix stable, cache hits et séparation entre contexte stable et variables d’exécution.
- Même série : Human-in-the-Loop : attente, refus, timeout de validation, et leur impact sur coûts et retries.
Commencez par une barrière au niveau session : définissez un per-session cost limit et terminez automatiquement la session quand elle dépasse le budget. C’est la première protection la plus rapide contre une tâche qui brûle le budget de la journée. Ensuite, étendez vers les couches de budget, le routage de modèles, les budgets d’outils, Prompt Caching, les circuit breakers de retry et les journaux de coût.
Concevoir un budget de coût et un circuit breaker pour Agent
Utilisez objets de budget, routage de modèles, routage de service, cache, budgets d'outils et circuit breakers de retry pour déplacer le contrôle des coûts avant l'exécution de chaque run.
⏱️ Estimated time: 45 min
- 1
Step 1: Lister tous les chemins de coût
Listez les appels de modèles, appels d'outils, lectures de fichiers, API externes, traitements batch, caches et chemins de retry de l'Agent. - 2
Step 2: Définir l'objet de budget
Tracez le budget par tenant, user, run, workflow, model, tool, retry, cache et time window. - 3
Step 3: Définir la stratégie de routage
Définissez le routage de modèle et le routage de service selon les types de tâches : online, batch, flex et queue. - 4
Step 4: Concevoir les cache hits
Placez le contexte stable dans le prompt prefix, les contenus variables plus loin, et distinguez prompt caching, cache de résultat métier et cache de réponse d'outil. - 5
Step 5: Limiter les outils et retries
Donnez à chaque outil un timeout, max retries, idempotency key, per-tool budget et fallback. - 6
Step 6: Journaliser les spans de coût
Sur les run/span, tracez token, cached token, tool, retry, latency, estimated cost, budget remaining et traceId. - 7
Step 7: Configurer la dégradation et le circuit breaker
Définissez les seuils de dégradation, pause, circuit breaker et alertes, puis testez-les avec de vrais cas d'échec.
FAQ
Faut-il suivre le coût d'un Agent par utilisateur, session, tâche ou outil ?
Le routage de modèles revient-il simplement à utiliser un petit modèle pour les tâches simples ?
Quelle différence entre Prompt Caching et un cache métier classique ?
Combien de fois faut-il réessayer un appel d'outil qui échoue ?
Que faire quand une longue tâche Agent arrive presque au bout de son budget ?
Quels champs un journal de coût doit-il enregistrer ?
16 min de lecture · Publié le: 17 sept. 2026
Guide d'ingénierie AI Agent
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Concevoir un agent Human-in-the-loop : quelles étapes exigent une approbation humaine ?
Guide pratique pour définir les points d'approbation d'un agent IA : actions automatisables, pauses obligatoires, approve/reject/resume, timeouts, compensation et journaux d'audit.
Partie 19 sur 22
Suivant
Modèle d'autorisation pour agent IA : identité utilisateur, droits d'outils, journaux d'audit et isolation des secrets
Avant de connecter un agent IA à de vrais outils, concevez son modèle d'autorisation : identité utilisateur, service account, per-tool permission, scopes, secret vault, rotation des clés, approval policy, data boundary et audit log.
Partie 21 sur 22



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire