Since PraisonAI PR #3790, the hierarchical process correctly excludes the manager task from delegation candidates and blocks self-delegation. Earlier releases could allow the manager to delegate a subtask back to itself and could surface the manager’s generic response as the final result. No API change — existing
process="hierarchical" code benefits automatically.Since PraisonAI PR #4739, the hierarchical loop only counts a task on its first transition to
completed (keyed by counted_task_ids), and excludes every synthetic manager_task from prior runs of the same team (tracked in Process._synthetic_manager_task_ids). Existing process="hierarchical" code benefits automatically — no API change.Quick Start
1
Delegate two tasks to two agents
The manager on
manager_llm picks the next task and agent each turn.2
See each delegation turn
Add
output="verbose" to watch the manager emit {task_id, agent_name, action} on every turn.Which hierarchical?
Two flows share thehierarchical keyword — pick the one that matches your setup.
How It Works
The manager loops: it picks a task and agent, the worker runs it, and the loop repeats until the manager returnsaction="stop".
Each turn the manager reads the goal and remaining tasks, returns a single {task_id, agent_name, action} object, and the framework runs the named agent on that task. When no work remains, the manager returns action="stop" and the team aggregates the results.
Return value
Withprocess="hierarchical", .start(), .astart(), and .run() return the raw output of the last user-supplied task (in insertion order), never the Manager’s synthetic manager_task.
- The default single-value return is the last user task’s raw output.
- A stale
manager_taskfrom a prior run of the same team is skipped — calling.start()twice no longer returns the previous run’s Manager output on the second call. - Pass
return_dict=Trueto inspect every task’sTaskOutput, keyed by task id. - The Manager’s own generated content lives only in the delegation trace — run with
output="verbose"to see it.
Prior to PraisonAI PR #3678, hierarchical runs returned the Manager agent’s own generic output instead of the final worker task’s result. If you were previously stripping the Manager preamble in application code, you can now remove that workaround.
Re-running the same team
Callingteam.start() a second time — or starting a team where some tasks are already status == "completed" — now completes correctly instead of looping until MAX_INVALID_SELECTIONS or silently returning early.
Two fixes make this safe:
- Stale managers excluded. Every synthetic
manager_taskthe process injects is tracked inProcess._synthetic_manager_task_ids. On a second run, the previous run’s manager (which the SDK never removes fromself.tasks) is excluded fromtotal_tasks, rejected as a delegation target by id, and left out of the delegable-ids re-prompt. - Counter seeded.
completed_countis seeded from tasks alreadystatus == "completed"before the run starts, so pre-completed tasks are counted once viacounted_task_idsand the loop can reach completion.
Since PraisonAI PR #4739, re-running a
PraisonAIAgents(process='hierarchical') team is safe: stale synthetic managers from prior runs are excluded from total_tasks and rejected as delegation targets, and pre-completed tasks are counted once via counted_task_ids. Previously a second team.start() could loop until MAX_INVALID_SELECTIONS or silently return early.Hierarchical loop bounds
The hierarchical manager delegates tasks in a loop. Two ceilings bound it so a repeatedly-failing task cannot run the loop forever.The manager task now reports
status == "failed" when the loop exits without completing every delegable task. Previously it always reported "completed", masking early exits. If you inspect agent_team.tasks[<manager_id>].status in tests or dashboards, expect "failed" on incomplete runs now.Since PraisonAI PR #4819,
hierarchical() / ahierarchical() honour max_iter (default 10) like workflow() already did, and cap per-task re-delegation at MAX_TASK_RESELECTIONS = 3. Before #4819 a task ending in status == "failed" could be re-delegated forever. No API change — existing process="hierarchical" code benefits automatically.The Manager’s Schema
The manager returns a fixed three-field object every delegation turn.Invalid Selections & the Synthetic manager_task
The framework injects a synthetic manager_task for the manager’s own turn — it is never a delegable task, and rejection is enforced by id so you can safely name a real Task "manager_task".
Before PraisonAI PR #3706,
ManagerInstructions.task_id was described as “1-based” while runtime ids are 0-based, and validation only checked membership in self.tasks — which let the manager delegate to itself and the real user task never ran. If you built a workaround (renaming tasks, wrapping the manager, patching response_format), you can remove it now.Re-selecting an already-completed task
A manager re-selecting a completedtask_id is now logged and ignored — completed_count advances only on a task’s first transition to completed, keyed by counted_task_ids.
Before PR #4739 the loop incremented completed_count on every status check for a selected task, so a re-selected completed task inflated the counter and let the loop exit while genuinely unexecuted tasks were still "not started". Now the counter only advances inside the execution branch, so re-selection is safe.
Run with output="verbose" to see the warning line on every re-selection — the correct signal to tighten the manager prompt or add task-completion hints.
OpenAI Strict-Mode Compatibility
Hierarchical process uses OpenAI’s strict structured-output API natively — no JSON fallback, no per-turn retry. Under the hood, every manager delegation turn asks the LLM for a fixed 3-field object:
The model (
ManagerInstructions in praisonaiagents/process/manager_schema.py) sets extra="forbid", so its generated JSON schema includes additionalProperties: false — the exact shape OpenAI’s strict structured-output validator requires.
Before PraisonAI 2026-08-04, hierarchical runs on OpenAI models silently fell back to JSON-mode on every delegation turn, making runs 5–13× slower. If you added a local workaround (patching
response_format, or forcing manager_llm="anthropic/..." to sidestep the issue), you can remove it — process="hierarchical" is now strict-native by default on any OpenAI model.Manager LLM Choice
manager_llm is optional and defaults to the team’s LLM. A cheaper model is a good default for the manager, because it only picks the next task and agent — it does not do the work.
Common Patterns
Three realistic setups where a manager delegates by name.- Research → Write → Review
- Collect → Analyse → Report
- Triage → Respond
Best Practices
Use a cheaper manager_llm
Use a cheaper manager_llm
The manager only picks the next
(task_id, agent_name, action) — it does not do the actual work. gpt-4o-mini (or an equivalently cheap model on another provider) is a good default.Give tasks descriptive names
Give tasks descriptive names
The manager delegates by
agent_name and picks tasks by task_id. A clear task description helps the manager reason about ordering.Do not set response_format on manager_llm
Do not set response_format on manager_llm
The framework already asks for the strict
ManagerInstructions schema and OpenAI accepts it natively. Overriding response_format re-introduces the JSON fallback.output="verbose" shows delegation turns
output="verbose" shows delegation turns
When debugging why the manager stops early, run with
output="verbose" and read the {task_id, agent_name, action} payloads emitted on each turn.Re-running a team is idempotent
Re-running a team is idempotent
A second
team.start() on an already-completed team returns immediately. Marking individual tasks "not started" and re-running will re-execute only those. Do not manually delete the synthetic manager_task from self.tasks — the SDK now tracks and excludes it automatically.Related
Hierarchical Workflows
The
AgentFlow variant, where a manager validates each step.Agents
The underlying
Agent class.Tasks
The
Task class the manager delegates.Process
The process-mode concept page.

