Side quests for fun and profit
Adventures with subagents
I’m really fascinated with subagents. I first became interested in them because context windows are not unlimited, and while we do have compaction, it’s a bit like lossy compression. I’ll say that lossiness is not always that bad. I’ve had long running threads compacted many times and it just works in a lot of cases.
Subagents can be seen as an alternate or complementary tool for managing context. We can always manually jump back session turns or fork a session at a particular point, something I recommend you do frequently if you’re still human in the looping, and that’s one way of managing context too. But subagents are like doing side quests. They get their own context window, go do the thing, and return to the main quest without polluting the main loop along the way.
We need context, the right context, but not too much context. I look at subagents’ primary use case as being a way to improve exactly this situation, and in my experience they work pretty well for it.
One thing worth being honest about though is subagents aren’t free in terms of context. The spawn prompt and the returned summary still land in your main loop, and the worker starts with zero conversation history. So the whole thing lives or dies on how well the main loop briefs it. A badly briefed subagent burns tokens and comes back with garbage, which may be worse than just doing the work inline. When it goes wrong, that’s usually where.
They can also be launched in parallel for certain types of work, which saves you time. That’s another legitimately useful aspect.
Cache rules everything around me
Finally, there’s cost savings. One idea is to run a smaller, cheaper, faster model in the main loop with a more intelligent model as an advisor. I actually love this idea, but so far I haven’t been able to get the advisor to fire as often as I’d hoped. The cheap model in the main loop is exactly the model least equipped to recognize when it’s out of its depth and should phone a friend. I haven’t given up on it, but I’ve found the inverse pattern of this approach works very well right now.
Keep the more intelligent model in the main loop and assign tasks to cheaper workers. The smart model is the one deciding what to delegate. I tend to use Fable in the main loop, Sonnet as the worker. You could just as easily use GPT 5.6 Sol in the main loop with Grok 4.5 or DeepSeek V4 Flash as the worker. You get the idea.
This approach has been really good for me personally. On my workloads I haven’t noticed any quality loss, and it should save you 50% or more on cost.
Subagents will fire on their own given a good harness. But I prefer to be more explicit as well, and add “use a subagent for this work” or if I know the task can be parallelized something like “fan out subagents.”
A good aim is to get more than 80% of your token volume onto Sonnet via the workers, with the remainder in your Fable main loop. You’ll be able to go longer in your main loop before you reach the dumb zone or compaction. I already mentioned that compaction is actually pretty decent nowadays, but I’d at the same time do what I can to minimize the frequency in which they occur. Plus, compaction takes so long, bummer!
Subagents speed up your work. Partly from parallel agents where possible, but even when parallel work isn’t possible, you’ll finish faster because Sonnet is faster than Fable. The Fable main loop has been enough to keep the Sonnet workers on track. It hands them the right context and verifies the results. It just kinda works.
In Claude Code, you can force all subagents onto Sonnet with one line in your ~/.claude/settings.json:
{
"env": {
"CLAUDE_CODE_SUBAGENT_MODEL": "sonnet"
}
}Simple and easy! Now every subagent launched in Claude Code uses the Sonnet worker. This environment variable takes priority over per-agent model: frontmatter and the model Claude picks at spawn time, so it really does catch everything.
The sweet spot for me has been Fable with medium reasoning effort in the main loop. It’s enough intelligence, reasonable time to completion, and is more affordable than high effort or greater.
One config to rule them all
Here’s what I actually want: Fable at medium effort in the main loop, but Sonnet at high effort for the workers. Out of the box, subagents inherit the session’s effort level. So with my main loop at medium, I get Sonnet workers, but they run at medium too. There’s no global setting for subagent effort the way there is for subagent model. Would be perfect if we had an environment variable to set the reasoning effort for the subagent in Claude Code.
What I can do is override the built-in agents. Custom subagents support an effort: frontmatter field that overrides the session effort, and a user-level agent that shares a built-in agent’s name (Explore, Plan, general-purpose) replaces the built-in. So I can drop files like this into ~/.claude/agents/:
---
name: Explore
description: Fast codebase exploration and search
effort: high
---
Search and analyze the codebase thoroughly. Read-only.Do the same for Plan and general-purpose, add effort: high to any custom agents, and the CLAUDE_CODE_SUBAGENT_MODEL environment variable still handles the model. Every worker is now Sonnet at high effort while my main loop stays Fable at medium.
But this comes with a drawback that is a dealbreaker for me. The built-in Explore and Plan agents deliberately skip loading CLAUDE.md files and the parent session’s git status to stay fast and lean. Custom subagents including ones that override built-ins by name always load both. So every Explore spawn now drags my full CLAUDE.md and a git snapshot into its context. That’s bloat and latency on every side quest, which cuts against the whole point. You’re also replacing the built-in agents’ prompts (which aren’t published) with your own, so behavior can drift from stock.
So the workaround works, but it trades away some of what makes the built-ins cheap. A first-class CLAUDE_CODE_SUBAGENT_EFFORT environment variable or effort as a parameter Claude can set when spawning, like it already can with model, would make this pattern clean.
Codex CLI already does basically exactly this. It has separate defaults for the model and reasoning effort used by subagents, independently of whatever you’re running in the main loop. Multi-agent support is enabled by default, and you can either ask Codex directly to use or fan out subagents or put the behavior into your project instructions.
So for example I could run GPT 5.6 Sol at medium in the main loop and make GPT 5.6 Luna at xhigh the default worker with this in ~/.codex/config.toml:
model = "gpt-5.6-sol"
model_reasoning_effort = "medium"
[agents]
default_subagent_model = "gpt-5.6-luna"
default_subagent_reasoning_effort = "xhigh"That’s kind of exactly what I want. The parent gets enough intelligence to decide what work should leave the main loop, while the workers can be independently optimized for whatever I care about, whether that’s cost, speed, reasoning depth, or some combination of them. Codex also lets an explicit spawn override either default, so you’re not locked into one worker configuration for everything.
You can go further and make named agent roles with their own config, model, effort, instructions, sandbox settings, and so on, but you don’t have to. I’m not about that life. For this pattern the global defaults are enough. That’s the part I like. I don’t need to replace built-ins with an effort override into every custom agent just to get a smarter main loop and cheaper workers running harder. The configuration describes the architecture directly.
And honestly that’s probably the right abstraction. Model choice and reasoning effort are two different decisions. If I’m delegating because I want to protect the main loop from token-heavy side quests, I should be able to choose a cheaper worker and then independently decide how hard I want that worker to think.


