Budget explosion (quand un agent brûle de l’argent) + fixes + code

  • Repère la panne tôt, avant que la facture grimpe.
  • Comprends ce qui casse en prod, et pourquoi.
  • Copie des garde-fous : budgets, stop reasons, validation.
  • Sache quand ce n’est pas la vraie cause.
Signaux de détection
  • Tool calls/run explosent (ou se répètent avec args hash).
  • Spend/tokens montent sans amélioration des outputs.
  • Retries passent de rares à constants (429/5xx).
Les budgets ne cassent pas d’un coup. Ils fuient via retries, prompt bloat et tool spam. Voilà comment ça explose en prod et comment capper le spend par run.
Sur cette page
  1. Le problème (côté prod)
  2. Pourquoi ça casse en prod
  3. 1) Les tokens scalent avec le contexte, pas avec l’intention
  4. 2) Les retries multiplient coût + latence
  5. 3) Planning = overhead
  6. 4) Tool spam rend le budget tokens inutile
  7. 5) Sans logs, tu ne sais pas ce que tu dépenses
  8. Exemple d’implémentation (code réel)
  9. Incident réel (avec chiffres)
  10. Compromis
  11. Quand NE PAS l’utiliser
  12. Checklist (copier-coller)
  13. Config par défaut sûre (JSON/YAML)
  14. FAQ (3–5)
  15. Pages liées (3–6 liens)
Flux interactif
Scénario:
Étape 1/2: Execution

Normal path: execute → tool → observe.

Le problème (côté prod)

Tu shipes un agent.

En test, ça coûte “quelques centimes”.

Puis ça passe en prod et quelqu’un demande :

“Pourquoi on a dépensé $900 hier ?”

Les explosions de budget sont rarement un gros bug. C’est l’accumulation :

  • tokens qui dérivent
  • retries qui multiplient
  • tools appelés en boucle
  • prompts gonflés “juste un peu”

Si tu ne mesures pas, tu apprends via finance. Finance n’est pas un système d’alerting.

Pourquoi ça casse en prod

1) Les tokens scalent avec le contexte, pas avec l’intention

Intention : “résume ça”. Réalité : “colle 40 messages + 6 tool outputs + 2 runbooks”.

2) Les retries multiplient coût + latence

Modèle retry → tu payes deux fois. Tool retry → tu payes tool + plus de tokens pour “expliquer l’erreur”.

3) Planning = overhead

Planifier avant d’agir peut être utile… ou juste coûteux.

4) Tool spam rend le budget tokens inutile

Tu peux avoir $0.01 de tokens et $5 de tools.

5) Sans logs, tu ne sais pas ce que tu dépenses

Tu veux des logs sur tokens, tool calls, estimations de coût, stop reasons.

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

Budget guard minimal :

  • time/steps/tool calls
  • estimation de coût
  • stop reason exploitable
PYTHON
from dataclasses import dataclass
import time


@dataclass(frozen=True)
class Budget:
  max_steps: int = 25
  max_seconds: int = 60
  max_tool_calls: int = 12
  max_usd: float = 1.00


@dataclass
class Usage:
  tool_calls: int = 0
  model_tokens_in: int = 0
  model_tokens_out: int = 0
  estimated_usd: float = 0.0


class BudgetExceeded(RuntimeError):
  pass


def estimate_usd(tokens_in: int, tokens_out: int) -> float:
  return (tokens_in + tokens_out) * 0.000002  # placeholder


class BudgetGuard:
  def __init__(self, budget: Budget) -> None:
      self.budget = budget
      self.usage = Usage()
      self.started = time.time()
      self.steps = 0

  def on_step(self) -> None:
      self.steps += 1
      if self.steps > self.budget.max_steps:
          raise BudgetExceeded("step budget exceeded")
      if time.time() - self.started > self.budget.max_seconds:
          raise BudgetExceeded("time budget exceeded")
      if self.usage.tool_calls > self.budget.max_tool_calls:
          raise BudgetExceeded("tool budget exceeded")
      if self.usage.estimated_usd > self.budget.max_usd:
          raise BudgetExceeded("cost budget exceeded")

  def on_tool_call(self) -> None:
      self.usage.tool_calls += 1

  def on_model_call(self, *, tokens_in: int, tokens_out: int) -> None:
      self.usage.model_tokens_in += tokens_in
      self.usage.model_tokens_out += tokens_out
      self.usage.estimated_usd = estimate_usd(
          self.usage.model_tokens_in, self.usage.model_tokens_out
      )
JAVASCRIPT
export class BudgetExceeded extends Error {}

