mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-08 07:38:29 +00:00
This PR implements task decomposition as requested in Issue #2717. It allows complex tasks to be automatically split into sub-tasks without manual intervention. - Added parent_task and sub_tasks fields to Task class - Implemented decompose() method to create sub-tasks - Added combine_sub_task_results() method to aggregate results - Updated execute_sync() to handle sub-task execution - Added execute_sub_tasks_async() for asynchronous execution - Created tests for the task decomposition functionality - Added example script demonstrating usage Co-Authored-By: Joe Moura <joao@crewai.com>
158 lines
4.8 KiB
Python
158 lines
4.8 KiB
Python
import pytest
|
|
from unittest.mock import Mock, patch
|
|
|
|
from crewai import Agent, Task
|
|
|
|
|
|
def test_task_decomposition_structure():
|
|
"""Test that task decomposition creates the proper parent-child relationship."""
|
|
agent = Agent(
|
|
role="Researcher",
|
|
goal="Research effectively",
|
|
backstory="You're an expert researcher",
|
|
)
|
|
|
|
parent_task = Task(
|
|
description="Research the impact of AI on various industries",
|
|
expected_output="A comprehensive report",
|
|
agent=agent,
|
|
)
|
|
|
|
sub_task_descriptions = [
|
|
"Research AI impact on healthcare",
|
|
"Research AI impact on finance",
|
|
"Research AI impact on education",
|
|
]
|
|
|
|
sub_tasks = parent_task.decompose(
|
|
descriptions=sub_task_descriptions,
|
|
expected_outputs=["Healthcare report", "Finance report", "Education report"],
|
|
names=["Healthcare", "Finance", "Education"],
|
|
)
|
|
|
|
assert len(sub_tasks) == 3
|
|
assert len(parent_task.sub_tasks) == 3
|
|
|
|
for sub_task in sub_tasks:
|
|
assert sub_task.parent_task == parent_task
|
|
assert parent_task in sub_task.context
|
|
|
|
|
|
def test_task_execution_with_sub_tasks():
|
|
"""Test that executing a task with sub-tasks executes the sub-tasks first."""
|
|
agent = Agent(
|
|
role="Researcher",
|
|
goal="Research effectively",
|
|
backstory="You're an expert researcher",
|
|
)
|
|
|
|
parent_task = Task(
|
|
description="Research the impact of AI on various industries",
|
|
expected_output="A comprehensive report",
|
|
agent=agent,
|
|
)
|
|
|
|
sub_task_descriptions = [
|
|
"Research AI impact on healthcare",
|
|
"Research AI impact on finance",
|
|
"Research AI impact on education",
|
|
]
|
|
|
|
parent_task.decompose(
|
|
descriptions=sub_task_descriptions,
|
|
expected_outputs=["Healthcare report", "Finance report", "Education report"],
|
|
)
|
|
|
|
with patch.object(Agent, 'execute_task', return_value="Mock result") as mock_execute_task:
|
|
result = parent_task.execute_sync()
|
|
|
|
assert mock_execute_task.call_count >= 3
|
|
|
|
for sub_task in parent_task.sub_tasks:
|
|
assert sub_task.output is not None
|
|
|
|
assert result is not None
|
|
assert result.raw is not None
|
|
|
|
|
|
def test_combine_sub_task_results():
|
|
"""Test that combining sub-task results works correctly."""
|
|
agent = Agent(
|
|
role="Researcher",
|
|
goal="Research effectively",
|
|
backstory="You're an expert researcher",
|
|
)
|
|
|
|
parent_task = Task(
|
|
description="Research the impact of AI on various industries",
|
|
expected_output="A comprehensive report",
|
|
agent=agent,
|
|
)
|
|
|
|
sub_tasks = parent_task.decompose([
|
|
"Research AI impact on healthcare",
|
|
"Research AI impact on finance",
|
|
])
|
|
|
|
for sub_task in sub_tasks:
|
|
sub_task.output = Mock()
|
|
sub_task.output.raw = f"Result for {sub_task.description}"
|
|
|
|
with patch.object(Agent, 'execute_task', return_value="Combined result") as mock_execute_task:
|
|
result = parent_task.combine_sub_task_results()
|
|
|
|
assert mock_execute_task.called
|
|
assert result == "Combined result"
|
|
|
|
|
|
def test_task_decomposition_validation():
|
|
"""Test that task decomposition validates inputs correctly."""
|
|
parent_task = Task(
|
|
description="Research the impact of AI",
|
|
expected_output="A report",
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="At least one sub-task description is required"):
|
|
parent_task.decompose([])
|
|
|
|
with pytest.raises(ValueError, match="expected_outputs must have the same length"):
|
|
parent_task.decompose(
|
|
["Task 1", "Task 2"],
|
|
expected_outputs=["Output 1"]
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="names must have the same length"):
|
|
parent_task.decompose(
|
|
["Task 1", "Task 2"],
|
|
names=["Name 1"]
|
|
)
|
|
|
|
|
|
def test_execute_sub_tasks_async():
|
|
"""Test that executing sub-tasks asynchronously works correctly."""
|
|
agent = Agent(
|
|
role="Researcher",
|
|
goal="Research effectively",
|
|
backstory="You're an expert researcher",
|
|
)
|
|
|
|
parent_task = Task(
|
|
description="Research the impact of AI on various industries",
|
|
expected_output="A comprehensive report",
|
|
agent=agent,
|
|
)
|
|
|
|
sub_tasks = parent_task.decompose([
|
|
"Research AI impact on healthcare",
|
|
"Research AI impact on finance",
|
|
])
|
|
|
|
with patch.object(Task, 'execute_async') as mock_execute_async:
|
|
mock_future = Mock()
|
|
mock_execute_async.return_value = mock_future
|
|
|
|
futures = parent_task.execute_sub_tasks_async()
|
|
|
|
assert mock_execute_async.call_count == 2
|
|
assert len(futures) == 2
|