Skip to content

Agent burns through its budget in a single run

A daily LLM API limit catches “we overspent over 24 hours” — but a single autonomous agent can burn $40 in one run if it goes into an expensive loop: a long context, repeated tool calls, retry after retry. By morning the daily alert is already useless — the money is spent.

You need a guard inside a single run: it counts cumulative cost as it goes and reacts before the bill grows several-fold. This is not about the daily spend, but specifically about runaway within one run.

We wrap every model call: convert tokens to dollars, accumulate the total, and at two thresholds (warn / hard) send a push. On hard — raise an exception and stop the agent.

import os, requests
# prices per 1M tokens (example — substitute your own for your model)
PRICE_IN = 3.00 / 1_000_000
PRICE_OUT = 15.00 / 1_000_000
class BudgetGuard:
def __init__(self, run_id, warn_usd=5.0, hard_usd=15.0):
self.run_id = run_id
self.warn = warn_usd
self.hard = hard_usd
self.spent = 0.0
self._warned = False
def add(self, usage):
# usage — the model response object with input_tokens / output_tokens
self.spent += usage.input_tokens * PRICE_IN
self.spent += usage.output_tokens * PRICE_OUT
if self.spent >= self.hard:
notify("💸🛑 Агент пробил hard-лимит",
f"run {self.run_id}: потрачено ${self.spent:.2f} "
f"(потолок ${self.hard:.2f}). Останавливаю run.",
priority=10)
raise RuntimeError(f"budget hard limit: ${self.spent:.2f}")
if self.spent >= self.warn and not self._warned:
self._warned = True
notify("💸⚠️ Агент близок к лимиту бюджета",
f"run {self.run_id}: уже ${self.spent:.2f} "
f"из ${self.hard:.2f}. Присматривайте.",
priority=6)
def snapshot(self):
return round(self.spent, 4)
def notify(title, message, priority):
requests.post(f"{os.environ['NOTIFLY_URL']}/message",
params={"token": os.environ["NOTIFLY_TOKEN"]},
json={"title": title, "message": message, "priority": priority},
timeout=5)

Embedding it in the agent loop:

guard = BudgetGuard(run_id="refactor-auth-2026-07-01")
def call_model(messages):
resp = client.messages.create(model="claude-sonnet-4-5",
max_tokens=2000, messages=messages)
guard.add(resp.usage) # crosses the limit -> raises an exception
return resp
# ... the agent runs its loop; on the hard limit the run fails with an alert

The key part: warn sends a push once (the _warned flag) so it doesn’t spam on every iteration. hard stops the run — a failed run beats a $40 bill.

An absolute ceiling catches a long run, but not a sudden spike. Track $/minute — if the agent suddenly starts burning three times faster than usual, that’s an early loop signal:

import time
class BurnRateGuard:
def __init__(self, run_id, max_usd_per_min=2.0):
self.run_id = run_id
self.max = max_usd_per_min
self.t0 = time.time()
self.spent = 0.0
self._fired = False
def add(self, delta_usd):
self.spent += delta_usd
mins = max((time.time() - self.t0) / 60, 0.1)
rate = self.spent / mins
if rate > self.max and not self._fired:
self._fired = True
notify("🔥 Скорость сжигания бюджета выросла",
f"run {self.run_id}: ${rate:.2f}/мин "
f"(норма ${self.max:.2f}). Возможно, петля.",
priority=8)

If the agent is a black box (a third-party CLI, someone else’s SDK) and you can’t wrap the model calls from the inside, catch the threshold from the outside. OpenAI/Anthropic have usage webhooks and budget alerts — route them into the Webhook Router and it turns the event into a push. For providers without webhooks, use the Email Inbox: a budget alert arrives as an email → Notifly sends a push.

The precision is lower (billing lag), but it works for any agent without touching its code.

  • run_id — which specific run, so you can find it in the logs;
  • spent / ceiling / burn rate $/min;
  • the top-1 “expensive” tool or the longest request in the run;
  • a hint about the cause: retry loop, context ballooned to 180k tokens.