Subgoal Decomposition Inside the Loop
Most agent advice says decompose everything into subgoals. That's wrong for half of tasks. Agent subgoal decomposition helps when structure exists and hurts when you force it. Here's the line.
def agent_loop(state, max_steps=15):
state.subgoals = model_decompose(state.goal) # [{id, desc, status}]
for _ in range(max_steps):
active = next((s for s in state.subgoals if s["status"] == "open"), None)
if active is None:
return synthesize_answer(state) # all subgoals done
action = model_work_on(state, active)
state = update_state(state, action, run_tool(action))
state.subgoals = model_update_status(state) # mark done / add new
return force_answer(state)Most agent guides tell you to decompose every task into subgoals, as if breaking things down is always the smart move. It isn't. Agent subgoal decomposition sharpens performance on tasks with real internal structure and quietly degrades it on tasks that don't have any — where you've forced an artificial skeleton onto something that should have flowed. The skill isn't decomposing more; it's knowing which tasks earn it.
This post covers what decomposition inside the loop actually does, when it helps, when it hurts, and how to implement it so the agent tracks and completes subgoals instead of just listing them and forgetting.
What Is Subgoal Decomposition in an Agent Loop?
Agent subgoal decomposition is breaking the main goal into smaller, trackable objectives that the loop pursues and checks off one at a time. Instead of holding "answer the user's complex question" as one giant target, the agent maintains a list — "find the config file," "identify the changed setting," "trace its effect" — and works them in sequence, marking each done.
The difference from planning is scope and mutability. A plan is usually a fixed sequence of actions. Subgoals are objectives, not actions, and the agent figures out how to achieve each one reactively. Subgoals say what needs to be true; the loop decides how. This makes decomposition more flexible than a rigid plan while still giving the run a spine.
The mechanism that makes it work is the tracking. Listing subgoals is worthless if the agent doesn't maintain their status. Real decomposition keeps a live checklist in the loop's state, updated each step, so the agent always knows what's done, what's active, and what's blocked.
It's worth being precise about the failure that decomposition prevents, because it's specific. Long-context agents suffer from a "lost middle" problem: requirements stated early in a request get buried under pages of tool output and quietly ignored by the time the agent writes its answer. A user who asks the agent to "update the record, notify the account owner, and log the change" often gets two of the three, because the third scrolled out of the agent's effective attention. A tracked subgoal list is immune to this, because the checklist is re-injected fresh every step and the third item sits there, unchecked, refusing to be forgotten. That single property — requirements that can't silently drop — is why decomposition is worth the overhead on any request with more than a couple of distinct asks.
Why It Matters
Decomposition helps for a specific reason: it converts one hard judgment ("am I done with everything?") into several easy ones ("is this subgoal done?"). Models are far better at judging small, concrete completion than sprawling, vague completion. A subgoal like "confirmed the timeout value" has an obvious done-state; "fully answered the question" does not.
It also fights the mid-task amnesia that plagues long loops. By step ten, an agent working a monolithic goal has often lost track of a requirement mentioned in the original request. An explicit subgoal list keeps every requirement visible and unchecked until satisfied, so nothing silently drops.
And it produces better observability. When each subgoal has a status, a stuck run tells you exactly which objective it's stuck on, instead of leaving you to reverse-engineer the failure from a wall of tool calls.
There's a fourth benefit that shows up in multi-agent systems: subgoals are the natural unit of delegation. A coordinator that has already decomposed a task into tracked subgoals can hand each one to a specialized worker and collect results against a clear checklist. Without decomposition, delegation is vague — "help with this task" — and coordination breaks down. The subgoal list becomes the shared contract between coordinator and workers, which is why decomposition tends to be a prerequisite for any agent system with more than one actor.
⚡ Pro tip: Store each subgoal's completion evidence alongside its status, not just a done flag. "Confirmed the timeout value [done: found 30s in config.yaml line 14]" is auditable; a bare checkmark is not. When an answer turns out wrong later, the evidence trail tells you which subgoal was marked done on bad grounds, which is the fastest way to find where a long run went off the rails.
⚡ Pro tip: The value of decomposition scales with how many distinct requirements a task has, not with how hard the task is. A single very hard question ("prove this theorem") may not decompose usefully, while an easy but multi-part request ("update these five records and email a summary") benefits enormously. Count the requirements, not the difficulty.
How to Implement Subgoal Tracking in the Loop
Give the loop a first-class subgoal list in its state, and let the model create, complete, and add subgoals as it learns. The list is data the loop maintains, not just text in a prompt.
[object Object], ,[object Object],(,[object Object],):
state.subgoals = model_decompose(state.goal) ,[object Object],
,[object Object], _ ,[object Object], ,[object Object],(max_steps):
active = ,[object Object],((s ,[object Object], s ,[object Object], state.subgoals ,[object Object], s[,[object Object],] == ,[object Object],), ,[object Object],)
,[object Object], active ,[object Object], ,[object Object],:
,[object Object], synthesize_answer(state) ,[object Object],
action = model_work_on(state, active)
state = update_state(state, action, run_tool(action))
state.subgoals = model_update_status(state) ,[object Object],
,[object Object], force_answer(state)What this does: It decomposes the goal into a tracked list, always works the first open subgoal, updates statuses each step, and only synthesizes a final answer once every subgoal is closed — so the loop can't quit with requirements outstanding.
Feed the subgoal list back into the model's context every step, formatted as a checklist, so the agent reasons against current status rather than reconstructing it.
[object Object], ,[object Object],(,[object Object],):
icons = {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}
,[object Object], ,[object Object],.join(,[object Object], ,[object Object], s ,[object Object], subgoals)What this does: It renders the subgoals as a visible checklist with clear status icons that gets injected into the prompt each step, keeping the agent oriented on what remains without it having to infer progress from the transcript.
⚡ Pro tip: Let the agent add subgoals mid-run, not just at the start. Real tasks reveal hidden requirements — "oh, this record has a dependency I need to update too." An agent that can only work the initial decomposition misses these; one that can append subgoals as it discovers them stays complete. Guard against runaway addition with a cap.
Subgoals also let you model dependencies explicitly, which flat step lists can't. If subgoal B can't start until subgoal A is done — you can't email the summary until you've computed the numbers — encode that as a dependency the loop respects, so the agent never picks up a blocked objective.
[object Object], ,[object Object],(,[object Object],):
done = {s[,[object Object],] ,[object Object], s ,[object Object], subgoals ,[object Object], s[,[object Object],] == ,[object Object],}
,[object Object], s ,[object Object], subgoals:
,[object Object], s[,[object Object],] == ,[object Object], ,[object Object], ,[object Object],(s.get(,[object Object],, [])) <= done:
,[object Object], s ,[object Object],
,[object Object], ,[object Object], ,[object Object],What this does: It selects the first open subgoal whose dependencies are all complete, so the agent works in a valid order automatically and surfaces a clear "everything remaining is blocked" state instead of attempting an objective it can't yet satisfy.
This dependency awareness is where subgoal decomposition pulls decisively ahead of a plain reactive loop. A reactive agent has to rediscover ordering constraints by trial and error — trying to send the summary, failing, realizing it needs the numbers first. An agent with declared dependencies simply never makes the mistake, because the loop won't hand it a blocked subgoal in the first place.
⚡ Pro tip: When you find the agent repeatedly hitting "everything is blocked" with open subgoals remaining, you have a dependency cycle or a missing prerequisite in your decomposition. That deadlock state is a gift — it points straight at a flaw in how the task was broken down, which is far easier to fix than debugging an agent that silently spins.
Common Mistakes
⚠️ Common mistake: Forcing decomposition on atomic tasks. If a task genuinely is one thing — a single lookup, a single transformation — wrapping it in subgoal machinery adds a decomposition call, checklist overhead, and status updates for zero benefit, and sometimes the model invents fake subgoals to fill the structure ("Step 1: understand the question"), wasting steps. Detect trivial tasks and skip decomposition for them.
A second mistake is decomposing too finely. Subgoals like "open the file," "read line 1," "read line 2" are actions masquerading as objectives. Good subgoals are meaningful milestones, each worth several actions. If your subgoals map one-to-one to tool calls, you've just renamed your steps.
A third is never revisiting the decomposition. If the agent discovers the original breakdown was wrong — a subgoal is impossible, or two collapse into one — it needs permission to revise the list, not doggedly pursue a broken plan. Static decomposition is nearly as brittle as a rigid plan.
Three teams show the range. A tax-prep agent decomposed each return into per-form subgoals and cut missed-field errors sharply, because the requirements were naturally discrete. A creative-writing assistant tried decomposition and abandoned it — forcing "write intro / write body / write conclusion" subgoals fragmented the prose and hurt quality. And an infrastructure-migration agent used decomposition with mid-run additions to handle dependencies it couldn't know upfront, adding subgoals as it discovered each downstream system.
Conclusion
Agent subgoal decomposition is a sharp tool for multi-requirement tasks and dead weight on atomic ones. Implement it as a tracked, live checklist the agent maintains and can revise, not a one-time list it forgets, and reserve it for tasks with genuine internal structure.
The decomposition prompt, the status-update logic, and the checklist rendering are reusable across every structured-task agent you build. A prompt library like PromptABCD is a handy place to keep these building blocks versioned, so your next agent gets subgoal tracking that already works rather than one you rebuild and re-debug each time. The upfront investment in a clean decomposition template pays back on every structured task you automate afterward.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
