The Rate Is the Tell - A Kill Switch That Doesn't Ask Nicely
The Rate Is the Tell - A Kill Switch That Doesn't Ask Nicely
The problem
I built a research agent that loops between an LLM call and a search tool, re-querying and re-summarizing a topic until it decides on its own that it's done. Into twenty of a hundred synthetic sessions, I seeded a runaway condition: the search tool returns near-duplicate results, which keeps the agent's own stopping heuristic from ever triggering, so it just keeps going. The only safeguard in this version was a five-dollar total-spend budget cap, checked once per loop iteration. Once a session crossed that cap, the system's response was to send the agent a message asking it to stop.
Total spend is a lagging indicator by construction. The check only fires between iterations, not mid-call, so a single expensive call can already blow past the cap before anyone notices. Worse, asking a runaway agent to stop routes the instruction through the same reasoning loop that is already misbehaving, a bit like asking a fire to put itself out. Across the twenty seeded runaway sessions, the average session ran three dollars or more past the cap before it actually stopped, and at least two of the twenty never stopped at all inside a ten-minute timeout. The agent was stuck in a loop that the stop message had no privileged access to interrupt, not ignoring the instruction out of any kind of malice.
The pattern
The fix throws out total spend as the signal entirely and replaces it with rate: how fast the agent is burning tokens right now, watched by a process that has nothing to do with the agent's own reasoning loop. An out-of-process supervisor polls a shared token-rate counter once a second. When the rate sustains above a threshold, three times the normal session's observed rate, held for ten seconds, the supervisor skips the message entirely and revokes the session's API credential at the transport layer. The agent's next LLM call fails with an authorization error no matter what it intends to do next, because the enforcement lives in a completely different process from the one being stopped.
Two things make this work where the budget cap failed. Rate spikes long before total spend crosses any fixed number, so the supervisor catches the problem sooner. And revocation doesn't depend on the agent noticing anything or choosing to comply, because the agent is never the one enforcing its own shutdown. Running the same hundred sessions through this version, every one of the twenty seeded runaways stopped within two seconds of crossing the rate threshold, and average overshoot dropped from three dollars or more down to under fifty cents.
Design considerations
A rate threshold is a heuristic tuned to the specific shape of runaway I built. Three times normal, sustained for ten seconds, catches a fast, obvious spike cleanly. It has nothing useful to say about a slower runaway that stays just under three times normal while quietly burning money for an hour, and it says nothing at all about a failure that isn't rate-visible in the first place: a single call that is simply very expensive on its own, with no loop of cheap calls behind it. Rate and total spend are two different lagging indicators. Neither one is a strict improvement over the other for every shape a runaway can take, and I built this pattern to fit the shape I seeded, not as a universal answer to every way an agent can overspend.
The threshold itself needs real calibration against a system's actual normal-usage variance, not a number picked because it sounded reasonable. A threshold set too tight will revoke credentials on legitimately complex sessions that happen to burn tokens fast for good reasons, which trades a runaway-cost problem for a false-positive problem that stops honest work partway through. A threshold set too loose just recreates the total-spend cap's slow-to-react failure mode with extra steps in between. I'd rather tune this against real observed session variance over the first few weeks of running it than trust a number chosen before a single real session ran through it. A credential revoked mid-task also needs a recovery path of its own. A session that gets cut off legitimately, not from a runaway but from a spike in a hard problem, still has partial work sitting in whatever state it was in when the key stopped working, and someone has to decide whether that partial work gets resumed under a fresh credential or thrown away entirely. The kill switch answers the stopping question cleanly, and leaves the question of what happens to the work in flight when the stop lands completely open.
I want to name a separate, related failure this pattern does not solve, so nobody assumes it's covered. A retry storm, many independently backing-off callers hitting the same rate limit and clearing their backoff windows at the same moment, is an orchestration-layer problem across many callers, not a single agent's loop. The fixes for that, circuit breakers, jittered exponential backoff, backpressure, are old distributed-systems tools reapplied to agent orchestration, and they solve a genuinely different problem than the one this pattern targets. This pattern stops one agent that will not stop itself, and that's the limit of its job description. What happens when many independently behaving agents all hit the same wall at the same moment is a question this mechanism was never built to answer.