Customer Service Hub: Agent Transfer Control¶
Demonstrates controlling how agents transfer between each other using disallow_transfer_to_parent, disallow_transfer_to_peers, and the .isolate() convenience method. The scenario: a customer service system where a coordinator routes to specialist agents that must complete their task before returning control.
Tip
What you’ll learn How to build a team of agents with a coordinator.
Source: 54_transfer_control.py
from adk_fluent import Agent
# Using explicit .disallow_transfer_to_parent() and .disallow_transfer_to_peers()
billing_explicit = (
Agent("billing_specialist")
.model("gemini-2.5-flash")
.instruct(
"You handle billing inquiries: refunds, payment disputes, "
"invoice corrections, and subscription changes. Resolve the "
"issue completely before finishing."
)
.describe("Handles billing, payment, and subscription issues")
.disallow_transfer_to_parent(True)
.disallow_transfer_to_peers(True)
)
# Using .isolate() -- sets both flags in one call
billing_isolated = (
Agent("billing_specialist")
.model("gemini-2.5-flash")
.instruct(
"You handle billing inquiries: refunds, payment disputes, "
"invoice corrections, and subscription changes. Resolve the "
"issue completely before finishing."
)
.describe("Handles billing, payment, and subscription issues")
.isolate()
)
technical_fluent = (
Agent("technical_specialist")
.model("gemini-2.5-flash")
.instruct(
"You handle technical support: bug reports, integration issues, "
"API errors, and configuration problems. Walk the customer "
"through troubleshooting steps."
)
.describe("Handles technical support and troubleshooting")
.isolate()
)
general_fluent = (
Agent("general_support")
.model("gemini-2.5-flash")
.instruct(
"You handle general inquiries: account information, product "
"questions, and feedback. You may transfer back to the "
"coordinator if the issue requires a specialist."
)
.describe("Handles general inquiries and account questions")
)
coordinator_fluent = (
Agent("service_coordinator")
.model("gemini-2.5-flash")
.instruct(
"You are the front-line customer service coordinator. Greet the "
"customer, understand their issue, and route to the right "
"specialist: billing for payment issues, technical for bugs "
"and integrations, or general for everything else."
)
.sub_agent(billing_isolated)
.sub_agent(technical_fluent)
.sub_agent(general_fluent)
.build()
)
from google.adk.agents.llm_agent import LlmAgent
billing_native = LlmAgent(
name="billing_specialist",
model="gemini-2.5-flash",
instruction=(
"You handle billing inquiries: refunds, payment disputes, "
"invoice corrections, and subscription changes. Resolve the "
"issue completely before finishing."
),
description="Handles billing, payment, and subscription issues",
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
)
technical_native = LlmAgent(
name="technical_specialist",
model="gemini-2.5-flash",
instruction=(
"You handle technical support: bug reports, integration issues, "
"API errors, and configuration problems. Walk the customer "
"through troubleshooting steps."
),
description="Handles technical support and troubleshooting",
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
)
general_native = LlmAgent(
name="general_support",
model="gemini-2.5-flash",
instruction=(
"You handle general inquiries: account information, product "
"questions, and feedback. You may transfer back to the "
"coordinator if the issue requires a specialist."
),
description="Handles general inquiries and account questions",
)
coordinator_native = LlmAgent(
name="service_coordinator",
model="gemini-2.5-flash",
instruction=(
"You are the front-line customer service coordinator. Greet the "
"customer, understand their issue, and route to the right "
"specialist: billing for payment issues, technical for bugs "
"and integrations, or general for everything else."
),
sub_agents=[billing_native, technical_native, general_native],
)
graph TD
n1["service_coordinator"]
n2["billing_specialist"]
n3["technical_specialist"]
n4["general_support"]
n1 --> n2
n1 --> n3
n1 --> n4
Equivalence¶
# 1. Coordinator defaults: transfer is allowed
assert coordinator_fluent.disallow_transfer_to_parent is False
assert coordinator_fluent.disallow_transfer_to_peers is False
# 2. Explicit flags work correctly
built_explicit = billing_explicit.build()
assert built_explicit.disallow_transfer_to_parent is True
assert built_explicit.disallow_transfer_to_peers is True
# 3. .isolate() sets both flags
built_isolated = billing_isolated.build()
assert built_isolated.disallow_transfer_to_parent is True
assert built_isolated.disallow_transfer_to_peers is True
# 4. .isolate() and explicit flags produce the same result
assert built_explicit.disallow_transfer_to_parent == built_isolated.disallow_transfer_to_parent
assert built_explicit.disallow_transfer_to_peers == built_isolated.disallow_transfer_to_peers
# 5. General agent keeps defaults (can transfer freely)
built_general = general_fluent.build()
assert built_general.disallow_transfer_to_parent is False
assert built_general.disallow_transfer_to_peers is False
# 6. Coordinator has the correct number of sub-agents
assert len(coordinator_fluent.sub_agents) == 3
assert coordinator_fluent.sub_agents[0].name == "billing_specialist"
assert coordinator_fluent.sub_agents[1].name == "technical_specialist"
assert coordinator_fluent.sub_agents[2].name == "general_support"
# 7. Native and fluent produce equivalent types
assert type(coordinator_native) == type(coordinator_fluent)
assert type(billing_native) == type(built_isolated)