Skip to content

fix(go-adk): use genai.Schema for sub-agent tool declaration#1713

Closed
mesutoezdil wants to merge 1 commit intokagent-dev:mainfrom
mesutoezdil:fix/go-subagent-schema-parameters
Closed

fix(go-adk): use genai.Schema for sub-agent tool declaration#1713
mesutoezdil wants to merge 1 commit intokagent-dev:mainfrom
mesutoezdil:fix/go-subagent-schema-parameters

Conversation

@mesutoezdil
Copy link
Copy Markdown
Contributor

@mesutoezdil mesutoezdil commented Apr 20, 2026

Fixes #1645.

Ran into this while testing a multi-agent setup with Bedrock. The orchestrator kept failing every time it tried to delegate to a sub-agent. Logs showed it first sent $task as the field name, got rejected, then tried empty args, got rejected again. Never got through.

Dug into it and found that functiontool.New puts the schema in ParametersJsonSchema on the function declaration. Turns out the Anthropic and Bedrock adapters in the ADK only read the Parameters field and just skip ParametersJsonSchema. So from the LLM side the tool shows up with no schema at all, and it has to guess what to send.

OpenAI and Gemini both handle both fields so they work fine, which is why it did not reproduce there.

The fix replaces functiontool.New with a small remoteA2ATool struct that sets Parameters directly using genai.Schema, the same way agenttool does it inside the ADK. It also wires up Run and ProcessRequest so the ADK runtime is happy without needing to reach into internal packages.

Builds clean, all existing tests pass.

Fixes kagent-dev#1645.

functiontool.New populates Declaration().ParametersJsonSchema (JSON
Schema wire format).  The Anthropic and AWS Bedrock model adapters
inside the Google ADK only read Declaration().Parameters (genai.Schema
format) when building the tool list for the API call; they silently
ignore ParametersJsonSchema.  The LLM therefore receives no parameter
schema for remote-agent tools and has to guess a field name.  Claude
Bedrock guessed "$task" on the first call (rejected as an unexpected
property) and then fell back to {} (rejected as missing the required
"request" property), so every sub-agent invocation failed.

Replace functiontool.New with a hand-written remoteA2ATool struct that:
- sets Declaration().Parameters with an explicit genai.Schema
  (same approach used by agenttool in the ADK itself)
- implements Run() and ProcessRequest() to satisfy the ADK-internal
  FunctionTool and RequestProcessor interfaces without importing the
  internal toolutils package

OpenAI and Gemini were not affected because their adapters handle both
schema representations; the regression was specific to Anthropic-family
models (direct API, Bedrock, Vertex AI).

Signed-off-by: mesutoezdil <mesudozdil@gmail.com>
Copilot AI review requested due to automatic review settings April 20, 2026 09:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses sub-agent tool invocation failures for certain backends by ensuring the remote A2A tool declaration carries a parameter schema in the format expected by ADK tooling.

Changes:

  • Replaces functiontool.New usage with a custom remoteA2ATool implementation.
  • Declares tool parameters via an explicit genai.Schema (FunctionDeclaration.Parameters) and ports request-packing logic into ProcessRequest.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +61 to +69
func (t *remoteA2ATool) Declaration() *genai.FunctionDeclaration {
return &genai.FunctionDeclaration{
Name: t.state.name,
Description: t.state.description,
Parameters: &genai.Schema{
Type: genai.TypeObject,
Properties: map[string]*genai.Schema{
"request": {
Type: genai.TypeString,
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remoteA2ATool.Declaration() sets only FunctionDeclaration.Parameters (genai.Schema). In this repo, the Anthropic model adapter currently builds tool schemas only from FunctionDeclaration.ParametersJsonSchema (see go/adk/pkg/models/anthropic_adk.go:238-253) and ignores Parameters. That means sub-agent calls will still be missing the required "request" field when using the Anthropic backend. To keep compatibility across adapters, either also populate ParametersJsonSchema here (e.g., as a small map[string]any JSON schema) or update the Anthropic adapter to handle Parameters as a fallback (like Bedrock/OpenAI do).

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +45
// genai.Schema value rather than ParametersJsonSchema. Anthropic and AWS
// Bedrock model adapters inside the ADK read the Parameters field; they ignore
// ParametersJsonSchema, so using functiontool.New causes those adapters to omit
// the "request" parameter from the tool schema sent to the LLM. When the LLM
// receives no schema it guesses field names (e.g. "$task") and the call fails
// with "missing properties: [request]".
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment claims Anthropic and Bedrock adapters "read the Parameters field" and "ignore ParametersJsonSchema", but in this repo Bedrock conversion checks ParametersJsonSchema first and Anthropic conversion currently only reads ParametersJsonSchema (go/adk/pkg/models/bedrock.go:512-522, go/adk/pkg/models/anthropic_adk.go:238-253). Please adjust the comment to match the actual compatibility matrix (or explicitly clarify it refers to upstream google.golang.org/adk adapters, not kagent's models).

Suggested change
// genai.Schema value rather than ParametersJsonSchema. Anthropic and AWS
// Bedrock model adapters inside the ADK read the Parameters field; they ignore
// ParametersJsonSchema, so using functiontool.New causes those adapters to omit
// the "request" parameter from the tool schema sent to the LLM. When the LLM
// receives no schema it guesses field names (e.g. "$task") and the call fails
// with "missing properties: [request]".
// genai.Schema value rather than relying only on ParametersJsonSchema. ADK
// adapters and conversion paths do not all consume tool schemas the same way,
// and ensuring Parameters is populated avoids cases where the "request"
// parameter is omitted from the tool schema sent to the LLM. When the LLM
// receives no usable schema it may guess field names (e.g. "$task"), and the
// call then fails with "missing properties: [request]".

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@supreme-gg-gg supreme-gg-gg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mesutoezdil this should be fixed already in #1650 the proper way is to use functiontool.New according to ADK docs and to avoid hand-rolling tool packing functions (and a lot of other boilerplate)

@mesutoezdil
Copy link
Copy Markdown
Contributor Author

mesutoezdil commented Apr 20, 2026

Thanks for pointing that out, @supreme-gg-gg! Closing this in favour of #1650. Will pull the latest and look at how you approached it properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Runtime: go | Agent fails to call sub-agents | Missing "request" field

3 participants