Dangerously Looping AI Agents
These are my notes from designing an autonomous research agent. The idea was very simple: an agentic loop whose main task is to research given topics and dump the findings into markdown files I can read later.
I designed it against Claude Code, so some of the specifics below are Claude Code specifics. But the failure modes aren't. If you're building an agent loop on any provider, you'll run into most of these.
Why would you want to loop agents at all?
You've got a subscription with more capacity than you actually use. A loop spends those leftover credits on something useful. Pick a topic, research it, write it up, move to the next one. Start it when you want, stop it when you want and read the output whenever.
It's a decent idea and most of it is a weekend of work. But there were two assumptions buried in my version of it that turned out to be wrong, and one feature I wanted that just isn't possible in the shape I wanted it.
Where the compute comes from:
There are two main ways to get model access, and they behave very differently once you automate.
API access is pay per token. You request a key, get billed for what you use, and every response comes back with rate limit headers that tell you exactly how much headroom is left. These headers are useful to reason with.
Whereas, subscription access (Pro, Max, Team seats, Enterprise seats) is a flat monthly fee with usage caps instead of a bill. The agent tooling that comes with a subscription can be driven programmatically, so you can build a loop without ever touching an API key.
These are two separate accounting systems. A subscription doesn't meter you in dollars like an API service does, it meters you in an opaque quota that the provider deliberately doesn't publish exact numbers for. This is what makes self-monitoring in subscription-based models harder.
Quota vs Burn Rate
There are usually two limits stacked on top of each other.
The first is a rolling session window, commonly around five hours. It does reset, but it starts counting from your first message rather than on a fixed clock. A loop that runs continuously keeps a window open more or less permanently.
The second is a weekly cap sitting on top of that. This is what you risk maxing out. It resets at a fixed time assigned to your account.
Now, it is worth checking what kind of plan you're on too. Some enterprise arrangements are consumption based with no per seat cap at all, and usage just gets billed at API rates. On those plans there's no wasted headroom to reclaim in the first place, so you're paying for every request.
And if the seat belongs to your employer, usage is visible to admins through analytics and compliance tooling. That's not a reason not to build it, but an agentic loop demands transparency with the owner of the account.
Can Agents monitor their usage?
The most important feature in such an arrangement.
Agent CLIs generally don't expose remaining quota to the agent that's running. The rate limit headers are there on the wire, but the tooling does not expose them. They don't reach hooks, reach status line scripts, or the SDK surface. The reliable way to confirm a feature doesn't exist, by the way, is to go look for open issues on the tool's tracker asking for it, and I found several.
And even if those headers were exposed, they'd be describing API rate limits. They wouldn't tell you how much of your weekly subscription allowance is left.
What you can get instead
A cost estimate per run. Structured output modes usually report an estimated cost for each invocation. It's calculated client side and it won't match your bill exactly, but it goes up and never down, which is all a budget governor fundamentally needs.
For now, a reactive signal is the best work-around. Streaming output emits retry events with an error category attached, things like rate_limit or overloaded or billing_error, along with a retry delay. Consider this your hard stop.
You can calibrate these limits manually. Run three or four tasks, then take a look at your usage dashboard. Now you know roughly what one task costs as a slice of your weekly bar. There's no way around doing this at least once.
The fix:
Do not ask the agent to decide whether to continue. Put the budget in the orchestrator instead.
The loop that spawns the agent holds the ledger and decides whether there's another iteration. In this model, the agent doesn't need self awareness, it needs a supervisor.
If you still want the agent to scope its own depth, interpolate the number into the prompt:
You have roughly 12% of this cycle's budget left. Keep this to a shallow pass.
That's a string format call. It costs nothing, and it's a lot more reliable than asking a model to reason about a number it can't see.
This generalises past quota. Anything the agent can't directly observe should be computed by the harness and handed over as text. Models are bad at introspection and pretty good at following instructions about facts you give them.
Dangerously Looping AI Agents can cause:
Context compounding
This is the arguably the biggest cost driver, and it isn't iteration count.
If you keep one long session alive and resume it every turn, each turn re-sends the whole accumulated context history. Cost per turn climbs faster than linearly. A loop would blow the context-window out of proportion really fast.
The naive fix is to start one fresh session per task. Pass the state through files instead of conversation. The agent reads a ledger and any prior output at the start of each run rather than remembering anything. Another benefit is that every run becomes reproducible and debuggable on its own.
Rabbit holing
An agent with no termination criterion will chase tangents forever. What it does could be interesting to the agent but not necessarily to you.
Give the agents a hard turn ceiling as a backstop, and an explicit definition of done in the prompt. Something like "Produce 800 to 1200 words covering these four sub questions, then stop" works. "Research this topic thoroughly" doesn't.
Duplicate work
With no memory of what it already covered (as a result of compacting context-windows, or maintaining a sparse ledger), the loop will research the same topic again in slightly different words and you end up with near identical files.
Quality Degradation
An unsupervised research agent produces confident, nicely formatted markdown containing citations that don't necessarily resolve, or that resolve to a page saying something different. The agent makes it looks exactly like the good output, however. That's what makes it a problem, because you're reading it later, out of context, with no memory of what the agent actually did to produce it.
To fix this, make the agents require a URL on every substantive claim, and think about a cheap agent in parallel whose only job is to check that the cited URLs exist and say what the first pass claimed they said. Verification is much cheaper than generation.
Blast radius
Agent CLIs tend to start in a mode where they ask permission for everything, which deadlocks instantly in an unattended loop. The obvious move is to turn permissions off entirely.
Though tempting, be wary of this move. Use a restrictive mode with an explicit allowlist of tools, scoped to one output directory. Skipping permissions wholesale is only reasonable inside a container with nothing valuable mounted in it.
Parallelism multiplies burn
"A bunch of researchers working in parallel" all pull from the same quota. Two agents in parallel halve your runway, and five cut it to a fifth. When the constraint is quota rather than latency, parallelism buys you wall clock time and nothing else.
So start in serial. Only add concurrency after calibration tells you what you can afford.
Rough architecture
You don't need a framework for this. A graph library or an agent framework gives you machinery a serial loop has no use for.
topics.txt queue of things to research
ledger.md append only: topic, timestamp, output path
budget.json running cost estimate for the cycle
STOP sentinel file, presence ends the loop
output/YYYY-MM-DD/ one markdown file per finished topic
The loop itself:
- Check for STOP, exit if it's there
- Check budget.json against your floor, exit if you're below it
- Pop a topic off the queue
- Spawn a fresh agent session with structured output, a narrow tool allowlist, a turn ceiling, and the remaining budget figure interpolated into the prompt
- Parse the result. Write the markdown, append to the ledger, add the cost estimate to the budget file
- Go again
Every bit of state is a file on disk. Nothing lives inside the agent between iterations. That's the property that makes this debuggable at 2am when the output starts looking weird.
Remember:
Subscription quota and API rate limits are separate accounting systems, and tooling built for one won't give you visibility into the other.
Agents can't introspect their own resource consumption. Whatever the agent can't observe, compute it in the harness and hand it over as text.
Context accumulation is what makes loops expensive, not the number of iterations. Fresh sessions plus file based state should be your default rather than an optimisation you get to later.
Every loop needs three exits: a budget floor, a turn ceiling, and a manual stop. Missing any one of them can result in the loop running away from you.
Unsupervised output needs verification built into the pipeline, because there's nobody reading it critically at the moment it gets produced.
Keep state on disk instead of in the agent. Files are debuggable, resumable, and they survive the process dying.