Task Decomposition
Task decomposition turns one vague goal into a small ordered plan: parallel searches first, then the summary that needs them, with room for one replan.
Why Does This Exist?
Ask for an EV market report in one breath: top two rivals in Europe, their prices, and a summary. A single model call writes a confident page with rounded prices and no sources. The shape is right. The numbers are invented, one rival is American, and nothing can be checked.
Task decomposition is the planning step that prevents that. It turns one vague goal into a small graph of runnable steps with named dependencies: search twice in parallel, then summarize only when both prices exist. Each step is small enough to check, and the order is written down instead of re-decided each time.
This page covers the split, the order, and the replan. The broader planning loop lives in planning in agents. Long runs with many dependencies live in long-horizon planning. Handing the steps to people instead of running them yourself lives in supervisor subagent patterns. Checking each draft before shipping lives in self-reflection agents.
Basics first: a subtask is one runnable action with its inputs named, such as "search European EV prices for rival 1." A dependency says which subtasks must finish first. If the summary needs two prices, it waits for both searches.
Think of It Like This
A prep list in a busy kitchen
A chef gets an order for a five-course banquet. Cooking everything at once burns the food and loses ingredients. So the chef writes a prep list first: chop vegetables, marinate meat, chill the dessert dough. Two cooks chop and marinate at the same time. Nobody simmers the soup until the chopping is done. When the fish delivery fails, the chef crosses out one line and writes a replacement instead of throwing the list away.
Map it point for point. The banquet order is the goal. Each prep line is a subtask. Chopping before simmering is the dependency. Two cooks at once is parallel execution. The crossed-out fish line is the replan.
Where it stops: a kitchen list fits on one card. An agent plan must also fit the tools that exist. A step naming an oven the kitchen never bought is not a plan. It is a wish.
How It Actually Works
Call the goal . A plan is a small directed graph of steps with no cycles: an edge from to means waits for . Steps with no incoming edges run in parallel. A step with two incoming edges waits for both. The executor works the graph top to bottom, and the watcher replans the remainder when a step fails.
The three steps
- A: search rival 1. No dependencies, so it runs immediately. It returns a price of with a source link.
- B: search rival 2. No dependencies, so it runs beside . Its first query returns nothing usable, so the watcher replaces it with , a rephrased query that returns with a source link.
- C: summarize. Depends on and , so it waits for both. It computes the gap and writes the report from the two sourced prices.
Worked example
Check the summary math by hand. The two prices are and . The gap is . Verify: . The mean price is . The sum is . Half is . That is the diagram above: gap , mean .
Count the work. Three planned steps plus one replan make executions. Two searches run in parallel, so the wall-clock cost is roughly two rounds, not three. The trade is concrete: one extra planning call buys an order nobody has to re-decide, and one replan slot buys survival when a query fails.
Size each subtask near one tool call. A step like "research everything" is still vague. A step like "open tool, type Paris" is dust. One meaningful call per step is the right grain.
Code
The point here is the order and the replan, not a framework. This stdlib-only sketch runs the same graph with fake tools and recomputes the same gap:
def search_rival_1() -> int: return 38400
def search_rival_2(attempt: int) -> int | None: return None if attempt == 1 else 41200
def summarize(a: int, b: int) -> tuple[int, float]: gap = b - a return gap, (a + b) / 2
a = search_rival_1()b = search_rival_2(1)replans = 0if b is None: b = search_rival_2(2) replans += 1
gap, mean = summarize(a, b)print(gap) # -> 2800print(mean) # -> 39800.0print(3 + replans) # -> 4The gap prints , the mean prints , and the execution count is . The None on attempt one is what a failed search looks like from the inside, and the if below it is the replan.
Watch Out For
Dust: steps too small to matter
Symptom: a simple question becomes five steps: open tool, type query, run search, read result, format answer. Tokens climb, latency climbs, and each extra step is a new place to fail. The plan is longer than the job.
Keep each subtask near one meaningful tool call. If a step cannot fail on its own, merge it into its neighbor. A plan with three real steps beats a plan with ten tiny ones.
A plan that cannot survive contact
Symptom: the first search fails and the run dies holding a beautiful graph from step zero. No watcher, no replan slot, no alternative query. The plan was a script, not a strategy.
Give the executor one explicit rule: on failure, replace the failed step once before quitting, and never run a dependent step on missing inputs. Hold until and both succeed.
The Quick Version
- Task decomposition turns one vague goal into a small graph of subtasks with named dependencies.
- Two searches with no dependencies run in parallel, and the summary waits for both prices.
- A worked report finds 38,400 and 41,200 for a gap of 2,800 and a mean of 39,800.
- Three planned steps plus one replan make 4 executions when the first query for rival 2 fails.
- One meaningful tool call per step is the right grain, and a failed step gets one replacement before the run quits.