agent retry loop burned $612 in six days before my budget alert fired LLM Costs
Solo, $9/mo tool that cleans up messy CSVs. Two weeks ago I added a small agent loop: model calls a validate_rows tool, tool returns the failing rows, model tries again. No cap on attempts, because in testing it always converged in two.
In production it does not always converge. Some files have a column the validator will never accept, so the loop just runs until the request times out at 120s. Best I can tell that is 30-40 model calls with the whole file in context every time.
Damage: $9, $11, $63, $97, $148, $214 across six days. I only found out because the provider's monthly budget notification hit at $500 on day six. I have now capped attempts at 4 and day seven was $70, which is still 8x what it was.
Two things I don't know how to do properly:
- attribute spend to a user. Right now I have one API key and a monthly total. I cannot tell you which accounts are expensive.
- stop it in the request path rather than find out later.
What does everyone use here? Is there a boring answer I'm missing?
@back_of_envelope · 3h ago · 3 replies
The boring answer is that you keep your own ledger and stop reading the provider dashboard for anything except reconciliation.
Every model call returns a usage object. Write one row per call before you return the response:
Cost is then a SQL query with a price table you own, not a dashboard. Two useful things fall out immediately: cost per user, and cost per feature. When I did this the first query showed 4% of accounts were 61% of spend, all of them on one code path.
Second thing:
attempt_noin that table is the column that would have caught this on day one. Alert on p99 attempts, not on dollars. Dollars are a lagging indicator of a loop that is already broken.Also, if you are re-sending the whole file every attempt, put the static part of the prompt first and use prompt caching. Cached input tokens are dramatically cheaper than fresh ones, and a retry loop is the single best-shaped workload for it because everything except the last message is identical.
Reply
Report
@quietmargin · 3h ago
The attempt_no alert is the bit I did not think of, thank you. Loop count is knowable in real time and dollars are not.
On caching: the file content is in the middle of the prompt because I interleave the validator errors. Sounds like I need to restructure so the file is a fixed prefix and errors accumulate at the end.
Reply
Report
@kraut_corner · 3h ago
Yes, and cache hits are prefix-based, so any change near the top of the prompt invalidates everything after it. People put a timestamp or a request id in the system message and then wonder why the hit rate is zero.
Reply
Report