The Agent That Polled 55 Times to Learn Nothing

2026-11-14来源 HackerNoonEnglish

When one agent waits on another, something has to tell the waiting side that the work is done. In normal backend code, that's a solved thing; nobody writes a loop that sleeps for four minutes and then checks a table. With coding agents it comes back again, because the agent decides how to wait by itself, and for a model with a shell the easiest option is just to sleep and look again. And it works; it's easy to read in a transcript too. The catch is the cost, and you only see it once you actually count the turns.

I found this in my own multi-agent delivery pipeline, in one of the branches I was exploring: a deeper hierarchy of agents. I didn't take that branch much further and went back to a flatter structure, but this case is what made me adapt the logic and the harness around it. Below you will find how it looked in the logs, what it cost, and what changed after.

The Setup: an orchestrator, a Lead, and the teams under it

The pipeline runs on a production codebase, and in that branch it had three layers. There's the orchestrator, the main session I talk to, which spawns tasks into a Lead; the Lead owns the ticket board, routes the work across teams of worker agents and then waits for them to finish; and the workers write the code and prove it with tests, because a ticket can't close until the proof passes. The board is just files on disk, so for the Lead, checking it again is always one shell command away.

The catch with a hierarchy like that is depth. I talk to the orchestrator, the orchestrator talks to the Lead, and whatever the Lead does between those messages happens a level below anything I look at by default. So when something goes wrong there, it stays hidden until I go and look at that layer on purpose, and the Lead turned out to be exactly the weak link.

The Signs: Slow runs and a bill about twice too big.

The first sign was time. Runs were taking a lot longer than usual to work a problem out, and at the same time the cost per run kept climbing; features were coming in at roughly twice the tokens I'd budgeted for one.

Then the Lead stopped reacting to me. For about half a day it ignored my corrections and pokes and took no action on them; it just stayed busy with whatever it had on hand, and that on-hand task had washed my messages out of its context. The last straw was a translation fix, four keys, that went through three escalations and still didn't land. The only way to see what the Lead was actually doing was the file logs, so I went in to find what's wrong and patch the gap.

Digging: Logs, then tool calls, then the sleeps.

I started with the logs, grepping through them to understand what's going on, and one thing kept dominating: sleeps. A sleep on its own is fine; agents wait for builds and tests all the time, but there were a lot of them and no visible reason for most. So I went into the tool calls around those sleeps, and the pattern was always the same: a sleep of a few minutes, then a grep of the board or a status check, then another sleep.

To put proper terms on it, a wasted sleep was any sleep longer than 100 seconds followed by greps or status checks on something, and nothing else. The scan is short enough to show:

import re SLEEPNUM = re.compile(r'(?:^|[;&|\s])sleep\s+(\d+)') POLLPAY = re.compile(r'board\.py list|ls flow/board|grep -c "T-"|playwright|health', re.I) for call in tool_calls: m = SLEEPNUM.search(call.cmd or '') if m and int(m.group(1)) >= 100 and POLLPAY.search(call.cmd): polls.append(call) 

Up to that point, the Lead had made 303 tool calls, and 55 of them were wasted sleeps, almost 20%.

The reason was simple once it was visible. The Lead had handed work out and had no other way to find out it was done, so it slept and looked, and every look pulled whatever half-finished state was on the board into its context. The sleeps were long too, with a median of 235 seconds, and some of them cut out midway and kicked off another scan and another sleep.

And it wasn't only the sleeps: some of the checks were piped through | tail, which prints nothing until the command in front of it finishes, so the Lead sat blocked on those as well, and now and then a wait ran as long as 600 seconds. Added up, the Lead spent 3.66 hours of a 19.3-hour stretch in sleeps alone, and while it was blocked like that it couldn't take a correction, a fix or a ping from me, which is exactly what I'd been seeing from the outside.

The Tokens: A dead lead on caching, and a pile of calls

So the sleeps explained the slowness and the silence. The next question was whether they also explained the bill, or whether it was only lost time, so I went to count how many tokens those wake-ups had actually used.

My first suspicion was the cache. Every turn sends the Lead's whole context to the model again, a couple of hundred thousand tokens by then, and prompt caching keeps that cheap only while the cache is warm; on Anthropic's pricing, a cache read is 0.1x the normal input price on most models, a write is 1.25x, and the cache goes cold after five minutes. The sleeps sat just under that, so I kinda expected the cadence to drift over the edge and every wake-up to pay for writing the context back in, and the occasional 600-second wait definitely went over.

