Feature request: let a background AgentTool/dispatched agent's own intermediate events be observed live (not just wait=false + poll for the final result)
@sanketpatil06 ci sta già lavorando.
Dal 14/9/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
** Please make sure you read the contribution guide and file the issues in the right place. **
Contribution guide.
🔴 Required Information
Is your feature request related to a specific problem?
Related to #4759 and the in-progress PR #6592 (AgentDispatcherToolset) — this is a narrower, additive request on top of those, not a replacement.
We want a chat orchestrator to invoke another agent as a tool, without blocking the orchestrator's own turn (same goal as #4759/#6592), and we want an external caller (e.g. a chat UI) to observe the background agent's own intermediate activity — its text, its own function_call/function_response events — live, as they happen, while it runs in its own isolated Runner/session.
We looked for a native way to do this in 2.7.1 and confirmed none of the existing primitives fit:
AgentTool(tools/agent_tool.py): builds an isolated childRunner, butawaits it fully insiderun_asyncbefore returning the tool'sfunction_response. Blocking, and the child's intermediate events never leave the wrapper — only the final merged text does.mode='task'sub-agents (_TaskAgentTool): not backgrounding — it's a human-in-the-loop hand-off. If the task agent doesn't finish in one internal pass, the whole invocation ends (NodeInterruptedErrorpropagates toRunner._drive_root_node), and the user's next plain-text message gets silently rerouted to the paused task agent (Runner._find_active_task_scope,_append_user_event) instead of the orchestrator. The orchestrator doesn't keep working — it becomes a silent proxy until the task agent callsfinish_task.LongRunningFunctionTool(is_long_running): a real "return now, deliver theFunctionResponselater via a freshrun_async()call" mechanism, and that later call can legitimately be triggered by a server-side callback rather than the end user (matching is purely byfunction_call.id). But marking the dispatch tool itself long-running makesEvent.is_final_response()true immediately, so the orchestrator's turn ends right at the dispatch call — it can't even emit an acknowledgement in the same turn, let alone keep working.response_scheduling(FunctionResponseScheduling): the one truly automatic, no-new-client-message push mechanism we found — but it's implemented only inhandle_function_calls_live(flows/llm_flows/functions.py), gated behind the Live/bidi (run_live/LiveRequestQueue) runtime. Not available to a standard request/responseRunner.run_async()service, which is what most chat backends (including ours) use.
We looked at PR #6592 closely — it already covers "dispatch, don't block, poll/await for the final result, follow-up questions on the same child session, on_complete callback." That's great and we'd use it. But per its own "Remaining bounds" section, its completion signaling is deliberately poll/callback only — there's no way for something outside the tool call itself (e.g. our WS layer) to observe the child's intermediate text/tool_call/tool_response events as they're produced, only the final get_agent_result/await_agent payload.
Describe the Solution You'd Like
On top of #6592's dispatcher (or as a variant of AgentTool), expose the background child's own ADK event stream to something outside the tool call — e.g. an optional on_event callback (sync, called once per child event, analogous to on_complete but per-event) alongside on_complete, so a host application can render live progress (a "sub-agent card" with its own nested activity) for a dispatched/background agent-as-tool the same way it already can for a foreground one.
Two things we'd want kept separate, since they answer different consumers:
- The orchestrator's own conversational context should still only ever see one final message per dispatch (as it does today via
AgentTool'sfunction_response, or via #6592'sget_agent_result) — never the play-by-play. - An external observer (UI, logging, tracing) should be able to see the child's own intermediate events live, independent of whether/when the orchestrator's context is updated.
Impact on your work
We're building this by hand today (isolated Runner + InMemorySessionService, a ContextVar-based side channel into our own event-processing loop, and a manual session_service.append_event for the single final message) specifically to drive a live "sub-agent activity" UI. It works, but every consumer of AgentTool/#6592 who wants observability into a background child's own steps will end up reinventing the same side channel unless it's part of the toolset/AgentTool contract itself.
Willingness to contribute
No — happy to share our working approach as a reference if useful, but not proposing to drive the PR.
🟡 Recommended Information
Describe Alternatives You've Considered
- Subclassing
AgentTool.run_asyncto drive the childRunnerby hand and forward events ourselves (what we did) — works, but touches several private APIs (_get_input_schema,tool_context._invocation_context,PluginManagerinternals) that aren't a documented extension point. mode='single_turn':event.branch("<agent>@<function_call.id>") gives free, correct correlation for concurrent invocations and the events do reach the parent's own stream — but it's fully inline/blocking (tool_context.run_nodeisawaited), so it doesn't help with not blocking the orchestrator's turn.
Proposed API / Implementation
Sketch, additive to #6592's AgentDispatcherToolset:
async def dispatch_agent(
self,
name: str,
instruction: str,
user_message: str,
wait: bool = False,
on_event: Callable[[str, Event], None] | None = None, # (dispatch_id, event)
on_complete: Callable[[str, str], None] | None = None, # (dispatch_id, result)
...
) -> dict:
...
on_event fires once per event the child's own isolated Runner.run_async() yields, same shape/timing as what a foreground AgentTool's caller already sees inside agent_tool.py's drain loop — just not thrown away.
- Lingua principale
- Python
- Stelle
- 21.6k
- Fork
- 4k
- Merge medio
- 13h 49m
- PR unite (30g)
- 10
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di google/adk-python
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
google/adk-python#7217 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
google/adk-python#7206 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
google/adk-python#7205 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
google/adk-python#7196 · 1 commento ·
-
eval request clarification
Difficoltà 1/5 1-3 ore Idoneità per principianti 86/100
google/adk-python#7146 · 2 commenti · 1 assegnatario ·
Tutte le issue di google/adk-python
Issue simili
-
link-check link-check:sphinx-theme
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
qgis/QGIS-Documentation#11275 ·
-
bug priority:normal ready-for-dev
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
OpenHands/extensions#626 · 1 commento ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
CSCfi/sd-search-api#39 ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100