Cost Limits pour agents IA (stopper la dépense) + Code

Les limites de tokens ne suffisent pas. Voici comment capper le coût total (LLM + tools), exposer un stop reason propre, et éviter les surprises.
Sur cette page
  1. Le problème (côté prod)
  2. Pourquoi ça casse en prod
  3. 1) Les équipes limitent les tokens et oublient les tools
  4. 2) Coût sans stop reason = pas opérable
  5. 3) Sans caps par tenant, un client peut cramer ton mois
  6. Exemple d’implémentation (code réel)
  7. Incident réel (avec chiffres)
  8. Compromis
  9. Quand NE PAS l’utiliser
  10. Checklist (copier-coller)
  11. Config par défaut sûre (JSON/YAML)
  12. FAQ (3–5)
  13. Pages liées (3–6 liens)
Flux interactif
Scénario:
Étape 1/3: Execution

Action is proposed as structured data (tool + args).

Le problème (côté prod)

Tu regardes l’usage LLM et tu te dis : « ça va ».

Puis tu reçois la facture du vendor navigateur / du scraper / du data provider. Et tu réalises que tu as mis une limitation de vitesse… sur une seule roue.

Les cost limits ne sont pas sexy. Mais c’est la différence entre :

  • « l’agent est parfois cher »
  • et « l’agent est un générateur de coût non borné »

Pourquoi ça casse en prod

1) Les équipes limitent les tokens et oublient les tools

Dans des agents réels :

  • les tokens sont relativement prévisibles
  • les tools sont la partie chaotique (retries, rate limits, travail variable)

Si un tool coûte $0.20 par call, 10 calls = $2. Ça arrive plus vite que ton run ne finisse.

2) Coût sans stop reason = pas opérable

Si tu ne vois qu’un « timeout », personne ne comprend que c’est « trop cher ». Et les utilisateurs relancent. Et ça devient plus cher.

3) Sans caps par tenant, un client peut cramer ton mois

Multi-tenant = un seul client peut ruiner ton graphique si tu ne capes pas.

Exemple d’implémentation (code réel)

Un cost guard simple :

  • tokens + tool costs dans un state
  • check après chaque step / tool call
  • stop reason max_usd
PYTHON
from dataclasses import dataclass, field
import time


TOOL_USD = {
  "browser.run": 0.20,
  "search.read": 0.00,
}


@dataclass(frozen=True)
class CostPolicy:
  max_usd: float = 1.00
  max_seconds: int = 60


@dataclass
class CostState:
  started_at: float = field(default_factory=time.time)
  tokens_in: int = 0
  tokens_out: int = 0
  tool_usd: float = 0.0

  def elapsed_s(self) -> float:
      return time.time() - self.started_at


def estimate_model_usd(tokens_in: int, tokens_out: int) -> float:
  return (tokens_in + tokens_out) * 0.000002


class CostExceeded(RuntimeError):
  def __init__(self, stop_reason: str, *, state: CostState):
      super().__init__(stop_reason)
      self.stop_reason = stop_reason
      self.state = state


class CostGuard:
  def __init__(self, policy: CostPolicy):
      self.policy = policy
      self.state = CostState()

  def total_usd(self) -> float:
      return estimate_model_usd(self.state.tokens_in, self.state.tokens_out) + self.state.tool_usd

  def check(self) -> None:
      if self.total_usd() > self.policy.max_usd:
          raise CostExceeded("max_usd", state=self.state)
      if self.state.elapsed_s() > self.policy.max_seconds:
          raise CostExceeded("max_seconds", state=self.state)

  def on_model(self, *, tokens_in: int, tokens_out: int) -> None:
      self.state.tokens_in += tokens_in
      self.state.tokens_out += tokens_out
      self.check()

  def on_tool(self, *, tool: str) -> None:
      self.state.tool_usd += float(TOOL_USD.get(tool, 0.0))
      self.check()
JAVASCRIPT
const TOOL_USD = { "browser.run": 0.2, "search.read": 0.0 };

export class CostExceeded extends Error {
constructor(stopReason, { state }) {
  super(stopReason);
  this.stopReason = stopReason;
  this.state = state;
}
}

export class CostGuard {
constructor(policy) {
  this.policy = policy;
  this.state = { startedAtMs: Date.now(), tokensIn: 0, tokensOut: 0, toolUsd: 0 };
}

totalUsd() {
  return estimateModelUsd(this.state.tokensIn, this.state.tokensOut) + this.state.toolUsd;
}

check() {
  if (this.totalUsd() > this.policy.maxUsd) throw new CostExceeded("max_usd", { state: this.state });
  if ((Date.now() - this.state.startedAtMs) / 1000 > this.policy.maxSeconds) {
    throw new CostExceeded("max_seconds", { state: this.state });
  }
}

onModel({ tokensIn, tokensOut }) {
  this.state.tokensIn += tokensIn;
  this.state.tokensOut += tokensOut;
  this.check();
}

onTool({ tool }) {
  this.state.toolUsd += Number(TOOL_USD[tool] ?? 0);
  this.check();
}
}

