Skip to main content
RapidDev - Software Development Agency
build-ai-agent

How to Build an AI Agent with Google ADK

Google's Agent Development Kit builds multi-agent systems where a coordinator automatically delegates to specialist sub-agents based on their descriptions, no routing code required. This guide builds an HR onboarding coordinator that hands new-hire requests to a Benefits agent or an IT Provisioning agent, each with its own tool. Stack: Python, google-adk, a Gemini API key. Budget 35-50 minutes.

What you'll learn

  • Build specialist sub-agents with LlmAgent and give each a tool function it actually needs
  • Let a coordinator agent delegate automatically via sub_agents instead of writing routing logic yourself
  • Run and test an agent locally with the adk CLI's dev UI before writing any deployment code
  • Scope each sub-agent's description precisely enough that the coordinator's delegation is predictable
Book a free consultation
4.9Clutch rating ⭐
1,000+Happy partners
20+Countries served
200+Team members
Intermediate9 min read35-50 minPython 3.10+, google-adk, Gemini API key (or Vertex AI credentials), any OSLast updated November 2026RapidDev Engineering Team
TL;DR

Google's Agent Development Kit builds multi-agent systems where a coordinator automatically delegates to specialist sub-agents based on their descriptions, no routing code required. This guide builds an HR onboarding coordinator that hands new-hire requests to a Benefits agent or an IT Provisioning agent, each with its own tool. Stack: Python, google-adk, a Gemini API key. Budget 35-50 minutes.

Quick facts about this guide
FactValue
ToolGoogle ADK
DifficultyIntermediate
Time required35-50 min
CompatibilityPython 3.10+, google-adk, Gemini API key (or Vertex AI credentials), any OS
Last updatedNovember 2026

What you'll build

You'll build an HR onboarding coordinator for a mid-size company. A new hire's first-day questions land on one coordinator agent, which delegates benefits questions to a Benefits agent backed by a plan-lookup tool, and IT setup questions to an IT Provisioning agent backed by an account-provisioning tool. Neither specialist sees the other's questions; the coordinator's own job is just deciding who should.

ADK's delegation is automatic once you set it up: pass sub_agents=[benefits_agent, it_agent] to the coordinator's LlmAgent, and ADK's AutoFlow intercepts a transfer_to_agent function call the coordinator's LLM generates on its own, using each sub-agent's description to decide. You don't write an if/elif router; you write good descriptions and a coordinator instruction that tells it when to delegate at all.

Choose ADK when you're building on Gemini or Google Cloud and want multi-agent delegation as a built-in framework feature rather than something you assemble from a lower-level primitive. If you want the same coordinator-and-specialists shape on Claude or OpenAI models instead, the OpenAI Agents SDK guide's handoffs pattern is the closest equivalent.

Prerequisites

  • Python 3.10+ and a terminal
  • A Gemini API key (GOOGLE_API_KEY) from Google AI Studio, or Vertex AI credentials
  • pip install google-adk
  • Comfort writing plain Python functions as tools

Step-by-step guide

1

Install the ADK and scaffold an agent project

Install google-adk and set your API key. The adk CLI can scaffold a starting project, but this guide writes the agent directly in one file for clarity; either approach produces the same LlmAgent objects underneath.

typescript
1pip install google-adk
2export GOOGLE_API_KEY="..."
3# Optional scaffold: adk create onboarding_agent

Expected result: google-adk installed and GOOGLE_API_KEY set; `adk --version` runs without error.

2

Define the Benefits agent and its tool

look_up_benefits_plan is a plain Python function with a docstring and type hints; ADK turns it into a callable tool automatically when you list it in an LlmAgent's tools. The agent's description is what the coordinator will read later to decide whether to delegate here, so make it specific, not generic.

typescript
1from google.adk.agents import LlmAgent
2
3def look_up_benefits_plan(employee_level: str) -> dict:
4 """Look up the health and retirement benefits plan for a given employee level."""
5 plans = {
6 "junior": {"health": "Standard PPO", "retirement_match": "3%"},
7 "senior": {"health": "Premium PPO", "retirement_match": "5%"},
8 }
9 return plans.get(employee_level, {"health": "Standard PPO", "retirement_match": "3%"})
10
11benefits_agent = LlmAgent(
12 name="BenefitsAgent",
13 model="gemini-3.8-flash",
14 description="Answers questions about health insurance, retirement plans, and benefits enrollment for new hires.",
15 instruction="Help new hires understand their benefits. Always look up their specific plan before answering with numbers.",
16 tools=[look_up_benefits_plan],
17)

Expected result: A BenefitsAgent that, run on its own, answers a benefits question using real plan data from the tool.

3

Define the IT Provisioning agent and its tool

provision_it_account follows the same shape: a plain function the agent calls to actually create accounts, stubbed here to return a confirmation. Its description is deliberately distinct from BenefitsAgent's so the coordinator has a clear signal for which one fits a given question.

