-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
We're working on a multi-agent application using ADK v0.5.0 and have encountered some ambiguity regarding the expected behavior of agent handoffs, specifically when using actions.transfer_to_agent
with the ADK Runner
and PostgresSessionService
.
Our Scenario & Observations:
We set up a minimal test case:
- A root agent (
MinimalTransferOrchestratorDebug
, aBaseAgent
) is configured in theRunner
. - Upon receiving its first user message, this orchestrator yields a single
Event
. TheEvent.actions
hastransfer_to_agent
set to the name of a target sub-agent (e.g.,"CustomUserInfoAgent_NativeLLM"
). - The target sub-agent is correctly listed in the root agent's
sub_agents
attribute during initialization.
Observed Behavior (via backend logs):
- The
MinimalTransferOrchestratorDebug
runs its turn and successfully yields the event with thetransfer_to_agent
action. - Our application code (which wraps the
Runner
) logs that theRunner
processed this event and recognized thetransfer_to_agent
request. - However, the target agent (
CustomUserInfoAgent_NativeLLM
) is not automatically invoked by theRunner
in the samerun_async()
cycle. Therun_async()
call completes after processing the orchestrator's event. - If a second message is sent from the client (triggering a new
run_async()
call), theRunner
's_find_agent_to_run
method still selects the original orchestrator, not the target agent. This is because_find_agent_to_run
appears to primarily use theauthor
of the last non-user event to determine the next agent, and it doesn't seem to directly act on atransfer_to_agent
value stored in a previous event's actions within the session history forBaseAgent
s. - We also tried setting the
author
of the transfer event to the target agent's name. This also did not result in the target agent running on the next turn; the_find_agent_to_run
logic still defaulted to the root agent because the target (aBaseAgent
) failed the_is_transferable_across_agent_tree
check (which expectsLlmAgent
). - Modifying the target agent to be an
LlmAgent
still did not result in an automatic chained invocation by theRunner
in the same turn.
This leads to our E2E tests timing out, as they expect the target agent to respond immediately after the transfer is signaled by the orchestrator.
Our Current Understanding/Hypothesis:
Based on documentation review (Multi-Agent Systems page) and preliminary Runner
source code inspection (runners.py
), it seems:
transfer_to_agent
(when set inEventActions
by aBaseAgent
) primarily serves to record the intent for transfer in the session.- The
Runner
does not automatically chain execution to this new target agent within the samerun_async()
call that processed the transfer event. - The logic in
_find_agent_to_run
for determining the next agent to execute in a newrun_async()
call relies onevent.author
and specific conditions (like_is_transferable_across_agent_tree
forLlmAgent
s) and may not directly use atransfer_to_agent
from a previous event's actions to pick the next agent if the author doesn't align with these conditions.
Questions for Clarification:
- What is the precise intended mechanism by which the
Runner
should pick up and execute an agent specified in a previous event'sactions.transfer_to_agent
when the nextrun_async()
call occurs (for bothBaseAgent
andLlmAgent
targets)? - Does the
Runner
support automatic chained invocation (i.e., the target agent runs immediately after the transferring agent's turn, within the same initialrun_async()
context/loop) whentransfer_to_agent
is used by aBaseAgent
? Or is a new external stimulus/run_async()
call always required? - If a new
run_async()
call is required, how does_find_agent_to_run
correctly identify the agent specified in the priortransfer_to_agent
if the last event'sauthor
is still the transferring agent (which might not pass_is_transferable_across_agent_tree
if it's aBaseAgent
)? - What is the recommended pattern for a
BaseAgent
(acting as an orchestrator) to reliably hand off control to anotherBaseAgent
(a sub-agent) such that the sub-agent runs immediately without requiring an intervening user message? Is direct invocation (target_agent._run_async_impl(ctx)
) from the orchestrator the only way, or is there aRunner
-mediated approach?
We are trying to achieve a seamless user experience where sub-tasks handled by different agents can flow automatically. Any guidance on the canonical ADK patterns for this, especially concerning BaseAgent
orchestration and the Runner
's role in transfer_to_agent
, would be immensely helpful.
Thank you!