Skip to content

fix: BigQuery plugin — A2A tool classification, self-consistent span tree, and A2A metadata#5325

Draft
caohy1988 wants to merge 4 commits intogoogle:mainfrom
caohy1988:fix/bq-plugin-span-tree-and-a2a-interaction
Draft

fix: BigQuery plugin — A2A tool classification, self-consistent span tree, and A2A metadata#5325
caohy1988 wants to merge 4 commits intogoogle:mainfrom
caohy1988:fix/bq-plugin-span-tree-and-a2a-interaction

Conversation

@caohy1988
Copy link
Copy Markdown

@caohy1988 caohy1988 commented Apr 14, 2026

Summary

Fixes #5073, #5310, and #5311

Three fixes to the BigQueryAgentAnalyticsPlugin, all localized to the plugin — no changes to ADK core.

Fix 1: Classify TransferToAgentTool transfers to RemoteA2aAgent as TRANSFER_A2A (#5073)

When a RemoteA2aAgent is registered via sub_agents=[], the plugin now correctly classifies transfers to it as TRANSFER_A2A instead of the generic TRANSFER_AGENT. Classification is call-level: a single TransferToAgentTool with mixed local and A2A targets produces the correct label per invocation based on the selected agent_name.

Changes:

  • Added _find_transfer_target() helper — searches the agent tree (sub-agents, parent, peers) for the transfer target
  • Extended _get_tool_origin() with optional tool_args/tool_context for call-level transfer classification
  • Updated after_tool_callback and on_tool_error_callback to pass tool_args/tool_context
Tool path Selected target Label
TransferToAgentTool local agent TRANSFER_AGENT (unchanged)
TransferToAgentTool RemoteA2aAgent TRANSFER_A2A (new)

Fix 2: Self-consistent BQ span tree (#5310)

When OTel instrumentation was active, _resolve_ids() derived parent_span_id from ambient framework spans (execute_tool, call_llm, invoke_agent, send_data) that are never written to BQ, creating phantom references. In production datasets, 92.8% of LLM_RESPONSE events and 10.3% of TOOL events had dangling parent_span_id values.

Root cause: _resolve_ids() Layer 2 (ambient OTel) unconditionally overrode the plugin stack's span_id / parent_span_id, and all 6 post-pop callbacks suppressed plugin-stack overrides when an ambient span was present (None if has_ambient else ...).

Fix:

  • _resolve_ids(): plugin-stack span_id / parent_span_id are now the preferred source. Ambient OTel only provides trace_id (always) and falls back for span IDs only when the plugin stack has no span.
  • 6 post-pop callbacks (after_agent, after_run, after_model, after_tool, on_model_error, on_tool_error): always pass both span_id_override and parent_span_id_override from the plugin stack. The has_ambient gating is removed.

This ensures every parent_span_id in BQ references a span_id that also exists in BQ, producing a self-consistent execution tree.

Fix 3: Surface A2A metadata in BQ (#5311)

RemoteA2aAgent already attaches correlation metadata (a2a:task_id, a2a:context_id, a2a:request, a2a:response) to event.custom_metadata on both legacy (lines 504-520) and v2 (lines 575-590) A2A paths. However, on_event_callback() only logged STATE_DELTA and HITL events — the A2A metadata was silently dropped before reaching BigQuery.

Fix:

  • on_event_callback() now detects events carrying a2a:request or a2a:response in custom_metadata and logs an A2A_INTERACTION event with:
    • content: truncated a2a:response payload (the remote agent's answer)
    • attributes.a2a_metadata: all truncated a2a:* keys for cross-reference JOINs
  • A2A_INTERACTION added to _EVENT_VIEW_DEFS with extracted columns: response_content, a2a_task_id, a2a_context_id, a2a_request, a2a_response
  • Events with only a2a:task_id / a2a:context_id (no request/response payload) are not logged

E2E Validation (real Gemini + real BigQuery)

Tested with a real multi-agent setup:

supervisor (gemini-2.0-flash)
  ├── local_helper  — local sub-agent (greeting)
  └── remote_math   — RemoteA2aAgent via A2A protocol (math)

26 rows landed in BigQuery. Results:

Check Result
Phantom parent_span_id refs 0 (all resolved or null for root events)
A2A_INTERACTION events 3 rows with a2a:task_id, a2a:response metadata
TRANSFER_A2A classification Correct for A2A transfers

Test plan

🤖 Generated with Claude Code

caohy1988 and others added 3 commits April 13, 2026 23:53
…SFER_A2A

When a RemoteA2aAgent is registered via sub_agents, the BigQuery analytics
plugin now correctly classifies transfers to it as "TRANSFER_A2A" instead
of the generic "TRANSFER_AGENT". Classification is call-level: a single
TransferToAgentTool with mixed local and A2A targets produces the correct
label per invocation based on the selected agent_name.

The fix is localized to the plugin — no changes to TransferToAgentTool or
the ADK core. The plugin resolves the target agent at runtime via the
invocation context's agent tree.

Fixes google#5073

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_callback

The error callback path was still calling _get_tool_origin(tool) without
the tool_args and tool_context needed for call-level A2A classification,
leaving failed remote A2A transfers misclassified as TRANSFER_AGENT.

Adds a callback-level regression test that exercises on_tool_error_callback
with a TransferToAgentTool targeting a RemoteA2aAgent and asserts the
logged tool_origin is TRANSFER_A2A.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…data

Fixes two BigQuery analytics plugin issues:

**google#5310 — phantom parent_span_id references:** When OTel instrumentation
was active, _resolve_ids() derived parent_span_id from ambient framework
spans (execute_tool, call_llm, etc.) that are never written to BQ,
creating dangling references. The fix flips priority: plugin-stack
span_id/parent_span_id are now preferred, with ambient OTel only used as
a fallback when the plugin stack has no span. All 6 post-pop callbacks
now always pass both span_id_override and parent_span_id_override from
the plugin stack, removing the has_ambient gating.

**google#5311 — A2A metadata not surfaced in BQ:** RemoteA2aAgent attaches
correlation metadata (a2a:task_id, a2a:context_id, a2a:request,
a2a:response) to event.custom_metadata, but on_event_callback() only
logged STATE_DELTA and HITL events. The fix adds A2A_INTERACTION event
detection: when an event carries a2a:request or a2a:response, it is
logged with the truncated a2a:response as content and all a2a:* keys
under attributes.a2a_metadata.

Fixes google#5310, Fixes google#5311

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@google-cla
Copy link
Copy Markdown

google-cla bot commented Apr 14, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot adk-bot added the tracing [Component] This issue is related to OpenTelemetry tracing label Apr 14, 2026
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Apr 14, 2026

Response from ADK Triaging Agent

Hello @caohy1988, thank you for creating this PR!

It looks like you have not yet signed the Contributor License Agreement (CLA). Please visit https://cla.developers.google.com/ to sign the agreement. This is necessary for us to be able to merge your contribution.

Thanks!

Without this entry the plugin creates the raw BQ rows but never
generates a typed per-event view for A2A_INTERACTION, so users
consuming the plugin through its generated views would not see
the new A2A rows or extracted fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tracing [Component] This issue is related to OpenTelemetry tracing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BigQueryAgentAnalyticsPlugin: tool_origin not set to "A2A" when RemoteA2aAgent is used via sub_agents

3 participants