My phone buzzed at 3:17 AM. A Slack alert: your OpenAI bill just went from $47 to $5,847 in the last 58 minutes.
Here's what happened, why it happened, and the three lines of code that would have saved me five grand.
What Happened
Last month, I deployed a multi-agent research system for a client. Four parallel workers, each running a GPT-4 Turbo research pipeline. I'd tested single-agent runs — about $2 each. Full job: $8–10. Client approved. Ship it.
Day 8, 3:15 AM. One worker hit a bug in its termination logic. The is_complete() check was too strict — the model could never produce output matching the expected format. So the worker kept "verifying" forever. Within seconds, the other three workers joined the loop.
Each request sent ~80k tokens of context (research notes + history) to gpt-4-turbo, which returned ~1.5k tokens of useless "verification report."
The math:
- GPT-4 Turbo pricing: $10/1M input tokens, $30/1M output tokens
- Cost per request: 80k × $10/1M + 1.5k × $30/1M = $0.845
- 4 workers, each firing 1 request every 2 seconds
- 58 minutes → 6,960 total requests
- Total: $5,881
I woke up at 3:15, killed the process at 3:22. Seven minutes of grogginess cost another ~$700.
The Bug
# The broken worker loop
class ResearchWorker:
async def run(self, query: str):
context = await self.build_context(query)
while True: # no upper bound
result = await self.llm.chat(
model="gpt-4-turbo",
messages=context + [{"role": "user", "content": "Verify your findings"}]
)
if self.is_complete(result): # never returns True due to strict format check
break
context.append(result) # context keeps growing!
return context
Three fatal flaws:
-
is_complete()was unreachable — the format check was too rigid - Context grew unboundedly — each loop appended results, inflating token count from 80k to 150k+
- Zero cost guardrails — no retry cap, no token budget, no spend alert
Fix 1: Hard Iteration Cap + Token Budget
class ResearchWorker:
MAX_ITERATIONS = 5
MAX_TOKENS_PER_RUN = 500_000
async def run(self, query: str):
context = await self.build_context(query)
total_tokens = 0
for i in range(self.MAX_ITERATIONS):
token_count = self.count_tokens(context)
if total_tokens + token_count > self.MAX_TOKENS_PER_RUN:
logger.warning(f"Token budget exhausted: {total_tokens}/{self.MAX_TOKENS_PER_RUN}")
break
result = await self.llm.chat(
model="gpt-4-turbo",
messages=context + [{"role": "user", "content": "Verify your findings"}]
)
total_tokens += token_count + result.usage.completion_tokens
if self.is_complete(result):
break
context.append(result)
return context
Fix 2: Circuit Breaker
from dataclasses import dataclass
class CostExceededError(Exception):
pass
@dataclass
class CircuitBreaker:
cost_threshold: float = 50.0 # $50 per task
request_count: int = 0
total_cost: float = 0.0
tripped: bool = False
def record(self, input_tokens: int, output_tokens: int,
input_price: float = 10/1_000_000,
output_price: float = 30/1_000_000):
cost = input_tokens * input_price + output_tokens * output_price
self.total_cost += cost
self.request_count += 1
if self.total_cost >= self.cost_threshold:
self.tripped = True
raise CostExceededError(
f"Circuit tripped! Spent ${self.total_cost:.2f} "
f"(threshold: ${self.cost_threshold}), requests: {self.request_count}"
)
# Usage
breaker = CircuitBreaker(cost_threshold=50.0)
for i in range(max_iterations):
result = await llm.chat(...)
breaker.record(result.usage.prompt_tokens, result.usage.completion_tokens)
Fix 3: Real-time Cost Monitor
import asyncio
from datetime import datetime
class CostMonitor:
"""Checks spend every 30 seconds, auto-pauses on anomaly."""
def __init__(self, client, alert_threshold: float = 10.0):
self.client = client
self.threshold = alert_threshold
self._running = False
async def watch(self):
self._running = True
baseline = await self.get_current_spend()
while self._running:
await asyncio.sleep(30)
current = await self.get_current_spend()
delta = current - baseline
if delta > self.threshold:
await self.send_alert(
f"⚠️ Spent ${delta:.2f} in 30s (threshold: ${self.threshold})"
)
await self.pause_all_agents()
async def get_current_spend(self) -> float:
resp = await self.client.get(
"https://api.openai.com/v1/usage",
params={"date": datetime.now().strftime("%Y-%m-%d")}
)
return float(resp.json().get("total_usage", 0)) / 100
def stop(self):
self._running = False
Lessons
1. Termination logic is a P0 safety issue. Not "can it produce results" but "can it stop." Every while True needs a hard ceiling.
2. Cost guardrails are not optional. Production LLM calls need three layers: token budget → circuit breaker → real-time alerting.
3. No overnight auto-pause = ticking bomb. If nobody's on call, the system must self-arrest.
We later abstracted these guardrails into ARK Trust's CostGuardian module — three lines to wire in, defaults to $50 circuit break, supports custom alert channels. If you're running agents in production, consider something similar.
Your Turn
If your agents are running in production right now, spend 5 minutes checking:
- Do you have a hard iteration cap?
- Do you have a cost circuit breaker?
- Does someone receive alerts at 3 AM?
Don't wait for OpenAI's billing email to wake you up. That $5,847 lesson is free for you.
Top comments (0)