export class BudgetGuard {
constructor(budget) {
  this.budget = budget;
  this.started = Date.now();
  this.steps = 0;
  this.usage = { toolCalls: 0, tokensIn: 0, tokensOut: 0, estimatedUsd: 0 };
}

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

onStep() {
  this.steps += 1;
  const elapsedS = (Date.now() - this.started) / 1000;
  if (this.steps > this.budget.maxSteps) throw new BudgetExceeded("step budget exceeded");
  if (elapsedS > this.budget.maxSeconds) throw new BudgetExceeded("time budget exceeded");
  if (this.usage.toolCalls > this.budget.maxToolCalls) throw new BudgetExceeded("tool budget exceeded");
  if (this.usage.estimatedUsd > this.budget.maxUsd) throw new BudgetExceeded("cost budget exceeded");
}

onToolCall() {
  this.usage.toolCalls += 1;
}

onModelCall({ tokensIn, tokensOut }) {
  this.usage.tokensIn += tokensIn;
  this.usage.tokensOut += tokensOut;
  this.usage.estimatedUsd = this.estimateUsd(this.usage.tokensIn, this.usage.tokensOut);
}
}

Les budgets se checkent en continu. Tu veux arrêter avant la falaise.

Incident réel (avec chiffres)

On avait un agent à ~3k tokens/request en dev.

On a ajouté du “contexte utile” :

  • 20 derniers messages
  • outputs bruts (HTML)
  • extrait de runbook

Personne n’a surveillé.

Impact (48h) :

  • tokens/request : 3k → 16k
  • latence p95 : 2.4s → 8.9s
  • spend : +$740

Fix :

  1. budgets (tokens/tools/temps/spend)
  2. prompt builder cap + summarization
  3. alerting sur tokens/request + spend/run
  4. safe-mode quand budgets hit

Ce n’était pas “le modèle”. C’était nous.

Compromis

  • Budgets serrés = plus de “stopped early”.
  • Estimation coût approximative = OK.
  • Summaries perdent de la nuance.

Quand NE PAS l’utiliser

  • Si tu ne peux pas estimer le coût, commence par budgets temps/tools.
  • Si c’est déterministe, workflow.
  • Si tu as besoin de long contexte, assume un budget plus haut et rends-le explicite.

Checklist (copier-coller)

  • [ ] Budgets: steps, tool calls, seconds, USD
  • [ ] Tokens in/out par run
  • [ ] Estimation spend + alerting
  • [ ] Caps sur retries
  • [ ] Caps sur dumps HTML/logs
  • [ ] Summarize quand over-budget
  • [ ] Stop reason explicite (pas de timeouts silencieux)

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

YAML
budgets:
  max_steps: 25
  max_seconds: 60
  max_tool_calls: 12
  max_usd: 1.0
llm:
  retries: { max_attempts: 2 }
context:
  max_prompt_tokens: 2500
  summarize_when_over_budget: true

FAQ (3–5)

J’ai besoin d’un coût exact pour un budget ?
Non. Un guard peut être approximatif. Le but est d’éviter les runs runaway.
Je commence par quel budget ?
Temps + tool calls, puis tokens/spend quand tu sais mesurer.
Et les requêtes qui demandent plus ?
Escalation : confirmation, tier de budget plus haut, ou async avec status.
Je mets un gros budget et basta ?
Tu peux. Tu reviendras apprendre via finance et on-call.

Q : J’ai besoin d’un coût exact pour un budget ?
R : Non. Un guard peut être approximatif. Le but est d’éviter les runs runaway.

Q : Je commence par quel budget ?
R : Temps + tool calls, puis tokens/spend quand tu sais mesurer.

Q : Et les requêtes qui demandent plus ?
R : Escalation : confirmation, tier de budget plus haut, ou async avec status.

Q : Je mets un gros budget et basta ?
R : Tu peux. Tu reviendras apprendre via finance et on-call.

Pages liées (3–6 liens)

Pas sur que ce soit votre cas ?

Concevez votre agent ->
⏱️ 5 min de lectureMis à jour Mars, 2026Difficulté: ★★☆
Implémenter dans OnceOnly
Guardrails for loops, retries, and spend escalation.
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
controls:
  loop_detection:
    enabled: true
    dedupe_by: [tool, args_hash]
  retries:
    max: 2
    backoff_ms: [200, 800]
stop_reasons:
  enabled: true
logging:
  tool_calls: { enabled: true, store_args: false, store_args_hash: 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)
  • Kill switch & arrêt incident
  • Audit logs & traçabilité
  • Idempotence & déduplication
  • Permissions outils (allowlist / blocklist)
Mention intégrée : OnceOnly est une couche de contrôle pour des systèmes d’agents en prod.
Exemple de policy (concept)
# Example (Python — conceptual)
policy = {
  "budgets": {"steps": 20, "seconds": 60, "usd": 1.0},
  "controls": {"kill_switch": True, "audit": True},
}
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.