function estimateModelUsd(tokensIn, tokensOut) {
return (tokensIn + tokensOut) * 0.000002;
}

Incident réel (avec chiffres)

On a vu une loop de recherche « simple » qui utilisait des tools navigateur.

Le vendor était instable ce jour-là. Les tools retryent. L’agent retry aussi. Et un seul request utilisateur coûte plus que ta moyenne journalière.

Impact :

  • p95 spend/request monté à $4.80
  • la queue s’est remplie (les runs duraient plus longtemps)
  • support a passé ~2h à trier des tickets « c’est lent »

Fix :

  1. hard cap max_usd + stop reason clair
  2. circuit breaker tool-level quand le vendor devient flaky
  3. caching/dedupe sur les mêmes URLs/queries

Compromis

  • Les caps peuvent couper des runs utiles.
  • Le coût exact par tool call est dur ; l’approx suffit en guardrail.
  • Cost limits sans budgets (steps/time/tool calls) = incomplet.

Quand NE PAS l’utiliser

  • Si tu ne peux vraiment pas estimer le coût, mets au moins max_seconds + max_tool_calls.
  • Pour des tools read-only internes, tu veux quand même capper dès qu’un vendor facture.

Checklist (copier-coller)

  • [ ] cap max_usd par run
  • [ ] stop reason max_usd dans logs + réponse
  • [ ] coûts tools approximés (conservateur)
  • [ ] circuit breaker vendor flaky
  • [ ] caps par tenant / tiers
  • [ ] alert sur spikes de stops max_usd

Config par défaut sûre (JSON/YAML)

YAML
cost_limits:
  max_usd: 1.00
  max_seconds: 60
tool_costs_usd:
  browser.run: 0.20
  search.read: 0.00
stop_reasons:
  log: true
  surface_to_user: true

FAQ (3–5)

Je peux capper seulement les tools ?
Non. Les tokens peuvent aussi exploser (context long, retries). Track les deux.
Comment faire des caps par tenant ?
Avec des tiers. Et log le spend par tenant, sinon tu le découvres en fin de mois.
C’est quoi une bonne cap par défaut ?
Assez basse pour éviter les surprises, assez haute pour les runs normaux. Commence à $1 et ajuste avec les données.
Pourquoi ne pas juste prendre le modèle le moins cher ?
Souvent le modèle n’est pas le plus cher. Les tool calls + retries sont le vrai driver.

Q: Je peux capper seulement les tools ?
A: Non. Les tokens peuvent aussi exploser (context long, retries). Track les deux.

Q: Comment faire des caps par tenant ?
A: Avec des tiers. Et log le spend par tenant, sinon tu le découvres en fin de mois.

Q: C’est quoi une bonne cap par défaut ?
A: Assez basse pour éviter les surprises, assez haute pour les runs normaux. Commence à $1 et ajuste avec les données.

Q: Pourquoi ne pas juste prendre le modèle le moins cher ?
A: Souvent le modèle n’est pas le plus cher. Les tool calls + retries sont le vrai driver.

Pages liées (3–6 liens)

Pas sur que ce soit votre cas ?

Concevez votre agent ->
⏱️ 6 min de lectureMis à jour Mars, 2026Difficulté: ★★★
Implémenter dans OnceOnly
Budgets + permissions you can enforce at the boundary.
Utiliser dans OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
writes:
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true }
Intégré : contrôle en productionOnceOnly
Ajoutez des garde-fous aux agents tool-calling
Livrez ce pattern avec de la gouvernance :
  • Budgets (steps / plafonds de coût)
  • Permissions outils (allowlist / blocklist)
  • Kill switch & arrêt incident
  • Idempotence & déduplication
  • Audit logs & traçabilité
Mention intégrée : OnceOnly est une couche de contrôle pour des systèmes d’agents en prod.
Auteur

Cette documentation est organisée et maintenue par des ingénieurs qui déploient des agents IA en production.

Le contenu est assisté par l’IA, avec une responsabilité éditoriale humaine quant à l’exactitude, la clarté et la pertinence en production.

Les patterns et recommandations s’appuient sur des post-mortems, des modes de défaillance et des incidents opérationnels dans des systèmes déployés, notamment lors du développement et de l’exploitation d’une infrastructure de gouvernance pour les agents chez OnceOnly.