Multi-Level Task Loop
Hierarchical decomposition with loops at each level — from strategic planning to sub-task execution.
Overview
The multi-level task loop extends the Nested Loop Architecture by organizing loops into a formal hierarchy of strategic, tactical, and operational levels. Each level runs its own loop with distinct objectives, time horizons, and evaluation criteria. This pattern mirrors how real autonomous agent platforms decompose complex work: a planner agent sets direction, task agents coordinate phases, and worker agents handle atomic code changes.
You can see this hierarchy in production systems today. OpenHands (github.com/All-Hands-AI/OpenHands) decomposes a user request into phases (plan, code, test, debug) and runs independent loops at each level. SWE-Agent (github.com/princeton-nlp/SWE-Agent, 15K+ stars) uses a similar structure: a high-level navigation loop selects which files to edit, and a low-level edit loop generates patches and validates them against test output. MetaGPT (github.com/geekan/MetaGPT, 45K+ stars) explicitly models software company roles -- Product Manager, Architect, Project Manager, Engineer -- each running its own loop in a multi-level pipeline.
The Three-Level Hierarchy
Level 1: Strategic Planning Loop
Objective: Define goals, decompose into major phases
Time horizon: Entire task lifetime
Evaluation: Overall progress toward final goal
|
+-- Level 2: Task Execution Loops (one per phase)
Objective: Execute a phase, manage subtasks
Time horizon: Phase duration
Evaluation: Phase completion and quality
|
+-- Level 3: Sub-Task Detail Loops (one per subtask)
Objective: Execute atomic work units
Time horizon: Single subtask
Evaluation: Subtask quality and correctness
Level 1: Strategic Planning Loop
The strategic loop operates at the highest level. It receives the overall objective and breaks it into a sequence of executable phases. This loop iterates until the global goal is achieved or deemed unreachable.
Real-world examples of strategic loops:
- LangGraph (github.com/langchain-ai/langgraph) uses a graph-based planner node as the top-level loop. The planner evaluates the current state and decides whether to route to a research agent, a code agent, or a review agent. It replans when a sub-agent returns an error or when the overall quality score falls below a threshold.
- CrewAI (github.com/crewAIInc/crewAI) assigns a "manager" agent at Level 1 that delegates tasks to specialized agents and evaluates their collective output. The manager loop continues until the overall objective is met.
- OpenHands runs a top-level controller that manages the plan-decide-act cycle. The controller decides when to hand off to the coding agent (Level 2) and when to replan based on test results.
class StrategicLoop:
def __init__(self, planner_agent, evaluator, phase_executor_factory):
self.planner = planner_agent
self.evaluator = evaluator
self.phase_executor_factory = phase_executor_factory
def run(self, objective: str) -> dict:
phases = self.planner.decompose(objective)
global_state = {"objective": objective, "status": "in_progress"}
for phase_idx, phase in enumerate(phases):
executor = self.phase_executor_factory.create(phase, global_state)
try:
phase_result = executor.run(phase)
global_state[f"phase_{phase_idx}_result"] = phase_result
except PhaseExecutionError as e:
global_state["status"] = "blocked"
# Replan remaining phases -- this is what LangGraph's
# conditional edges do when an agent node raises an error
revised_phases = self.planner.replan(objective, phases[phase_idx:], e)
phases[phase_idx:] = revised_phases
continue
global_status = self.evaluator.evaluate_global(global_state)
global_state["status"] = "complete" if global_status.passed else "degraded"
return global_state
Key characteristics of Level 1:
- Scope: Entire task or project
- Iteration trigger: Phase completion or failure
- Adaptation: Can replan remaining phases based on outcomes (as LangGraph does with conditional routing)
- State: Maintains global progress and phase dependencies
Level 2: Task Execution Loops
Each phase from Level 1 spawns one or more Level 2 loops. These loops manage the execution of a phase by decomposing it into concrete subtasks.
Aider (github.com/paul-gauthier/aider, 30K+ stars) implements a simpler Level 2 loop. When you run aider --model claude-3-5-sonnet, it identifies which files to edit based on your request, then enters an edit-test-commit loop. Each iteration, aider reads the git diff, checks for lint errors, and either commits or retries.
class TaskExecutionLoop:
def __init__(self, task_agent, evaluator, subtask_executor_factory):
self.agent = task_agent
self.evaluator = evaluator
self.subtask_factory = subtask_executor_factory
def run(self, phase: Phase, global_state: dict) -> PhaseResult:
subtasks = self.agent.decompose_phase(phase)
phase_state = {"phase": phase.name, "subtasks": []}
for subtask in subtasks:
executor = self.subtask_factory.create(subtask, global_state, phase_state)
result = executor.run(subtask)
phase_state["subtasks"].append(result)
if not result.passed and subtask.critical:
raise PhaseExecutionError(f"Critical subtask failed: {subtask.name}")
phase_feedback = self.evaluator.evaluate_phase(phase, phase_state)
return PhaseResult(
phase=phase.name,
output=phase_state,
passed=phase_feedback.passed,
score=phase_feedback.score
)
Key characteristics of Level 2:
- Scope: Single phase within the overall plan
- Iteration trigger: Subtask completion or failure
- Adaptation: Can reorder or skip non-critical subtasks (Cursor's parallel agents handle independent subtasks simultaneously)
- State: Maintains phase-level progress and subtask results
Level 3: Sub-Task Detail Loops
Level 3 loops handle the finest-grained work -- the loops closest to the actual execution environment (APIs, code execution, data processing). This is where Cline (github.com/cline/cline) operates. Cline, as a VS Code plugin with MCP protocol support, enters an edit-run-check loop for each individual code change: write the edit, run the test or linter, read the output, and retry if it fails.
Codex CLI (github.com/openai/codex) works similarly at Level 3. When given a specific task, it enters a write-evaluate-revise loop, running shell commands to verify each change before moving on.
Windsurf (windsurf.ai) uses its Cascade agent for multi-step execution at this level. Each step in a Cascade flow is a Level 3 loop: execute an action, observe the result, decide whether to continue or retry.
class SubTaskDetailLoop:
def __init__(self, worker_agent, validator, config):
self.agent = worker_agent
self.validator = validator
self.config = config
self._best_output = None
self._best_score = 0.0
def run(self, subtask: SubTask) -> SubTaskResult:
for iteration in range(self.config.max_retries):
output = self.agent.execute(subtask)
validation = self.validator.validate(subtask.acceptance_criteria, output)
# Track best result even on failure (avoid blind retries)
if validation.score > self._best_score:
self._best_score = validation.score
self._best_output = output
if validation.passed:
return SubTaskResult(
name=subtask.name,
output=output,
passed=True,
score=validation.score,
iterations=iteration + 1
)
# Propagate feedback into subtask context for next iteration
subtask.context["last_feedback"] = validation.feedback
subtask.context["iteration"] = iteration + 1
# Guard against context overflow -- compress old context
if subtask.estimated_token_count() > self.config.max_subtask_tokens:
subtask.context = self._compress_context(subtask.context)
return SubTaskResult(
name=subtask.name,
output=self._best_output,
passed=False,
score=self._best_score,
iterations=self.config.max_retries
)
Key characteristics of Level 3:
Cross-Level Communication
Communication between levels flows both upward and downward. In LangGraph, this is implemented through the shared state object that passes through every node in the graph. In CrewAI, it flows through task outputs and memory. In AutoGen (github.com/microsoft/autogen), messages between agents carry structured context that propagates across levels.
| Direction | Mechanism | Content | Real-World Example |
|---|---|---|---|
| Down (L1 -> L2) | Phase definitions | Goals, constraints, context | LangGraph planner node sets state for downstream agents |
| Down (L2 -> L3) | Subtask definitions | Requirements, acceptance criteria | Claude Code task list entries with file paths and criteria |
| Up (L3 -> L2) | Subtask results | Output, score, feedback | Aider returning git diff results to its edit loop |
| Up (L2 -> L1) | Phase results | Phase output, aggregated score | SWE-Agent test results bubbling up to navigation loop |
| Cross (L1 -> L3) | Global state | Shared context, constraints | MetaGPT PRD document available to Engineer role |
State Propagation
class StatePropagator:
def __init__(self, max_tokens_per_level: dict):
self.global_state = {}
self.phase_states = {}
self.subtask_states = {}
self.max_tokens = max_tokens_per_level
def update_global(self, key: str, value: Any):
self.global_state[key] = value
def get_context_for_level(self, level: int, loop_id: str) -> dict:
context = {"global": dict(self.global_state)}
if level >= 2:
context["phase"] = self.phase_states.get(loop_id, {})
if level >= 3:
context["subtask"] = self.subtask_states.get(loop_id, {})
# Apply compression principle for Level 3
# (avoids the context overflow failure pattern)
if self._estimate_tokens(context) > self.max_tokens.get(level, 8000):
context = self._compress_for_level(context, level)
return context
Token and Cost Considerations
This is not theoretical. The traditional prompt stuffing approach documented in a enterprise report sends 20,000+ tokens per request, but a large portion of that context is irrelevant to the specific subtask at hand. Multi-level loops fix this by scoping context to what each level actually needs.
When to Use Multi-Level Loops
This architecture is appropriate when:
- Tasks are complex enough to warrant strategic decomposition (like SWE-Agent resolving GitHub issues)
- Subtasks have their own iteration and quality requirements (like Aider's edit-test-commit cycle)
- Replanning may be needed based on intermediate results (like LangGraph's conditional edge routing)
- Different levels benefit from different models (the layered model strategy from the token optimization article)
- Multiple agents need to collaborate at different abstraction levels (like MetaGPT's role-based pipeline)
For simpler tasks, start with a Single Loop Architecture. For two-level decomposition, use Nested Loop Architecture. Only adopt multi-level when the complexity justifies the overhead.
Best Practices
- Keep each level's concerns separate -- strategic loops should not deal with implementation details. MetaGPT enforces this by assigning distinct roles to each level.
- Define clear contracts between levels -- input/output schemas for each level boundary. LangGraph's state typing system makes these contracts explicit.
- Implement replanning at Level 1 to handle phase failures gracefully. OpenHands does this in its controller loop when test results indicate a need for a different approach.
- Use timeouts at every level proportional to the expected duration. The "four major failure scenarios" analysis shows that missing termination conditions at Level 3 cause infinite loops.
- Maintain a global state snapshot that any level can reference, but compress it for Level 3 to avoid context overflow.
- Design for level-skipping -- in simple cases, Level 3 may be unnecessary. Claude Code often skips explicit subtask loops for straightforward file edits.
- Monitor resource consumption per level to detect imbalances. With Claude 3.5 Sonnet at $3.00/1M input tokens, a runaway Level 3 loop can quickly become expensive.
- Use the layered model strategy -- run Level 3 subtask loops on cheaper models and reserve powerful models for strategic planning. This is the single most impactful technique from the token optimization guide.