Feature request: let a background AgentTool/dispatched agent's own intermediate events be observed live (not just wait=false + poll for the final result)
メンテナーはふだん 5 日以内に返信
@sanketpatil06 がすでに取り組んでいます。
2026年9月14日 から。
評価
この issue はまだ評価されていません。
説明
** 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.
- 主要言語
- Python
- スター
- 21.6k
- フォーク
- 4k
- 平均マージ
- 9時間 59分
- マージ済み PR(30日)
- 5
環境構築
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
google/adk-python のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
google/adk-python#7385 ·
メンテナーはふだん 5 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
google/adk-python#7381 ·
メンテナーはふだん 5 日以内に返信
-
[A2A] RemoteA2aAgent(use_legacy=False): extension header written to state['http_kwargs'], ignored by a2a-sdk 1.x transports対応中かも @surajksharma07 が 4 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
google/adk-python#7334 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
-
GoogleOidcVerifier treats string "false" as a verified email claim対応中かも @surajksharma07 が 5 日前に担当しました。 オープンcore
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
google/adk-python#7289 · コメント 5 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
-
RestApiTool raises uncaught KeyError when a required path param is omitted対応中かも @llalitkumarrr が 5 日前に担当しました。 オープンrequest clarification tools
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
google/adk-python#7282 · コメント 5 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
google/adk-python の issue をすべて見る
似ている issue
-
good first issue hacktoberfest
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
RogueAlg0/taken#387 · コメント 4 件 ·
メンテナーはふだん 1 日以内に返信
-
tool-calling
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
vllm-project/vllm#59838 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
raullenchai/Rapid-MLX#4042 ·
メンテナーはふだん 1 日以内に返信
-
documentation
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
transitmatters/mbta-slow-zone-bot#70 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
open-webui/open-webui#31871 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信