typescript
1def provision_it_account(employee_name: str, department: str) -> dict:
2 """Provision email, Slack, and VPN access for a new hire in the given department."""
3 # In production this calls your identity provider's API (e.g. Okta, Google Workspace Admin SDK)
4 return {"employee": employee_name, "email": f"{employee_name.lower().replace(' ', '.')}@company.com", "status": "provisioned"}
5
6it_agent = LlmAgent(
7 name="ITProvisioningAgent",
8 model="gemini-3.8-flash",
9 description="Sets up accounts, email, Slack, and VPN access for new hires on their first day.",
10 instruction="Provision IT access for new hires once you have their name and department. Confirm what was set up.",
11 tools=[provision_it_account],
12)

Expected result: An ITProvisioningAgent that, run on its own, provisions an account and confirms the resulting email address.

4

Build the coordinator with automatic delegation

The coordinator lists both specialists in sub_agents=[...] and nothing else; no tools of its own. Its instruction tells it when to delegate at all, not how, since the how is handled by ADK's AutoFlow generating a transfer_to_agent call once the model decides which sub-agent's description fits.

typescript
1coordinator = LlmAgent(
2 name="OnboardingCoordinator",
3 model="gemini-3.8-flash",
4 description="Routes new-hire onboarding questions to the right specialist.",
5 instruction=(
6 "You help new hires on their first day. Delegate benefits and insurance questions "
7 "to BenefitsAgent and account/access setup questions to ITProvisioningAgent. "
8 "Don't try to answer either kind yourself."
9 ),
10 sub_agents=[benefits_agent, it_agent],
11)
12
13root_agent = coordinator # ADK's CLI and dev UI look for this name

Expected result: A coordinator that, asked a benefits question, internally transfers to BenefitsAgent and returns its answer.

5

Run it locally and watch the delegation happen

adk run starts an interactive terminal chat against root_agent; adk web starts a local browser dev UI (not for production) that shows each transfer_to_agent call as it happens, which is the fastest way to confirm delegation is working as intended before writing any deployment code.

typescript
1adk run onboarding_agent
2# or, for a browser UI:
3adk web --port 8000

Expected result: Typing 'What's my retirement match as a senior engineer?' shows a transfer to BenefitsAgent, then an answer citing the 5% match.

6

Harden the descriptions and prepare for deployment

If the coordinator ever delegates to the wrong specialist, the fix is almost always a vaguer description, not a bigger model. Tighten both agents' description fields with concrete nouns (benefits, insurance, retirement vs. accounts, email, VPN, access) until test questions route correctly, then deploy the whole tree to Cloud Run, GKE, or Agent Runtime; sub_agents deploy together as one unit, not as separate services.

typescript
1# Tightened descriptions reduce ambiguous delegation:
2benefits_agent.description = (
3 "Answers questions about health insurance, retirement plans, 401k matching, "
4 "and benefits enrollment. Does not handle IT, email, or account setup."
5)
6it_agent.description = (
7 "Sets up email, Slack, VPN, and system accounts for new hires. "
8 "Does not handle benefits, insurance, or retirement questions."
9)

Expected result: A coordinator whose delegation choices stop being ambiguous on borderline questions after description edits, with no code changes.

Complete working example

onboarding_agent/agent.py
1"""HR onboarding coordinator: delegates to BenefitsAgent or ITProvisioningAgent automatically.
2Run: adk run onboarding_agent (or: adk web --port 8000)
3(pip install google-adk; set GOOGLE_API_KEY)
4"""
5from google.adk.agents import LlmAgent
6
7
8def look_up_benefits_plan(employee_level: str) -> dict:
9 """Look up the health and retirement benefits plan for a given employee level."""
10 plans = {
11 "junior": {"health": "Standard PPO", "retirement_match": "3%"},
12 "senior": {"health": "Premium PPO", "retirement_match": "5%"},
13 }
14 return plans.get(employee_level, {"health": "Standard PPO", "retirement_match": "3%"})
15
16
17def provision_it_account(employee_name: str, department: str) -> dict:
18 """Provision email, Slack, and VPN access for a new hire in the given department."""
19 # In production this calls your identity provider's API (e.g. Okta, Google Workspace Admin SDK)
20 return {
21 "employee": employee_name,
22 "email": f"{employee_name.lower().replace(' ', '.')}@company.com",
23 "status": "provisioned",
24 }
25
26
27benefits_agent = LlmAgent(
28 name="BenefitsAgent",
29 model="gemini-3.8-flash",
30 description=(
31 "Answers questions about health insurance, retirement plans, 401k matching, "
32 "and benefits enrollment. Does not handle IT, email, or account setup."
33 ),
34 instruction="Help new hires understand their benefits. Always look up their specific plan before answering with numbers.",
35 tools=[look_up_benefits_plan],
36)
37
38it_agent = LlmAgent(
39 name="ITProvisioningAgent",
40 model="gemini-3.8-flash",
41 description=(
42 "Sets up email, Slack, VPN, and system accounts for new hires. "
43 "Does not handle benefits, insurance, or retirement questions."
44 ),
45 instruction="Provision IT access for new hires once you have their name and department. Confirm what was set up.",
46 tools=[provision_it_account],
47)
48
49coordinator = LlmAgent(
50 name="OnboardingCoordinator",
51 model="gemini-3.8-flash",
52 description="Routes new-hire onboarding questions to the right specialist.",
53 instruction=(
54 "You help new hires on their first day. Delegate benefits and insurance questions "
55 "to BenefitsAgent and account/access setup questions to ITProvisioningAgent. "
56 "Don't try to answer either kind yourself."
57 ),
58 sub_agents=[benefits_agent, it_agent],
59)
60
61root_agent = coordinator # adk run / adk web look for this exact name
62

Common mistakes

Why it's a problem: Writing vague sub-agent descriptions like 'handles HR stuff'

How to avoid: The coordinator's delegation quality is only as good as the descriptions it reads. Use concrete nouns for what each specialist does and explicitly does not handle.

Why it's a problem: Naming the final agent variable something other than root_agent

How to avoid: Both `adk run` and `adk web` look for that exact name at module level to find your entry point; anything else fails to load.

Why it's a problem: Giving the coordinator its own tools instead of keeping it as a pure router

How to avoid: A coordinator with tools starts answering things itself instead of delegating, defeating the point of having specialists with focused descriptions.

Why it's a problem: Assuming sub_agents run in parallel

How to avoid: LLM-based delegation via transfer_to_agent hands off control to one sub-agent at a time for that turn; it's sequential delegation, not fan-out. Use ADK's workflow agents for true parallel execution.

Why it's a problem: Testing only with adk web and shipping straight from there

How to avoid: The dev UI is explicitly not for production. Deploy the agent tree to Cloud Run, GKE, or Agent Runtime once testing confirms delegation works.

Best practices

  • Write each sub-agent's description with both what it does and what it explicitly doesn't, to reduce ambiguous delegation.
  • Keep the coordinator free of its own tools so its only job is routing.
  • Test borderline questions (ones that could plausibly go to either specialist) specifically, since those reveal weak descriptions fastest.
  • Scope each sub-agent's tools to exactly what that specialist needs; BenefitsAgent has no reason to touch IT provisioning.
  • Use adk web during development to see every transfer_to_agent call, then move to adk run or a real deployment for anything beyond local testing.
  • Keep model choices consistent across sub-agents unless one genuinely needs more reasoning power than another.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Google ADK coordinator (OnboardingCoordinator) delegating to BenefitsAgent and ITProvisioningAgent via sub_agents (code below). Add a third specialist, PayrollAgent, with a tool for looking up pay schedule and direct deposit setup, and tighten all three descriptions so delegation stays unambiguous. [paste complete_code]

Build AI Agents Prompt

In Claude Code, open onboarding_agent/agent.py and ask: 'Add a FacilitiesAgent sub-agent with a tool for looking up desk assignment and building access, and update OnboardingCoordinator's instruction to mention when to delegate to it.'

Frequently asked questions

How does the coordinator actually decide which sub-agent to use?

Its LLM reads its own instruction plus every sub-agent's description field and generates a transfer_to_agent function call naming the one it judges best. ADK's AutoFlow intercepts that call and switches execution to the named sub-agent; there's no separate classifier model involved.

Can a sub-agent delegate further to its own sub-agents?

Yes, LlmAgent supports nesting sub_agents at any level, so a specialist can itself be a coordinator for narrower specialists. Keep nesting shallow in practice; each extra level adds a delegation decision that can go wrong.

What's the difference between LlmAgent and the Agent class shown in some ADK examples?

Agent is commonly used as an alias/import path for LlmAgent in ADK examples and quickstarts; both refer to the same LLM-backed agent class. This guide imports LlmAgent directly for clarity when building multi-agent delegation.

Does delegation work the same way with Vertex AI credentials instead of a Gemini API key?

Yes. Swap your authentication method (Vertex AI project/location credentials instead of GOOGLE_API_KEY) and the LlmAgent, sub_agents, and tool code are identical; only the auth and deployment target change.

Can I force the coordinator to always delegate instead of ever answering directly?

State that explicitly in its instruction, as this guide's coordinator does ('Don't try to answer either kind yourself'). ADK doesn't hard-enforce it at the framework level; it's a prompting discipline backed by keeping the coordinator's own tools list empty.

Can RapidDev build a multi-agent ADK system like this for our team?

Yes. RapidDev builds coordinator/specialist agent systems on Google ADK, the OpenAI Agents SDK, and Claude Agent SDK, including deployment to Cloud Run or Agent Runtime. A free 30-minute consultation is the fastest way to scope yours.

RapidDev

Talk to an Expert

Our team has built 1,000+ apps. Get personalized help with your project.

Book a free consultation
Matt Graham

Written by

Matt Graham · CEO & Founder, RapidDev

1,000+ client projects delivered. Columbia University & Harvard Business School alumnus, U.S. Navy veteran. About the author →

Learning is great. Shipping is faster with help.

Our engineers have built 1,000+ apps on your platform and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.