Overview
ThePlannerWorkerSwarm implements a planner-worker-judge architecture for parallel multi-agent task execution. Based on Cursor’s “Scaling long-running autonomous coding” research, it separates planning from execution: a planner decomposes goals into prioritized tasks, worker agents claim and execute tasks concurrently from a shared queue, and a judge evaluates the cycle results.
The swarm follows a cycle-based workflow:
- Planning: A planner agent decomposes the goal into concrete, prioritized tasks with dependencies
- Execution: Worker agents independently claim tasks from a shared queue and execute them concurrently via
ThreadPoolExecutor— no worker-to-worker coordination - Evaluation: A judge agent evaluates the combined results and decides: complete, fill gaps, or fresh start
- Iteration: If not complete, the planner receives judge feedback and produces new tasks for the next cycle
PlannerWorkerSwarm is not exported from the top-level swarms package. Import it from swarms.structs.planner_worker_swarm.PlannerWorkerSwarm emits OpenTelemetry spans: one when it is constructed, and one for each run() call. Worker threads carry the tracing context, so each worker’s Agent.run span nests under the swarm run. See Telemetry to turn tracing off.
Installation
Attributes
str
default:"PlannerWorkerSwarm"
Name identifier for this swarm instance
str
default:"A planner-worker execution swarm"
Description of the swarm’s purpose
List[Union[Agent, Callable]]
required
Worker agents that execute tasks. Must not be empty.
int
default:"1"
Maximum planner-worker-judge cycles (must be greater than 0)
str
default:"gpt-5.4"
Model for the planner agent
str
default:"gpt-5.4"
Model for the judge agent
int
default:"1"
Max recursive sub-planner depth.
1 = no sub-planners; 2 = CRITICAL tasks are decomposed once.Optional[float]
default:"None"
Max seconds for the entire worker pool per cycle
Optional[float]
default:"None"
Max seconds per individual task execution
Optional[int]
default:"None"
Max concurrent worker threads. Defaults to
min(len(agents), os.cpu_count() or 4).OutputType
default:"dict-all-except-first"
Format for the final result
bool
default:"False"
Whether to save the conversation history to the swarm’s workspace directory at the end of each
run()bool
default:"False"
Enable verbose logging
Methods
run()
Executes the planner-worker-judge cycle up tomax_loops times or until the judge declares the goal complete.
task(str): The goal to accomplishimg(str, optional): Optional image input, passed to the planner, every worker and the judge
output_type
Raises:
ValueError: Iftaskis not provided
run() calls.
get_status()
Returns a structured status report of the swarm and its task queue.name, original_task, and queue (containing total, progress, status_counts, and per-task details)
Usage Examples
Quick Start
Multi-Cycle with Judge Feedback
Setmax_loops > 1 so the judge can request additional planning cycles when the goal is not yet achieved:
- Cycle 1: Judge finds gaps (“missing regional analysis”) — planner creates targeted tasks
- Cycle 2: Judge finds remaining issues (“outlook section too shallow”) — planner fills gaps
- Cycle 3: Judge marks complete with quality 9/10
Recursive Sub-Planners
Setmax_planner_depth > 1 to automatically decompose CRITICAL-priority tasks via sub-planner agents:
Timeouts
worker_timeout: Total wall time for the worker pool per cycle. Workers stop claiming new tasks after this deadline.task_timeout: Per-task execution limit. If exceeded, the task fails with aTimeoutErrorand may be retried. The timed-out call is not interrupted: the worker records the failure once that call returns.
Checking Swarm Status
SwarmRouter Integration
PlannerWorkerSwarm is available as a swarm type in SwarmRouter:
Architecture
Design Principles (from the Cursor blog)
Task State Machine
Cycle Flow
How It Works
Planner Agent: Created internally each cycle. Uses structured output (PlannerTaskSpec) to produce a plan narrative and a list of concrete tasks with title, description, priority (0-3), and dependency titles. On subsequent cycles, the planner receives the judge’s feedback (gaps + follow-up instructions) appended to the original task.
Worker Execution: Each worker runs in a ThreadPoolExecutor thread, independently claiming tasks from a shared TaskQueue. Workers never coordinate with each other. Each worker loop: claims a task, resets agent memory, builds context (WORKER_SYSTEM_PROMPT + task description + dependency results), executes via agent.run(), and marks the task complete or failed. A completed result is added to the shared conversation under the worker’s name.
Optimistic concurrency: every task has a version field. State transitions check the expected version — if another worker modified the task, the operation is rejected. This avoids lock-based coordination problems (deadlocks, forgotten releases).
Claim priority: highest priority first, then oldest first, with dependency satisfaction required.
Judge Agent: Created internally after workers complete. Its task holds the original goal and a status report of every task in the queue. The shared conversation reaches it as typed chat turns, not pasted into the task: its own earlier verdicts arrive as assistant turns and the planner’s and workers’ messages as labelled user turns, so in later cycles it can tell its own reasoning from theirs. It produces a CycleVerdict: