Implement A2A protocol support for recursive agent invocation

- Modified BaseAgentTool._execute to accept and pass tools parameter
- Updated DelegateWorkTool and AskQuestionTool to pass agent tools
- Modified AgentTools to inject tools into delegation tools
- Added test cases to verify recursive agent invocation works

Fixes #2900

Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
Devin AI
2025-05-25 03:28:40 +00:00
parent 921423679a
commit 14145268d2
5 changed files with 157 additions and 5 deletions

View File

@@ -22,11 +22,21 @@ class AgentTools:
i18n=self.i18n,
description=self.i18n.tools("delegate_work").format(coworkers=coworkers), # type: ignore
)
delegate_tool._agent_tools = self._get_all_agent_tools()
ask_tool = AskQuestionTool(
agents=self.agents,
i18n=self.i18n,
description=self.i18n.tools("ask_question").format(coworkers=coworkers), # type: ignore
)
ask_tool._agent_tools = self._get_all_agent_tools()
return [delegate_tool, ask_tool]
def _get_all_agent_tools(self) -> list[BaseTool]:
"""Get all tools from all agents for recursive invocation"""
all_tools = []
for agent in self.agents:
if agent.tools:
all_tools.extend(agent.tools)
return all_tools

View File

@@ -25,4 +25,5 @@ class AskQuestionTool(BaseAgentTool):
**kwargs,
) -> str:
coworker = self._get_coworker(coworker, **kwargs)
return self._execute(coworker, question, context)
tools = getattr(self, '_agent_tools', None) or kwargs.get('tools')
return self._execute(coworker, question, context, tools)

View File

@@ -1,5 +1,5 @@
import logging
from typing import Optional
from typing import Any, List, Optional
from pydantic import Field
@@ -50,7 +50,8 @@ class BaseAgentTool(BaseTool):
self,
agent_name: Optional[str],
task: str,
context: Optional[str] = None
context: Optional[str] = None,
tools: Optional[List[Any]] = None
) -> str:
"""
Execute delegation to an agent with case-insensitive and whitespace-tolerant matching.
@@ -59,6 +60,7 @@ class BaseAgentTool(BaseTool):
agent_name: Name/role of the agent to delegate to (case-insensitive)
task: The specific question or task to delegate
context: Optional additional context for the task execution
tools: Optional tools to pass to the delegated agent for recursive invocation
Returns:
str: The execution result from the delegated agent or an error message
@@ -113,9 +115,10 @@ class BaseAgentTool(BaseTool):
agent=agent,
expected_output=agent.i18n.slice("manager_request"),
i18n=agent.i18n,
tools=tools,
)
logger.debug(f"Created task for agent '{self.sanitize_agent_name(agent.role)}': {task}")
return agent.execute_task(task_with_assigned_agent, context)
return agent.execute_task(task_with_assigned_agent, context, tools)
except Exception as e:
# Handle task creation or execution errors
return self.i18n.errors("agent_tool_execution_error").format(

View File

@@ -27,4 +27,5 @@ class DelegateWorkTool(BaseAgentTool):
**kwargs,
) -> str:
coworker = self._get_coworker(coworker, **kwargs)
return self._execute(coworker, task, context)
tools = getattr(self, '_agent_tools', None) or kwargs.get('tools')
return self._execute(coworker, task, context, tools)