Google's Agent Development Kit (ADK) Explained In 20 Minutes
Google has recently launched its new agent framework, the Agent Development Kit (ADK), and it's rapidly gaining traction. In this comprehensive guide, we'll take you from beginner to expert, enabling you to build your own AI agents, automate workflows, and integrate AI agents into your applications.
To help you master ADK as efficiently as possible, we've structured this guide around more than 10 distinct examples. We'll begin with the fundamentals of creating a single agent and progressively introduce more advanced features, culminating in the construction of multi-agent workflows complete with tool-calling capabilities and more.
This guide is designed to be beginner-friendly, walking through each example step-by-step to ensure clarity and demonstrate the simplicity of creating AI agents with ADK.
Overview of the Examples
Here are the various examples we will build together in this article:
- Your First Agent: Understand the core principles of creating a single agent in ADK.
- Adding Tools: Learn to add pre-built and custom tools to enhance your agent's functionality.
- Integrating Other Models: Discover how to incorporate models from OpenAI and Anthropic, so you're not limited to Gemini.
- Structured Outputs: Ensure your agents produce specific JSON structures for seamless integration with other APIs and tools.
- Session and Memory: Enable agents to remember information across different conversations.
- Saving Data (Persistence): Make agents save their session and memory, so data persists even after the application closes.
- Basic Multi-Agent Solutions: Start creating workflows where multiple agents collaborate.
- Stateful Multi-Agent Solutions: Add session and memory to multi-agent systems for more complex interactions.
- Callbacks: Gain control over every part of the agent lifecycle (before/after run, model calls, tool calls).
- Sequential Workflows: Ensure agents work in a specific, ordered sequence (e.g., Agent 1, then Agent 2, then Agent 3).
- Parallel Workflows: Have multiple agents work on tasks in parallel and combine their results.
- Looping Agents: Create agents that work iteratively until they achieve a desired outcome.
Let's begin with our first example: building your first agent with ADK.
Example 1: Building Your First Single Agent
In this first example, we'll focus on building and running your first single agent. We'll walk through five steps:
- Core Agent Attributes: Understand the properties that make up an agent.
- Folder Structure: Learn the required project format for ADK agents.
- Installing Dependencies: Set up the necessary libraries to run the agents.
- Getting an API Key: Access and configure your API key.
- Running Your Agent: Chat with your agent and see it in action.
Core Components of an ADK Agent
When creating your first agent, it's essential to understand its core components.
- Root Agent: Every ADK project must have at least one
root
agent. This serves as the entry point for all requests. - Name: Each agent needs a
name
. This name must match the agent's folder name in the file structure (e.g., agreeting-agent
folder must contain an agent namedgreeting-agent
). If they don't match, ADK will raise an error. - Model: You must specify a large language model (LLM). While you can use models from other providers like OpenAI or Claude (which we'll cover later), the simplest to use are Google's Gemini models. For this guide, we'll use
Gemini 1.5 Flash
. You can explore other available models and their pricing on the Google AI model dashboard.Gemini 1.5 Flash
is incredibly affordable and capable, with a 1 million token context window. - Description: The
description
becomes crucial in multi-agent systems. It provides a high-level overview of the agent's specialty. The root agent uses this description to delegate tasks to the most appropriate sub-agent. For a single agent, it's less critical as there's no delegation. - Instructions: This is the most important property. The
instructions
tell the agent exactly what it should do and how it should behave. You'll see how we can use complex instructions to guide the agent's behavior effectively.
Folder Structure
ADK requires a specific folder structure:
- Agent Folders: Each agent resides in its own folder (e.g.,
/greeting-agent
). -
__init__.py
: This file tells Python to treat the directory as a package. Inside, you'll import the agent, making it accessible to ADK. For instance,from .agent import greeting_agent
points to theagent.py
file. -
.env
file: This file stores your environment variables, like API keys. You only need one.env
file, and it should be placed inside your root agent's folder. -
agent.py
: This file contains the actual agent definition. Remember, the agent's name must match the folder name.
Installing Dependencies
To run the agents, you'll need to install the required packages. A requirements.txt
file lists all necessary dependencies, with the most important being google-adk
.
Follow these steps to create and activate a virtual environment:
- Create a virtual environment:
bash python -m venv .venv
- Activate the environment:
- On macOS/Linux:
source .venv/bin/activate
- On Windows:
.\.venv\Scripts\activate
- On macOS/Linux:
- Install dependencies:
bash pip install -r requirements.txt
Accessing Your API Key
To run the agents, you need a Google AI API key.
- Go to Google AI Studio.
- Create a new project or select an existing one.
- Navigate to the API key section and click "Create API key".
- Copy the generated key and paste it into your
.env
file inside the root agent's folder.
Running the Agent
With everything set up, you can now run your agent.
- Navigate to the folder containing your root agent in your terminal.
- Use the ADK Command Line Interface (CLI). The most useful command for development is
adk web
. This command spins up a user-friendly web interface for interacting with your agents.
adk web
This will start a local server. Open the provided URL in your browser, and you'll see the web interface. You can select your agent, send messages, and inspect the events
, state
, and session
data in real-time. This interactive view is one of ADK's most powerful features for debugging and understanding agent behavior.
For instance, if your greeting agent is instructed to greet the user and ask for their name, you can test it by sending "hello," and it should respond accordingly. The events
tab will show the detailed request and response, including the system instructions and the messages exchanged.
Example 2: Adding Tools to Your Agents
Tools supercharge your agents by giving them additional functionality. We'll cover:
- Types of Tools: The different kinds of tools you can use.
- Adding Tools: How to add them to your agents.
- Best Practices & Limitations: Important considerations when building custom tools.
- Running an Agent with Tools: Seeing it all in action.
Types of Tools in ADK
ADK supports three main types of tools:
- Function Calling Tools: These are custom Python functions you create and pass to your agent. This is the most common approach for tasks like fetching weather data or looking up stock prices.
- Built-in Tools: Google provides several pre-built tools, including Google Search, Code Execution, and RAG (Retrieval-Augmented Generation).
- Note: Built-in tools currently only work with Gemini models.
- Third-Party Tools: You can integrate tools from other frameworks like LangChain or LlamaIndex, as ADK is designed to be open and extensible.
Adding Tools to an Agent
To add a tool, you simply add a tools
property to your agent's definition. This property takes a list of the tools you want the agent to use.
For a built-in tool like Google Search, the implementation is straightforward:
from google_adk.tools import google_search
# Inside your agent definition
tools=[google_search.tool]
Important Limitation: You can only use one built-in tool at a time per agent.
For a custom Python function, you define the function and then pass it to the tools
list.
def get_current_time() -> dict:
"""Fetches the current time."""
# ... implementation ...
return {"current_time": "..."}
# Inside your agent definition
tools=[get_current_time]
Best Practices for Custom Tools
- Docstrings are Crucial: The agent uses the function's docstring to understand what the tool does and when to call it. Be descriptive.
- Specific Return Values: Always return a dictionary with clear, descriptive keys. Instead of just returning a value, wrap it in a dictionary like
{"current_time": "..."}
. This helps the agent understand the context of the returned data. - No Default Parameters: At the time of this writing, ADK does not support default values for function parameters. Define parameters with their types only.
- Limitation: You cannot mix built-in tools and custom tools in the same agent's
tools
list. This will cause an error.
Example 3: Using Other LLMs (OpenAI, Claude)
ADK allows you to connect with models beyond Gemini, such as those from OpenAI and Anthropic. This is achieved using two key technologies: LightLLM and OpenRouter.
- LightLLM: A free library that standardizes the process of calling different LLMs. It provides a single, consistent interface for models from various providers. ADK integrates LightLLM, making it easy to use.
- OpenRouter: A service that acts as a single gateway to numerous LLMs. You buy credits on OpenRouter and can use them to make API calls to any supported model (like GPT-4, Claude 3, etc.) using a single API key.
To use a different model:
- Get an OpenRouter API Key: Sign up on OpenRouter, buy some credits, and create an API key.
- Configure the Agent:
- Import
LightLLM
fromgoogle_adk.llm
. - In your agent's
model
property, instantiateLightLLM
, specifying the provider (openrouter
), the model family (e.g.,openai
), and the specific model name (e.g.,openai/gpt-4o-mini
). - Provide your OpenRouter API key, typically loaded from your
.env
file.
- Import
from google_adk.llm import LightLLM
import os
# Inside your agent definition
model=LightLLM(
model="openrouter/openai/gpt-4o-mini",
api_key=os.environ.get("OPENROUTER_API_KEY")
)
This setup allows you to leverage the strengths of different models within the ADK framework, providing immense flexibility.
Example 4: Structured Outputs
Ensuring your agents produce data in a specific format (like JSON) is critical for building robust workflows. ADK provides a powerful way to enforce structured outputs.
The primary method is using an output_schema
. This tells the agent to format its final response according to a Pydantic BaseModel
class you define.
from pydantic import BaseModel
class EmailContent(BaseModel):
subject: str
body: str
# Inside your agent definition
output_schema=EmailContent
Best Practices:
- Instruct the Agent: In your agent's
instructions
, explicitly tell it to return JSON matching the defined structure. This significantly improves reliability. - Constraint: You cannot use
output_schema
when the agent also uses tools or delegates to other agents. A common pattern is to have one agent perform the complex logic and a second, separate agent handle the final formatting. -
output_key
: You can use theoutput_key
property to save the structured output to a specific key in the agent'sstate
. This makes the data easily accessible to other agents or parts of your application.
# Inside your agent definition
output_schema=EmailContent,
output_key="generated_email"
When you run an agent with these properties, the state
tab in the ADK web UI will show the generated_email
key with the structured JSON object as its value.
Example 5: Session, State, and Runners
These three components are the core engine of ADK. While adk web
handles them automatically, understanding them is key to building custom applications.
Session: A session is essentially a stateful chat history. It contains two main parts:
- State: A dictionary for storing arbitrary data (e.g.,
{"username": "Alex"}
). All agents in a workflow can access and modify this shared state. - Events: A list of all interactions that have occurred, including user messages, agent responses, and tool calls.
- Sessions are identified by a unique ID and are associated with an app name and a user ID.
- State: A dictionary for storing arbitrary data (e.g.,
Types of Sessions:
- In-Memory Session: Data is stored in memory and is lost when the application closes. Good for testing.
- Database Session: Data is persisted to a local database (like SQLite), so conversations are saved.
- Vertex AI Session: Data is stored in the cloud on Google's Vertex AI platform.
Runner: The runner connects everything. It takes two main ingredients:
- Agents: The list of available agents.
- Session Service: The service managing the session (in-memory, database, etc.).
When a user sends a message, the runner: 1. Retrieves the current session and state. 2. Passes the context to the appropriate agent. 3. The agent may call tools or the LLM to generate a response. 4. The new interaction is saved as an event in the session. 5. The final response is sent back to the user.
To build a custom application, you would manually create the session service, create a session, define a runner with your agents and session service, and then create a loop to process user input by calling runner.run_async()
.
Example 6: Persisting Sessions to a Database
To save conversations, you can use a DatabaseSessionService
.
- Specify a Database File: Define the path to your SQLite database file.
- Use
DatabaseSessionService
: Instead ofInMemorySessionService
, instantiateDatabaseSessionService
with the database path. - Manage Sessions: Your application logic should check if a session for a given user already exists in the database.
- If it exists, load it.
- If not, create a new one.
This allows your agent to "remember" past conversations across application restarts. You can inspect the SQLite database to see the raw sessions
, events
, and state
data being stored.
Example 7: Your First Multi-Agent System
Multi-agent systems in ADK work differently from other frameworks like CrewAI. The core principle is delegation.
How it Works: When a root agent receives a query, it examines the
description
of its sub-agents to find the best one for the job. It then delegates the entire task to that sub-agent, which becomes solely responsible for generating the final response. The root agent does not get the result back to re-process.Limitation with Built-in Tools: You cannot use built-in tools (like Google Search) within a sub-agent directly.
- Workaround: You must wrap the sub-agent using
agent_as_tool
. When called this way, the child agent acts like a tool. It does its work and returns the result to the parent agent, which then formulates the final response. This is the only way to use built-in tools in a delegated fashion.
- Workaround: You must wrap the sub-agent using
To create a multi-agent system, you define a sub_agents
property in your root agent, which is a list of the other agent objects.
Example 8: Multi-Agent Systems with Shared State
This is where ADK's power truly shines. By sharing state
across a multi-agent system, you can build incredibly sophisticated workflows.
Imagine a customer service system for an online course with several agents:
- Policy Agent: Answers general questions about refunds, etc.
- Sales Agent: Handles course purchases. It checks the state
to see if the user already owns the course. If not, it "sells" it and updates the state
by adding the course to a purchased_courses
list.
- Course Support Agent: Answers specific questions about the course content, but only if it sees the course is in the user's purchased_courses
list in the state
.
- Order Agent: Processes refunds. It checks the state
for a purchased course and the purchase date, then removes the course from the state
if a refund is processed.
Each agent's behavior is dynamically influenced by the shared state
, allowing them to collaborate intelligently to solve complex, multi-step problems.
Example 9: Callbacks
Callbacks give you fine-grained control over the agent lifecycle. There are six types:
-
before_agent_callback
/after_agent_callback
: Triggered at the very beginning and end of the entire agent process.- Use Cases: Hydrating state with user data before the agent runs; logging results or updating metrics after it finishes.
-
before_model_callback
/after_model_callback
: Triggered just before and after calling the LLM.- Use Cases: Adding guardrails to check for inappropriate content before sending a request to the LLM; reformatting the LLM's response, censoring sensitive data, or replacing keywords.
-
before_tool_callback
/after_tool_callback
: Triggered before and after a tool is called.- Use Cases: Validating tool arguments or performing authorization checks before execution; modifying or logging the tool's results afterward.
You add callbacks to your agent definition and point them to a function. These functions receive a callback_context
object, which provides access to state
and other information.
Example 10: Sequential Workflows
A SequentialAgent
ensures that its sub-agents execute in a specific, predefined order. The first agent runs to completion, then the second, and so on.
This is perfect for tasks that have dependent steps. For example, a lead qualification pipeline:
1. Lead Validator Agent: Checks if the provided lead information is complete. Saves validation_status: "valid"
to state.
2. Lead Scorer Agent: If valid, this agent scores the lead based on criteria like budget and urgency. Saves lead_score: 8
to state.
3. Action Recommender Agent: Based on the score, this agent suggests the next steps (e.g., "Schedule a demo").
To create one, you import SequentialAgent
instead of the regular Agent
and list your sub-agents in the desired order of execution. State is shared between them using output_key
and state injection in the prompts.
Example 11: Parallel Workflows
A ParallelAgent
executes all its sub-agents simultaneously. This is ideal for speed when you have multiple independent tasks to perform.
A common pattern is to run several data-gathering agents in parallel and then have a final sequential agent synthesize the results. For example, a system analytics report:
1. Parallel Agents:
- CPU Agent
: Gathers CPU stats.
- Memory Agent
: Gathers RAM usage.
- Disk Agent
: Gathers storage information.
- Each saves its findings to a unique key in state
.
2. Synthesizer Agent (run sequentially after the parallel block):
- Reads the CPU, memory, and disk information from state
and compiles it into a single, well-formatted report.
This approach allows you to combine different workflow types to build efficient and powerful systems.
Example 12: Loop Workflows
A LoopAgent
is like a sequential agent on steroids. It repeatedly executes its sequence of sub-agents until a specific condition is met or a maximum number of iterations is reached. This enables agents to refine their work over and over, similar to a ReAct (Reason and Act) pattern.
For example, an agent that generates and refines a LinkedIn post:
1. Initial Post Generator (Sequential): Creates a first draft of the post and saves it to state
.
2. Loop Agent (with max_iterations: 5
):
- Post Reviewer Agent: Checks the draft against criteria (e.g., character count, tone). If it fails, it provides feedback and saves it to state
. If it passes, it calls a special exit_loop
tool.
- Post Refiner Agent: Reads the feedback from state
and revises the post, saving the new version back to state
.
The loop continues—review, refine, review, refine—until the post meets all criteria and the exit_loop
tool is called, or it hits the iteration limit. The exit_loop
functionality is achieved by calling tool_context.actions.escalate()
from within a tool.
This concludes our deep dive into Google's Agent Development Kit. You are now equipped with the knowledge to build everything from simple, single agents to complex, multi-agent workflows that can reason, collaborate, and solve problems iteratively.
Join the 10xdev Community
Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.