Skip to content

AgentTool ignores input_schema when wrapping SequentialAgent (or any non-LlmAgent) #4154

@marcopellegrino-devoteam

Description

Summary
When using AgentTool to wrap a SequentialAgent as a tool for a PlanReActPlanner, the input_schema defined on the first sub-agent is not exposed in the tool's function declaration. This causes the planner to send unstructured input (a simple request string) instead of the expected structured schema.

Root Cause
In agent_tool.py, the _get_declaration() method only uses input_schema when the wrapped agent is an LlmAgent:

if isinstance(self.agent, LlmAgent) and self.agent.input_schema:
    result = _automatic_function_calling_util.build_function_declaration(
        func=self.agent.input_schema, variant=self._api_variant
    )
else:
    # Falls back to simple 'request' string parameter
    result = types.FunctionDeclaration(
        parameters=types.Schema(
            type=types.Type.OBJECT,
            properties={
                'request': types.Schema(type=types.Type.STRING),
            },
            required=['request'],
        ),
        ...
    )

Since SequentialAgent extends BaseAgent (not LlmAgent), it always falls into the else branch, exposing only a generic request string parameter.

Additionally, SequentialAgent itself doesn't support input_schema as a parameter (its Pydantic model has extra='forbid'), so there's no way to define an input schema directly on the sequence.

Expected Behavior
When wrapping a SequentialAgent with AgentTool, the tool should expose the input_schema from the first sub-agent (if available), since that's where the input will be consumed.

Reproduction

from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.planners import PlanReActPlanner
from google.adk.tools import agent_tool
from pydantic import BaseModel, Field

class MyInputSchema(BaseModel):
    query: str = Field(..., description="The user's question")
    language: str = Field(..., description="The user's language")

agent_1 = LlmAgent(
    name="agent_1",
    model="gemini-2.5-flash",
    input_schema=MyInputSchema,  # This schema should be exposed
    disallow_transfer_to_peers=True,
)

agent_2 = LlmAgent(
    name="agent_2",
    model="gemini-2.5-flash",
    disallow_transfer_to_peers=True,
)

sequence = SequentialAgent(
    name="sequence",
    description="Process the query through multiple steps",
    sub_agents=[agent_1, agent_2],
)

# AgentTool falls back to 'request' string instead of MyInputSchema
tool_sequence = agent_tool.AgentTool(agent=sequence)

root_agent = LlmAgent(
    name="router",
    model="gemini-2.5-flash",
    planner=PlanReActPlanner(),
    tools=[tool_sequence],
    disallow_transfer_to_parent=True,
)

Actual behavior: The planner sees tool_sequence with a single request: str parameter.

Expected behavior: The planner sees tool_sequence with query: str and language: str parameters from MyInputSchema.

Suggested Fix
Modify AgentTool._get_declaration() to check for SequentialAgent and use the first sub-agent's input_schema:

def _get_declaration(self) -> types.FunctionDeclaration:
    input_schema = None
    
    if isinstance(self.agent, LlmAgent) and self.agent.input_schema:
        input_schema = self.agent.input_schema
    elif isinstance(self.agent, SequentialAgent) and self.agent.sub_agents:
        first_sub = self.agent.sub_agents[0]
        if isinstance(first_sub, LlmAgent) and first_sub.input_schema:
            input_schema = first_sub.input_schema
    
    if input_schema:
        result = _automatic_function_calling_util.build_function_declaration(
            func=input_schema, variant=self._api_variant
        )
        result.description = self.agent.description
    else:
        # Fall back to 'request' string
        ...

The same logic should be applied in run_async() to properly validate and serialize the structured input.

Environment
ADK version: 1.22.0
Python: 3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    core[Component] This issue is related to the core interface and implementation

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions