Building AI Agents with Google ADK (Agent Development Kit)
ADK is Google's open-source, code-first agent framework — the one behind Agent Builder on Vertex AI. One class does the heavy lifting, workflow agents give you deterministic orchestration, and callbacks handle safety. Here's the runnable tour.
Google's Agent Development Kit — ADK — is the framework that powers Agent Builder on Vertex AI, released as open source so you can run the same thing anywhere. It's deliberately code-first and model-agnostic (though Gemini is first-class), and its best quality is how little it asks you to learn: one class, Agent, does almost everything, and the rest of the framework is a small set of well-chosen tools for orchestration and safety. This is the runnable tour — a single agent, its tools, deterministic multi-step workflows, multi-agent routing, and the callbacks that keep it safe.
The building block: the Agent
Everything in ADK starts with Agent (its formal name is LlmAgent). You give it a name, a model, a one-line description of what it's for, an instruction that acts as its system prompt, and a list of tools. Tap through the pieces:
A unique id for the agent — also how other agents refer to it.
One class — Agent (aka LlmAgent) — and almost everything in ADK is built on it.
Here's a complete agent. Notice that a tool is just a Python function — ADK reads its signature and docstring to build the schema, and by convention a tool returns a dict so it can carry a status alongside its result:
# pip install google-adk
from google.adk.agents import Agent
def get_weather(city: str) -> dict:
"""Return the current weather for a given city."""
return {"status": "success", "report": f"It's 18°C and clear in {city}."}
root_agent = Agent(
name="weather_agent",
model="gemini-2.0-flash",
description="Answers questions about the weather in a city.",
instruction="You are a helpful weather assistant. Use get_weather to answer.",
tools=[get_weather],
)To talk to it, ADK ships a CLI: run adk web in your project for an instant local chat UI, or adk run for a terminal session. To embed the agent in your own app, you drive it with a Runner and a session service — from google.adk.runners import Runner and from google.adk.sessions import InMemorySessionService — then call runner.run_async(...). The ADK Fundamentals notebook builds that full loop, cell by cell.
Workflow agents: deterministic orchestration
Here's where ADK's design shines. Sometimes you don't want the model deciding the order of operations — you want a guaranteed sequence. ADK gives you workflow agents for exactly that: agents whose only job is to orchestrate other agents, in code rather than by a model's guess.
Runs its children in a fixed order, passing shared state along. A deterministic pipeline — writer → editor → publisher.
Workflow agents give you deterministic orchestration — the sequence is code, not a model's guess.
from google.adk.agents import Agent, SequentialAgent
writer = Agent(name="writer", model="gemini-2.0-flash",
instruction="Draft a short blog section on the given topic.")
editor = Agent(name="editor", model="gemini-2.0-flash",
instruction="Tighten the draft: cut fluff, fix flow. Return the final text.")
# Runs writer, then editor, passing state along — every time, in order.
pipeline = SequentialAgent(name="blog_pipeline", sub_agents=[writer, editor])Swap SequentialAgent for ParallelAgent to fan independent work out concurrently, or LoopAgent to repeat a generator-and-critic pair until the output is good enough. The Workflow Agents notebook and Loop Agent notebook build both.
Multi-agent: sub_agents and transfer_to_agent
For model-driven routing — where the agent decides who should handle a request — you give a parent agent a list of sub_agents. ADK automatically gives the parent a transfer_to_agent action, and it uses each sub-agent's description to decide where to send the conversation. This is the classic support-router pattern:
from google.adk.agents import Agent
billing = Agent(name="billing_agent", model="gemini-2.0-flash",
description="Handles payments, charges, invoices and refunds.",
instruction="Resolve billing issues precisely.")
technical = Agent(name="technical_agent", model="gemini-2.0-flash",
description="Handles bugs, logins, errors and outages.",
instruction="Help the user troubleshoot.")
support_router = Agent(
name="support_router",
model="gemini-2.0-flash",
instruction="Route each request to the specialist whose description fits best.",
sub_agents=[billing, technical], # ADK adds transfer_to_agent automatically
)List agents as `sub_agents` and ADK gives the parent a `transfer_to_agent` action to route the conversation.
Callbacks: safety without touching the prompt
ADK's answer to guardrails is callbacks — hooks that fire around the model and tool calls, where you can inspect, modify, or block what's happening. Because they live outside the agent's instruction, they're the right place for the controls you never want the model to be able to talk its way around: PII scrubbing, tool allowlists, budget caps, output checks.
Callbacks are ADK's guardrails: safety and control that live outside the agent's prompt.
Learn it by running it
The concepts here transfer directly to runnable practice. The Google ADK notebook track walks the whole framework in the browser: the LlmAgent and run loop, typed function tools, sequential and parallel workflow agents, multi-agent routing, and callbacks and safety. If you're weighing ADK against the alternatives, the framework comparison puts it side by side with the rest.
ADK's whole personality is in that split: the model decides what it can decide well (what to say, which specialist fits), and you decide what needs to be deterministic (the sequence, the safety, the budget). Learn where to draw that line and you'll build agents that are both capable and boringly reliable — which, in production, is the highest compliment there is.
Further reading & references
Was this useful?
Comments
Loading comments…