Hierarchical process: a manager for the crew
In a hierarchical crew, tasks name no agent: CrewAI builds a manager from manager_llm, and the manager delegates each task to the crew's agents.
Last updated: 28 Sep, 2026 · CrewAI 1.15
Delegation made one worker a delegator. The hierarchical process adds a separate manager on top of the crew instead, so no worker has to be one.
The hierarchical process
Set process=Process.hierarchical and give the crew a manager_llm. Leave the tasks' agent unset; the manager assigns them.
crew = Crew(agents=[clerk, writer], tasks=[task],
process=Process.hierarchical,
manager_llm=ManagerLLM(model="shop")) # or manager_agent=<Agent>A task with no agent
The task names no worker. The manager decides who runs it.
task = Task(description="Answer the customer: {question}",
expected_output="One sentence.") # no agent= here
crew = Crew(agents=[clerk, writer], tasks=[task],
process=Process.hierarchical, manager_llm=ManagerLLM(model="shop"))View the code here
from crewai.tools import tool
ORDERS = {"A17": "shipped on 3 March", "C40": "waiting for stock"}
@tool
def lookup_order(order_id: str) -> str:
"""Look up an order's shipping status by its id, such as A17."""
status = ORDERS.get(order_id)
return f"{order_id} {status}." if status else f"{order_id} is not an order we have."
import json
import os
import re
from crewai import BaseLLM
os.environ["OTEL_SDK_DISABLED"] = "true"
os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
os.environ["CREWAI_TRACING_ENABLED"] = "false"
os.environ["CREWAI_DISABLE_VERSION_CHECK"] = "true"
class ShopLLM(BaseLLM):
script: list = []
def supports_function_calling(self):
return True
def call(self, messages, tools=None, **kwargs):
if isinstance(messages, str):
messages = [{"role": "user", "content": messages}]
if self.script:
return self.script.pop(0)
names = [t["function"]["name"] for t in tools or []]
return self.decide(messages, names)
def decide(self, messages, tools):
last = messages[-1]
if last["role"] == "tool":
return last["content"]
text = last["content"]
orders = re.findall(r"\b[A-Z]\d+\b", text)
wanted = "refund_order" if "refund" in text.lower() else "lookup_order"
matches = [name for name in tools if name.endswith(wanted)]
if orders and matches:
args = json.dumps({"order_id": orders[0]})
return [{"id": f"call_{orders[0]}", "type": "function",
"function": {"name": matches[0], "arguments": args}}]
if "working with:" in text:
context = text.split("working with:")[1].strip().split("\n\n")[0]
return f"Dear customer, {context}"
if orders:
return f"I have no way to look up {orders[0]} yet."
return "Hello. Which order is this about?"
import json
import re
from shop_llm import ShopLLM
class ManagerLLM(ShopLLM):
def call(self, messages, tools=None, **kwargs):
found = {t["function"]["name"]: t["function"] for t in tools or []}
delegate = found.get("delegate_work_to_coworker")
if delegate and messages[-1]["role"] == "user":
names = delegate["description"].split("coworkers: ")[1].split("\n")[0]
coworker = next(n for n in names.split(", ") if "order" in n.lower())
job = re.search(r"Current Task: (.*)", messages[-1]["content"]).group(1)
args = {"task": job, "context": "A customer wrote in.", "coworker": coworker}
return [{"id": "delegate_1", "type": "function", "function": {
"name": "delegate_work_to_coworker", "arguments": json.dumps(args)}}]
return super().call(messages, tools=tools, **kwargs)
A manager running the crew
The manager sends the one task to the clerk, who looks the order up.
import os
os.environ["OTEL_SDK_DISABLED"] = "true"
os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
from crewai import Agent, Crew, Task
from shop_llm import ShopLLM
from tools import lookup_order
clerk = Agent(role="Order clerk", goal="Find the status of customers' orders",
backstory="You can look up any order in the shop's system.",
llm=ShopLLM(model="shop"), tools=[lookup_order])
writer = Agent(role="Reply writer", goal="Write replies to customers",
backstory="You write short, friendly emails.",
llm=ShopLLM(model="shop"))
from crewai import Process
from desk_manager import ManagerLLM
task = Task(description="Answer the customer: {question}", expected_output="One sentence.")
crew = Crew(agents=[clerk, writer], tasks=[task], process=Process.hierarchical,
manager_llm=ManagerLLM(model="shop"), verbose=False)
print(crew.kickoff(inputs={"question": "Where is my order A17?"}).raw)A17 shipped on 3 March.
What the manager did
- CrewAI built a manager called
Crew Managerand gave it the two coworker tools. - The manager delegated the task to the clerk, whose model looked A17 up.
- The workers needed no
allow_delegation; only the manager delegates.
A hierarchical crew with no manager
Leave out manager_llm and the crew is refused when it is built, not at kickoff.
import os
os.environ["OTEL_SDK_DISABLED"] = "true"
os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
from crewai import Agent, Crew, Task
from shop_llm import ShopLLM
from tools import lookup_order
clerk = Agent(role="Order clerk", goal="Find the status of customers' orders",
backstory="You can look up any order in the shop's system.",
llm=ShopLLM(model="shop"), tools=[lookup_order])
writer = Agent(role="Reply writer", goal="Write replies to customers",
backstory="You write short, friendly emails.",
llm=ShopLLM(model="shop"))
from crewai import Process
task = Task(description="Answer the customer: {question}", expected_output="One sentence.")
Crew(agents=[clerk, writer], tasks=[task], process=Process.hierarchical)Traceback (most recent call last):
File "main.py", line 18, in <module>
Crew(agents=[clerk, writer], tasks=[task], process=Process.hierarchical)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Crew
Attribute `manager_llm` or `manager_agent` is required when using hierarchical process. [type=missing_manager_llm_or_manager_agent, input_value={'agents': [Agent(role=Or...chical: 'hierarchical'>}, input_type=dict]A hierarchical process needs manager_llm or manager_agent. Pass a manager_agent when you want to write the manager's role and backstory yourself.
Hierarchical vs delegation
| Delegation | Hierarchical | |
|---|---|---|
| Who delegates | A worker you set | A manager CrewAI builds |
| Task's agent | Set on the task | Left unset |
| Extra model | None | The manager, on every task |
When to add a manager
- Several tasks whose assignment should be decided at run time, not written down.
- A crew where you want one coordinator over interchangeable workers.
Related
- Previous: Delegation between agents
- Next: First flow: steps in plain Python
- Reference: Hierarchical process
- Add a second task and see whether the manager delegates it too.
- Print the manager's role from
result.tasks_output[0].agent. - Pass a
manager_agentwithllm=ManagerLLM(model="shop")instead ofmanager_llm.
This is what real progress feels like.