Skip to content

Document or support change-subject flow: suspend task sub-agent, handle side topic via parent and resume task branch after #6209

Description

@ipassynk

🔴 Required Information

Is your feature request related to a specific problem?

I’m building a coordinator + task sub-agent flow (e.g. order_collector collecting an order) where the user may change topic mid-task — ask something unrelated (restaurant address), then continue the same task without starting over.

Today there is no clear ADK pattern for this. I expect: the task sub-agent stays open (finish_task not called), transfer_to_agent to the parent handles the side topic (e.g. parent calls a single_turn sub-agent), then control returns to the same suspended task branch so the user can finish the original task.

In practice, transfer/resume is unreliable: the parent may not run visibly, the task agent may not re-prompt the user, and the next message may not route back to the open task. I need ADK to document and/or support this change-subject flow explicitly.

Describe the Solution You'd Like

A short guide: “Mid-task topic change with task sub-agents” (coordinator + task + single_turn pattern)
EventActions.suspend_task - set when a task sub-agent calls transfer_to_agent(parent) for a side topic
EventActions.resume_task_branch - The branch id to return to after the detour

Impact on your work

Users do not stay on one topic in a straight line. During a multi-step flow (consultation flow, booking), they often interrupt with a side question (“what’s your address?”, “can i take selfi”, “how much is delivery?”) and then expect to continue where they left off without restarting the flow or losing context.

Willingness to contribute

Yes


🟡 Recommended Information

Describe Alternatives You've Considered

We implemented the side-topic flow in a sample, but it is unreliable and depends on bloated prompts.
google-adk==2.0.0b1

# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from google.adk import Agent
from google.adk.models.llm_request import LlmRequest
from google.adk.tools.function_tool import FunctionTool
from pydantic import BaseModel
from pydantic import Field


class OrderItem(BaseModel):
  name: str = Field(description="Name of the food item ordered")
  quantity: int = Field(description="Quantity ordered")


class PaymentInfo(BaseModel):
  """Output schema for the payment collection task."""

  credit_card_number: str
  cvv: str


class RestaurantAddress(BaseModel):
  """Output schema for the restaurant address lookup."""

  address: str = Field(description="The restaurant street address")


def place_order(orders: list[OrderItem], payment_info: PaymentInfo) -> str:
  """Mock an order placement operation."""
  total_items = sum(item.quantity for item in orders)
  return f"Successfully placed order for {total_items} items."


def confirmation() -> str:
  """Confirm proceeding with the order."""
  return "Proceeding with order."


def get_restaurant_address() -> str:
  """Returns the restaurant street address."""
  return "123 Main Street, Downtown, City Center"


async def _text_response_for_finish_task_agents(
    callback_context, llm_request: LlmRequest
):
  """Use plain text during intake; structured output comes from finish_task.

  On Vertex AI, ADK may set response_mime_type=application/json whenever an
  agent has both output_schema and tools. That forces JSON on every turn,
  including user-facing questions. Clear it here so only finish_task returns
  structured data.
  """
  del callback_context  # Unused.
  llm_request.config.response_schema = None
  llm_request.config.response_mime_type = None
  return None


restaurant_info = Agent(
    name="restaurant_info",
    mode="single_turn",
    model="gemini-3.5-flash",
    output_schema=RestaurantAddress,
    instruction=("""\
You are a restaurant address lookup assistant.
Call the `get_restaurant_address` tool once, then respond with JSON matching the
output schema. Do not add conversational text outside the JSON object.
    """),
    description="Looks up the restaurant street address.",
    tools=[FunctionTool(get_restaurant_address)],
)

order_collector = Agent(
    name="order_collector",
    model="gemini-3.5-flash",
    mode="task",
    output_schema=list[OrderItem],
    instruction=("""\
You are the order collection assistant for a food delivery service.
Our menu today has exactly 3 items: 1. Pizza, 2. Burger, 3. Salad.

You ARE `order_collector`. Never call an `order_collector`, `restaurant_info`,
or `payment_collector` tool — those only exist on the coordinator. Your tools
are `confirmation`, `finish_task`, and `transfer_to_agent` (to `coordinator`).

Ask the user what they would like to order and collect their choice and quantity.
Use plain conversational text when asking questions or clarifying the order.
When the user states items and quantities, acknowledge them and continue the
conversation or call `finish_task` once the order is final.
Do not output JSON or OrderItem lists until you call `finish_task`.
Do not offer anything else.
If the user asks for the restaurant address, call `transfer_to_agent` to hand off
to `coordinator`. Do not call `restaurant_info` yourself.
After control returns to you, continue collecting the order. If the address was
already given, ask:
"What would you like to order, and how many of each? We have Pizza, Burger,
and Salad on the menu today."
If the combined quantity of items exceeds 5, you MUST use the `confirmation`
tool to get user's confirmation before proceeding.
Do not ask for confirmation in natural language, always use the confirmation tool.
If the user asks about payment or other topics unrelated to collecting their
food order, call `transfer_to_agent` to hand off to `coordinator`.
Once you have their final order and confirmation if needed, call `finish_task`.
    """),
    description="Collects the food order from the user.",
    tools=[FunctionTool(confirmation, require_confirmation=True)],
    before_model_callback=_text_response_for_finish_task_agents,
)

payment_collector = Agent(
    name="payment_collector",
    mode="task",
    output_schema=PaymentInfo,
    model="gemini-3.5-flash",
    instruction=("""\
You are a payment collection assistant.
Ask the user for their credit card number and CVV using plain conversational text.
Do not output JSON until you call finish_task at the end.
Once you have both pieces of information, finish your task.
    """),
    description="Collects credit card and CVV from the user.",
    before_model_callback=_text_response_for_finish_task_agents,
)

root_agent = Agent(
    name="coordinator",
    model="gemini-3.5-flash",
    sub_agents=[order_collector, payment_collector, restaurant_info],
    tools=[place_order],
    instruction="""\
You are a helpful coordinator for a food delivery service.
You need both order and payment information to place an order.

Delegate work using the sub-agent tools — do not use `transfer_to_agent` for
`order_collector`, `payment_collector`, or `restaurant_info`:
- Call `order_collector` when the user wants to order food.
- Call `payment_collector` when the user is ready to pay.
- Call `restaurant_info` for any restaurant address question, including when
  `order_collector` transfers to you mid-order.

When the user asks for the restaurant address, call `restaurant_info` exactly
once, then reply in friendly plain text with the address. Do not show raw JSON.
If `order_collector` transferred to you mid-order, end your reply by asking:
"What would you like to order, and how many of each? We have Pizza, Burger,
and Salad on the menu today."
Do not call `restaurant_info` again if the address is already in the
conversation. Do not call `order_collector` when you were transferred to only
answer an address question — the order task will resume automatically.
When `order_collector` or `payment_collector` finish, summarize the result for
the user in friendly plain text. Do not show raw JSON.
    """,
)

here is the flow with side question about address.
Image

Proposed API / Implementation

If you have ideas on how this should look in code, please share a
pseudo-code example.

Additional Context

Add any other context or screenshots about the feature request here.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementation
No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions