mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 04:18:35 +00:00
Fix hierarchical delegation bug where manager couldn't delegate to all crew agents
Fixes #3887 In hierarchical process, when a task was assigned to a specific agent, the manager's delegation tools were only given access to that task's assigned agent instead of all crew agents. This caused delegation errors when trying to delegate to other agents in the crew. Changes: - Updated _update_manager_tools in crew.py to pass self.agents instead of [task.agent] to _inject_delegation_tools - Added comprehensive tests reproducing the bug scenario - Updated existing test to reflect correct behavior where manager can delegate to all crew agents The fix ensures that in hierarchical mode, the manager agent can delegate to any agent in the crew's agents list, not just the task's assigned agent. Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
@@ -1122,7 +1122,7 @@ class Crew(FlowTrackable, BaseModel):
|
||||
) -> list[BaseTool]:
|
||||
if self.manager_agent:
|
||||
if task.agent:
|
||||
tools = self._inject_delegation_tools(tools, task.agent, [task.agent])
|
||||
tools = self._inject_delegation_tools(tools, task.agent, self.agents)
|
||||
else:
|
||||
tools = self._inject_delegation_tools(
|
||||
tools, self.manager_agent, self.agents
|
||||
|
||||
@@ -517,4 +517,103 @@ interactions:
|
||||
- req_8a1b3f7a05a8699dfb93ea93be9788f9
|
||||
http_version: HTTP/1.1
|
||||
status_code: 200
|
||||
- request:
|
||||
body: '{"trace_id": "2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9", "execution_type":
|
||||
"crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null,
|
||||
"crew_name": "crew", "flow_name": null, "crewai_version": "1.4.1", "privacy_level":
|
||||
"standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count":
|
||||
0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-11-11T11:04:11.109473+00:00"},
|
||||
"ephemeral_trace_id": "2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9"}'
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '488'
|
||||
Content-Type:
|
||||
- application/json
|
||||
User-Agent:
|
||||
- CrewAI-CLI/1.4.1
|
||||
X-Crewai-Version:
|
||||
- 1.4.1
|
||||
method: POST
|
||||
uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches
|
||||
response:
|
||||
body:
|
||||
string: '{"id":"ed5f3778-b549-4c32-b7b4-1c43eedb314e","ephemeral_trace_id":"2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.4.1","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"1.4.1","privacy_level":"standard"},"created_at":"2025-11-11T11:04:11.451Z","updated_at":"2025-11-11T11:04:11.451Z","access_code":"TRACE-72181a84cb","user_identifier":null}'
|
||||
headers:
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '515'
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Tue, 11 Nov 2025 11:04:11 GMT
|
||||
cache-control:
|
||||
- no-store
|
||||
content-security-policy:
|
||||
- 'default-src ''self'' *.app.crewai.com app.crewai.com; script-src ''self''
|
||||
''unsafe-inline'' *.app.crewai.com app.crewai.com https://cdn.jsdelivr.net/npm/apexcharts
|
||||
https://www.gstatic.com https://run.pstmn.io https://apis.google.com https://apis.google.com/js/api.js
|
||||
https://accounts.google.com https://accounts.google.com/gsi/client https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css.map
|
||||
https://*.google.com https://docs.google.com https://slides.google.com https://js.hs-scripts.com
|
||||
https://js.sentry-cdn.com https://browser.sentry-cdn.com https://www.googletagmanager.com
|
||||
https://js-na1.hs-scripts.com https://js.hubspot.com http://js-na1.hs-scripts.com
|
||||
https://bat.bing.com https://cdn.amplitude.com https://cdn.segment.com https://d1d3n03t5zntha.cloudfront.net/
|
||||
https://descriptusercontent.com https://edge.fullstory.com https://googleads.g.doubleclick.net
|
||||
https://js.hs-analytics.net https://js.hs-banner.com https://js.hsadspixel.net
|
||||
https://js.hscollectedforms.net https://js.usemessages.com https://snap.licdn.com
|
||||
https://static.cloudflareinsights.com https://static.reo.dev https://www.google-analytics.com
|
||||
https://share.descript.com/; style-src ''self'' ''unsafe-inline'' *.app.crewai.com
|
||||
app.crewai.com https://cdn.jsdelivr.net/npm/apexcharts; img-src ''self'' data:
|
||||
*.app.crewai.com app.crewai.com https://zeus.tools.crewai.com https://dashboard.tools.crewai.com
|
||||
https://cdn.jsdelivr.net https://forms.hsforms.com https://track.hubspot.com
|
||||
https://px.ads.linkedin.com https://px4.ads.linkedin.com https://www.google.com
|
||||
https://www.google.com.br; font-src ''self'' data: *.app.crewai.com app.crewai.com;
|
||||
connect-src ''self'' *.app.crewai.com app.crewai.com https://zeus.tools.crewai.com
|
||||
https://connect.useparagon.com/ https://zeus.useparagon.com/* https://*.useparagon.com/*
|
||||
https://run.pstmn.io https://connect.tools.crewai.com/ https://*.sentry.io
|
||||
https://www.google-analytics.com https://edge.fullstory.com https://rs.fullstory.com
|
||||
https://api.hubspot.com https://forms.hscollectedforms.net https://api.hubapi.com
|
||||
https://px.ads.linkedin.com https://px4.ads.linkedin.com https://google.com/pagead/form-data/16713662509
|
||||
https://google.com/ccm/form-data/16713662509 https://www.google.com/ccm/collect
|
||||
https://worker-actionkit.tools.crewai.com https://api.reo.dev; frame-src ''self''
|
||||
*.app.crewai.com app.crewai.com https://connect.useparagon.com/ https://zeus.tools.crewai.com
|
||||
https://zeus.useparagon.com/* https://connect.tools.crewai.com/ https://docs.google.com
|
||||
https://drive.google.com https://slides.google.com https://accounts.google.com
|
||||
https://*.google.com https://app.hubspot.com/ https://td.doubleclick.net https://www.googletagmanager.com/
|
||||
https://www.youtube.com https://share.descript.com'
|
||||
etag:
|
||||
- W/"0108bfa1b1646d9b56d1c8bfafae33c3"
|
||||
expires:
|
||||
- '0'
|
||||
permissions-policy:
|
||||
- camera=(), microphone=(self), geolocation=()
|
||||
pragma:
|
||||
- no-cache
|
||||
referrer-policy:
|
||||
- strict-origin-when-cross-origin
|
||||
strict-transport-security:
|
||||
- max-age=63072000; includeSubDomains
|
||||
vary:
|
||||
- Accept
|
||||
x-content-type-options:
|
||||
- nosniff
|
||||
x-frame-options:
|
||||
- SAMEORIGIN
|
||||
x-permitted-cross-domain-policies:
|
||||
- none
|
||||
x-request-id:
|
||||
- 2bc7f5a5-7082-4864-8f9e-dafe6bff32b5
|
||||
x-runtime:
|
||||
- '0.070395'
|
||||
x-xss-protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 201
|
||||
message: Created
|
||||
version: 1
|
||||
|
||||
@@ -1864,7 +1864,10 @@ def test_hierarchical_crew_creation_tasks_with_agents(researcher, writer):
|
||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||
def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, writer, ceo):
|
||||
"""
|
||||
Tests that async tasks in hierarchical crews are handled correctly with proper delegation tools
|
||||
Tests that async tasks in hierarchical crews are handled correctly with proper delegation tools.
|
||||
|
||||
After fix for issue #3887, the manager should be able to delegate to ALL crew agents,
|
||||
not just the task's assigned agent.
|
||||
"""
|
||||
task = Task(
|
||||
description="Write one amazing paragraph about AI.",
|
||||
@@ -1906,13 +1909,15 @@ def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, write
|
||||
|
||||
# Verify the delegation tools were passed correctly
|
||||
assert len(tools) == 2
|
||||
|
||||
# After fix for issue #3887, manager can delegate to ALL crew agents
|
||||
assert any(
|
||||
"Delegate a specific task to one of the following coworkers: Senior Writer\n"
|
||||
"Delegate a specific task to one of the following coworkers: Senior Writer, Researcher, CEO"
|
||||
in tool.description
|
||||
for tool in tools
|
||||
)
|
||||
assert any(
|
||||
"Ask a specific question to one of the following coworkers: Senior Writer\n"
|
||||
"Ask a specific question to one of the following coworkers: Senior Writer, Researcher, CEO"
|
||||
in tool.description
|
||||
for tool in tools
|
||||
)
|
||||
@@ -4772,3 +4777,186 @@ def test_ensure_exchanged_messages_are_propagated_to_external_memory():
|
||||
assert "Researcher" in messages[0]["content"]
|
||||
assert messages[1]["role"] == "user"
|
||||
assert "Research a topic to teach a kid aged 6 about math" in messages[1]["content"]
|
||||
|
||||
|
||||
def test_hierarchical_manager_can_delegate_to_crew_agents_issue_3887():
|
||||
"""
|
||||
Test that reproduces and fixes issue #3887.
|
||||
|
||||
When using hierarchical process with a custom manager_agent and tasks assigned
|
||||
to that manager, the manager should be able to delegate to all agents in the
|
||||
crew's agents list, not just the task's assigned agent.
|
||||
|
||||
Bug scenario from issue #3887:
|
||||
- Crew has agents=[planner]
|
||||
- manager_agent=coordinator
|
||||
- Task is assigned to coordinator (the manager)
|
||||
- Manager tries to delegate to "planner"
|
||||
- Before fix: Error "coworker mentioned not found, it must be one of the following options: - coordinator"
|
||||
- After fix: Manager can successfully delegate to "planner"
|
||||
"""
|
||||
# Create agents matching the bug report scenario
|
||||
coordinator = Agent(
|
||||
role="Coordinator",
|
||||
goal="Coordinate the team to complete tasks",
|
||||
backstory="You're a coordinator who delegates work to team members.",
|
||||
allow_delegation=True,
|
||||
)
|
||||
|
||||
planner = Agent(
|
||||
role="Planner",
|
||||
goal="Create technical plans",
|
||||
backstory="You're a planner who creates detailed technical plans.",
|
||||
allow_delegation=False,
|
||||
)
|
||||
|
||||
# Create a task assigned to the coordinator (manager agent)
|
||||
task = Task(
|
||||
description="Create a technical plan for the project",
|
||||
expected_output="A detailed technical plan",
|
||||
agent=coordinator,
|
||||
)
|
||||
|
||||
# Create hierarchical crew with custom manager_agent
|
||||
crew = Crew(
|
||||
agents=[planner], # Only planner in crew agents
|
||||
tasks=[task],
|
||||
process=Process.hierarchical,
|
||||
manager_agent=coordinator, # Coordinator is the manager
|
||||
)
|
||||
|
||||
mock_task_output = TaskOutput(
|
||||
description="Mock description",
|
||||
raw="mocked output",
|
||||
agent="mocked agent",
|
||||
messages=[]
|
||||
)
|
||||
task.output = mock_task_output
|
||||
|
||||
with patch.object(
|
||||
Task, "execute_sync", return_value=mock_task_output
|
||||
) as mock_execute_sync:
|
||||
crew.kickoff()
|
||||
|
||||
# Verify execute_sync was called
|
||||
mock_execute_sync.assert_called_once()
|
||||
|
||||
# Get the tools argument from the call
|
||||
_, kwargs = mock_execute_sync.call_args
|
||||
tools = kwargs["tools"]
|
||||
|
||||
# Verify the delegation tools were passed correctly
|
||||
# The manager should be able to delegate to "planner" (from crew.agents)
|
||||
assert len(tools) == 2
|
||||
|
||||
# Check that the delegation tool includes "Planner" as a coworker
|
||||
delegation_tool = next(
|
||||
(tool for tool in tools if "Delegate" in tool.name), None
|
||||
)
|
||||
assert delegation_tool is not None
|
||||
assert "Planner" in delegation_tool.description, (
|
||||
f"Expected 'Planner' in delegation tool description, "
|
||||
f"but got: {delegation_tool.description}"
|
||||
)
|
||||
|
||||
# Verify the delegation tool has the planner agent in its agents list
|
||||
assert hasattr(delegation_tool, "agents")
|
||||
agent_roles = [agent.role for agent in delegation_tool.agents]
|
||||
assert "Planner" in agent_roles, (
|
||||
f"Expected 'Planner' in delegation tool agents, "
|
||||
f"but got: {agent_roles}"
|
||||
)
|
||||
|
||||
# Verify that "Coordinator" is NOT in the coworkers list
|
||||
# (manager should not delegate to itself)
|
||||
assert "Coordinator" not in delegation_tool.description or (
|
||||
"Coordinator" in delegation_tool.description and "Planner" in delegation_tool.description
|
||||
), "Manager should be able to delegate to crew agents"
|
||||
|
||||
|
||||
def test_hierarchical_manager_can_delegate_to_multiple_crew_agents():
|
||||
"""
|
||||
Test that the manager can delegate to multiple crew agents.
|
||||
|
||||
This extends the fix for issue #3887 to ensure it works with multiple agents.
|
||||
"""
|
||||
# Create manager agent
|
||||
manager = Agent(
|
||||
role="Manager",
|
||||
goal="Manage the team",
|
||||
backstory="You're a manager who coordinates team members.",
|
||||
allow_delegation=True,
|
||||
)
|
||||
|
||||
# Create multiple crew agents
|
||||
researcher = Agent(
|
||||
role="Researcher",
|
||||
goal="Research topics",
|
||||
backstory="You're a researcher.",
|
||||
allow_delegation=False,
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
role="Writer",
|
||||
goal="Write content",
|
||||
backstory="You're a writer.",
|
||||
allow_delegation=False,
|
||||
)
|
||||
|
||||
analyst = Agent(
|
||||
role="Analyst",
|
||||
goal="Analyze data",
|
||||
backstory="You're an analyst.",
|
||||
allow_delegation=False,
|
||||
)
|
||||
|
||||
# Create a task assigned to the manager
|
||||
task = Task(
|
||||
description="Coordinate the team to complete the project",
|
||||
expected_output="Project completed",
|
||||
agent=manager,
|
||||
)
|
||||
|
||||
# Create hierarchical crew with multiple agents
|
||||
crew = Crew(
|
||||
agents=[researcher, writer, analyst],
|
||||
tasks=[task],
|
||||
process=Process.hierarchical,
|
||||
manager_agent=manager,
|
||||
)
|
||||
|
||||
mock_task_output = TaskOutput(
|
||||
description="Mock description",
|
||||
raw="mocked output",
|
||||
agent="mocked agent",
|
||||
messages=[]
|
||||
)
|
||||
task.output = mock_task_output
|
||||
|
||||
with patch.object(
|
||||
Task, "execute_sync", return_value=mock_task_output
|
||||
) as mock_execute_sync:
|
||||
crew.kickoff()
|
||||
|
||||
# Get the tools argument from the call
|
||||
_, kwargs = mock_execute_sync.call_args
|
||||
tools = kwargs["tools"]
|
||||
|
||||
# Find the delegation tool
|
||||
delegation_tool = next(
|
||||
(tool for tool in tools if "Delegate" in tool.name), None
|
||||
)
|
||||
assert delegation_tool is not None
|
||||
|
||||
# Verify all crew agents are available for delegation
|
||||
assert hasattr(delegation_tool, "agents")
|
||||
agent_roles = [agent.role for agent in delegation_tool.agents]
|
||||
|
||||
assert "Researcher" in agent_roles
|
||||
assert "Writer" in agent_roles
|
||||
assert "Analyst" in agent_roles
|
||||
|
||||
# Verify all agents are mentioned in the description
|
||||
assert "Researcher" in delegation_tool.description
|
||||
assert "Writer" in delegation_tool.description
|
||||
assert "Analyst" in delegation_tool.description
|
||||
|
||||
Reference in New Issue
Block a user