Chapter 4 of 8 · 9 min read
A sub-agent saves the transcript, not the work
On this page
A customer wrote in asking why their webhook retry limit had dropped from five attempts to three sometime in the spring, and whether that explained the delivery gaps they had been seeing since. The support agent handled the first eleven turns of the thread well: it read the account, confirmed the observed behaviour, and established that the change was not something the customer had made. Then it went looking for when the value changed, and the thread came apart.
The log search ran fourteen times. Each call returned a page of up to 200 raw log lines, and the agent worked backwards month by month because it had no way to bisect. By the time it located the deploy — rel-2024.03.19-b, which had rolled a platform-wide default from 5 to 3 as part of a rate-limiting change — the conversation carried roughly 31,000 tokens of log output. The answer it gave the customer was about the rate limiter, which is what most of those log lines were about, and it never returned to the retry limit or to the delivery gaps the customer had actually asked about. The failure was not that the agent could not find the change. It found it on call twelve. The failure was that the surrounding 31,000 tokens had displaced the question.
Someone proposed a sub-agent: a fresh context, a brief describing the config key and the date range, only the log tools, and a return value of at most a few hundred tokens. It is the obvious fix, and it works. It also, when both versions were instrumented over 60 replayed tickets, cost 41% more tokens per resolved ticket than the version that was failing.
Where the extra 41% comes from
A sub-agent is not a function call with a smaller context. It is a second conversation, and conversations have fixed costs that do not amortise across a boundary. The parent pays to write a brief; the child pays to read that brief, plus its own system prompt, plus the JSON schemas for every tool it is given, on every one of its own turns. Nothing the parent already knows is available to the child unless it is re-serialized into the brief, and nothing the child learns is available to the parent unless it is in the return value.
Read the loop in that diagram as the thing that costs money. The child's ninth turn re-reads everything the child accumulated on turns one through eight, exactly as the parent's turns re-read the parent transcript. That re-reading is why a conversation's token cost grows quadratically: with every turn reading everything before it, a loop of n turns bills on the order of n² tokens rather than n. Delegation does not remove the quadratic; it starts a second one from a lower base. Whether the total is lower depends entirely on whether the child's loop is shorter than the digression it replaced, and in the log-triage case it was not — the child made nine tool calls where the inline version made fourteen, which is better but not five times better.
The measured comparison across 60 replayed tickets, with token counts as billed input plus output and latency measured end to end on the ticket:
| Inline in the main loop | Sub-agent with fresh context | |
|---|---|---|
| Parent transcript at ticket close | 47,200 tok | 15,900 tok |
| Total tokens billed per ticket | 112,000 tok | 158,000 tok |
| Model calls per ticket | 19 | 27 (18 parent + 9 child) |
| p50 latency, whole ticket | 74 s | 96 s |
| p95 latency, whole ticket | 210 s | 268 s |
| Correct answer to the asked question | 22 / 60 | 51 / 60 |
| Confidently wrong answer | 14 / 60 | 6 / 60 |
The parent transcript fell by 66% and the bill rose by 41%. Both are real, and only one of them is a saving.
The metric that cannot fail
Parent context length is the number teams instinctively reach for, because context exhaustion is the visible symptom and the delegation is aimed at it. It is also the one number that is guaranteed to improve for any delegation whatsoever, which makes it useless as a decision criterion. A summary is shorter than the transcript it summarises by construction; there is no delegation that returns more text to the parent than the work would have produced inline, because if the child returned everything it saw there would have been no point routing it away. Measuring only the parent means every proposal passes, including proposals that doubled the model calls and added a new way to be wrong.
The set of numbers that actually decides is larger and has to be collected on both sides of the boundary. Total tokens billed, summed across parent and child, is the cost. Model calls, likewise summed, is the latency driver and the rate-limit driver. End-to-end latency on the whole task matters more than either, because a sub-agent's turns are serial with the parent's and the user is waiting through both. Task success rate on the question actually asked is the reason for doing it at all. The measurement that people skip is the failure mode, and specifically the rate at which the child returns a plausible summary of the wrong thing — because the parent cannot tell, having thrown away the evidence.
That last one is worth a number. Of the nine failures in the sub-agent column, six were confident wrong answers, and four of those six had the same shape: the child found a config change in the window and reported it, because its brief said find when the value changed and it had no way to express "I found three candidates and I am not sure which one you mean". The parent received retry_limit changed from 5 to 3 in rel-2024.03.19-b, which is a sentence with no uncertainty in it, and wrote a confident reply. Inline, those same runs had the raw log lines in context, and the model sometimes noticed the ambiguity because it could see it.
What would make you reject the sub-agent, then, is a specific and checkable list. Reject it if total tokens rose and success did not, because you have bought nothing. Reject it if end-to-end p95 moved outside your budget, since 268 seconds is fine for an async ticket reply and fatal for an interactive one. Reject it if the confidently-wrong rate went up, even if the overall success rate also went up, when the cost of a wrong answer is high enough — a support reply that invents a cause is worse than one that says it could not determine the cause. And reject it if the brief you had to write to make the child effective turned out to be most of the parent's context, which is the sign that the sub-task was not separable in the first place.
Price the cheap repairs before the architectural one
The instrumented comparison above was between the failing inline version and the sub-agent, and that is a comparison between one option and one option. Three changes cost an afternoon each and address the same underlying problem, which is that the log tool returned raw lines into a context that had better uses for the space.
The first is to change what the tool returns. log_search returned up to 200 matching lines; replacing it with log_config_changes(key, since, until) that returns one row per change — timestamp, deploy ID, old value, new value — turns 2,600 tokens per call into about 90. This is a tool-shaping change, not an architecture change, and it removes the reason the context filled up rather than relocating the fill.
The second is a cap. Any tool that can return unbounded output should have a limit and a continuation token, so that a single call cannot spend 3,000 tokens of context on the model's behalf without the model choosing to. A hard max_bytes on the response plus a next_page field makes the cost of exploration explicit and pageable.
The third is compaction at the point where the digression starts. If the loop knows it is entering a search phase, the transcript up to that point can be summarised and the raw turns dropped once the phase ends, which gives most of the context saving of a sub-agent without a second model loop.
Measured over the same 60 tickets, with each change applied alone to the inline version:
| Change | Total tokens / ticket | Parent transcript at close | Correct answers |
|---|---|---|---|
| Inline, unchanged | 112,000 | 47,200 | 22 / 60 |
| Aggregate-returning log tool | 61,000 | 19,400 | 44 / 60 |
| 40-line cap + pagination | 84,000 | 33,100 | 33 / 60 |
| Compact at end of search phase | 96,000 | 21,800 | 38 / 60 |
| Aggregate tool + compaction | 58,000 | 17,200 | 49 / 60 |
| Sub-agent (for reference) | 158,000 | 15,900 | 51 / 60 |
The last two rows are within two tickets of each other on correctness and a factor of 2.7 apart on cost. That is the comparison the original proposal never ran, because the proposal was framed as a context problem and a sub-agent is the mechanism that most obviously addresses context. The aggregate tool addresses the same problem at its source, and it also makes the inline version's evidence legible — the model sees three candidate changes as three rows and can say so.
None of that makes the sub-agent wrong in general. It makes it the more expensive of two adequate options for this particular task, and the honest write-up says so. Where delegation earns its cost is where the sub-task genuinely has many intermediate results with no post-hoc value and no aggregate form: exploring an unfamiliar repository, trying six approaches to a failing build, walking a dependency graph whose shape you do not know in advance. Those are cases where you cannot design the tool to return the answer, because you do not know what the answer looks like until you have looked.
What delegation is not buying you
A sub-agent is not an isolation boundary in the security sense. The child runs with tools you gave it, in your process, with your credentials, and its output lands in the parent's context as text the parent may act on. Giving the child only log_search and log_fetch narrows what it can do, which is genuinely useful, but the narrowing comes from the tool list rather than from the delegation — you could have narrowed the parent's tool list for that phase instead.
It is also not a reliability mechanism, and the numbers above show why: it changed the distribution of failures rather than removing them, trading fourteen answers that lost the thread for six that were confidently about the wrong config change. And it is not a cost optimisation under any circumstances that hold generally, since the fresh context is paid for from zero and the child's loop is a second accumulation.
The operational column from chapter 3 treats it kindly — no deploy, no credential, no availability number — and that is exactly what makes it easy to over-adopt. Nobody has to run it, so nobody weighs it. The bill arrives in the token line rather than the pager rotation.
What the six confidently-wrong answers have in common with something already seen in this topic is that nothing errored. The child returned a well-formed summary; the parent read it as true; the trace shows a successful delegation. A tool that cannot reach its service throws, and a server that is down returns a code you can alert on, while a sub-agent that summarised the wrong logs and a skill whose contents stopped being accurate both fail by being believed. Those two failure kinds do not belong in the same column of a matrix, and the next chapter separates them.
The extensibility landscape — read the current version at https://daviddominguez.dev/learn/en/extensibility-landscape/a-sub-agent-saves-the-transcript-not-the-work