That was mostly a dead lead. On average, a turn after a sleep read about 231k tokens from the cache and wrote only roughly 13k into it, so for most wake-ups the session stayed warm and got the cheap rate. The tokens came from the number of calls instead. Counting everything the model processed on those turns, cached re-reads included, the 55 turns right after a wasted sleep came to about 13.5M tokens, over 25% of everything the Lead had processed up to that point. Most of that is the same context read from the cache again and again, cheap each time, but it adds up.

If it had just waited for the work to finish and checked about 5 times, at the same ~245k tokens a turn, it would have spent roughly 1.2M for the same logic, instead of 13.5M.

The Fix: A flatter structure and sleeps that need a reason.

The first half of the fix was structural. I took the orchestrator layer out and kept a flatter shape, where I work with the Lead directly and can see what it's doing without digging a level down. That's an opinionated way of working; I prefer a closer, hands-on look and a more controlled environment over a fully automated build-up, so take it as my style rather than a recommendation for everyone.

The second half was tool usage. A sleep now needs a reason stated by the agent, and the alternative for the Lead is a cron-style reminder to check in on the team at a set time; both are documented, and the Lead's trace gets checked against them. Implementer agents still wait, but only on things that end by themselves, a test run finishing or a script reporting its status.

Small fixes got a fast lane in the same change, so a four-key translation fix doesn't go through the same ceremony as a feature anymore. The rules went into the agents' definitions the same day and went through the pipeline as their own ticket, like any other change. The idea of a run that parks and gets picked up again later is also in the harness I've been building in the open since, agentweft.

Two weeks of observations.

Over the next two weeks, the Lead kept trying to sleep, and the attempts got rejected every time; barely any of them had a good reason behind them. The only route left for it was the reminders, and the logs show the result: after the change, the same Lead made 75 more tool calls with no wasted sleep among them, and the ten Leads that ran after it in those two weeks had none either. The sleeps that remained were the allowed kind: 27 long timeouts watching for a dead run and one 8-second wait for a service to come up.

Small fixes got much faster. The median for a small tooling fix went from 23.2 minutes to 5.6, and the share landing inside 15 minutes went from 27% to 71%. Part of that is the fast lane starting the fix the moment it's raised, so the ticket clock starts later too, but the ceremony really did collapse. The same week, the pipeline also got a reflection step, where the agents go back over the feedback from a run and adjust, and that made the iterations kinda agile in a way they weren't before.

On tokens, the measurable part is narrower than I'd like. Over 25% of the Lead's tokens went on wasted sleeps before the change, and none after. A pipeline-wide total of tokens saved I can't give you: I count tokens per agent run in a run ledger, the main session isn't in it, and the same change moved small fixes into the main session, so a before/after total would count those fixes on one side and not on the other. And tickets per hour of agent work went from 1.76 to 1.17, though the work moved from small setup tickets to features at the same time, so I wouldn't read much into it either way.

What carries over.

The weak fix here would have been one more prompt line saying "don't poll". The Lead picked sleeping because nothing told it what waiting costs, and from inside one turn you just don't see that cost; and the next run wouldn't remember a correction made in this one anyway. So the rules went into the agents' definitions, which every new run reads before it starts, together with a way to wait that doesn't need a sleep.

The other part is depth. Every layer between you and the agent doing the work is a layer whose behaviour you only see if you go and look on purpose, and problems collect exactly there. A deeper hierarchy can still be the right call; it just needs someone, or something, watching the layer that waits.

If you run agents that wait on other agents, I'd start by counting the turns in your transcripts that do no work, with the definition written down before you count. Then put the waits next to your cache lifetime: inside it the cost hides, and outside it every wake-up rebuilds the cache at 12.5 times or more the price of a read, depending on the model.

In my case, one read of 303 tool calls found over a quarter of an agent's tokens going on "is it done yet", and the rest of that day was enough to replace it with something that tells it when to look.

原文:https://www.a8q9.cn/en/article/the-agent-that-polled-55-times-to-learn-nothing-5c3a7b.html

相关文章