What You’ll Learn

By the end of this post, you’ll:

  • Understand what Google ADK is
  • Build a working AI agent from scratch
  • Understand ADK’s core architecture (App, Agent, Tools)

Prerequisites: Python 3.10+, basic Python knowledge, an API key for an LLM provider (Google, OpenAI, or Anthropic)

What is Google ADK?

Google ADK (Agent Development Kit) is an open-source, code-first toolkit for building AI agents. It’s Google’s answer to the growing complexity of agent development, designed to make building production-grade agents as straightforward as building a web API.

What Makes ADK Stand Out

ADK is built around a few core principles:

1. Model Agnostic Use any LLM provider (Google, OpenAI, Anthropic, local models) through LiteLLM integration. Switch providers with a single config change.

2. Production-Ready Features Built-In

  • Multi-agent orchestration: Sequential, Parallel, and Loop agents for complex workflows
  • Human-in-the-loop: First-class support for approval workflows
  • Safety guardrails: Before/after callbacks for policy enforcement
  • Observability: Native OpenTelemetry integration for tracing and monitoring

3. Flexible Deployment (Deployment Agnostic) Deploy anywhere - Cloud Run, Vertex AI, GKE, or your own infrastructure. ADK doesn’t lock you into a specific platform.

4. Simple Just three concepts to learn to get started: App, Agent, and Tools. Everything else builds on these fundamentals.

Core Architecture

ADK has three main concepts:

ADK core architecture

  1. App: The container that holds everything. Handles configuration, plugins, and exposes the API.

  2. Agent: The brain. Combines an LLM with instructions and tools to accomplish tasks.

  3. Tools: Functions the agent can call to interact with the world (APIs, databases, files, etc.).

Setting Up Your Environment

Let’s build your first agent. First, set up your project:

# Create a new parent directory. All ADK agents will be created in this directory
mkdir agents
cd agents

# Create a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install ADK
pip install google-adk

# Create the first agent
mkdir my_first_adk_agent
cd my_first_adk_agent

Configure Your API Key

If you don’t already have a Gemini API key, create a key in Google AI Studio on the API Keys page.

Create a .env file:

GOOGLE_API_KEY=your-google-api-key

Building Your First Agent

Create a file called agent.py:

"""
My First ADK Agent
A simple agent that can answer questions and do basic math.
"""

from google.adk.agents import LlmAgent
from google.adk.apps.app import App


# Step 1: Define your tools
def add_numbers(a: float, b: float) -> float:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number

    Returns:
        The sum of a and b
    """
    return a + b


def multiply_numbers(a: float, b: float) -> float:
    """Multiply two numbers together.

    Args:
        a: First number
        b: Second number

    Returns:
        The product of a and b
    """
    return a * b


def get_current_time() -> str:
    """Get the current date and time.

    Returns:
        Current date and time as a string
    """
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


# Step 3: Create your agent
assistant = LlmAgent(
    name="assistant",
    model='gemini-3-flash-preview',
    instruction="""You are a helpful assistant that can:
    - Answer general questions
    - Perform basic math calculations using the add_numbers and multiply_numbers tools
    - Tell the user what time it is using the get_current_time tool

    Always be friendly and helpful. When asked to do math, use the appropriate tool
    rather than calculating in your head.
    """,
    tools=[add_numbers, multiply_numbers, get_current_time]
)


# Step 4: Create your app
app = App(
    name="my_first_adk_agent",
    root_agent=assistant
)

That’s it! You’ve built an AI agent in about 50 lines of code.

Running Your Agent

ADK provides multiple ways to run your agent:

Note

Run these commands from the parent directory that contains your my-first-adk-agent/ folder. For example, if your agent is inside agents/my-first-adk-agent/, run adk web, adk run from the agents/ directory.

Option 1: Command Line

adk run my_first_adk_agent

ADK run sample interaction with agent

adk web

This opens a web UI where you can chat with your agent, see tool calls, and debug.

ADK web sample run

Option 3: API Server

adk api_server

This starts a FastAPI server and exposes agent’s API interface. The server would be started at http://localhost:8000. You should be able to use /list-apps, /run, etc. A few example invocations are given below.

List apps

curl -X GET "http://localhost:8000/list-apps" \
  -H "Content-Type: application/json"

Create a new session

curl -X POST "http://localhost:8000/apps/my_first_adk_agent/users/u_123/sessions/s_123"

Chat with the agent

curl -X POST "http://localhost:8000/run" \
  -H "Content-Type: application/json" \
  -d '{
    "appName": "my_first_adk_agent",  
    "userId": "u_123",      
    "sessionId": "s_123",    
    "newMessage": {
      "role": "user",
      "parts": [
        {
          "text": "What is the current time"
        }
      ]
    }
  }'

ADK API Server sample execution

Option 4: Programmatic

Create a file called test_agent.py:

"""
Test your agent programmatically
"""
import asyncio
from google.adk.runners import InMemoryRunner
from agent import assistant


async def main():
    # Create an in-memory runner (simplest option for testing)
    runner = InMemoryRunner(agent=assistant)

    # Test conversations
    test_messages = [
        "What time is it?",
        "What's 25 plus 17?",
        "Can you multiply 8 by 9?",
        "Hello! What can you help me with?"
    ]

    for message in test_messages:
        print(f"\n{'='*50}")
        print(f"User: {message}")
        print(f"{'='*50}")

        # Run the agent using run_debug (requires ADK v1.18.0+)
        response = await runner.run_debug(message, verbose=False)
        print(f"Agent: {response}")


if __name__ == "__main__":
    asyncio.run(main())

Run it:

python test_agent.py

Understanding What Just Happened

Let’s break down the key components:

1. Tools Are Just Functions

def add_numbers(a: float, b: float) -> float:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number

    Returns:
        The sum of a and b
    """
    return a + b

ADK automatically:

  • Extracts the function signature for the LLM
  • Parses the docstring for parameter descriptions
  • Handles calling the function when the LLM requests it

No decorators, no special classes, just Python functions.

2. The LLM Model

model = 'gemini-3-flash-preview'

We are using the Gemini 3 Flash model. ADK supports other LLM Providers via LiteLLM integration.

3. Agents Combine Everything

assistant = LlmAgent(
    name="assistant",          # Unique identifier
    model=model,               # The LLM to use
    instruction="...",         # System prompt
    tools=[...]                # Available tools
)

The LlmAgent class handles:

  • Sending messages to the LLM
  • Managing conversation history
  • Executing tool calls
  • Returning responses

4. The App is the Container

app = App(
    name="my_first_agent",
    root_agent=assistant
)

The App handles:

  • Configuration and plugins
  • Session management
  • Exposing the agent via API
  • Lifecycle management

Adding More Functionality

Let’s enhance our agent with a more useful tool - a weather lookup:

def get_weather(city: str) -> dict:
    """Get the current weather for a city.

    Args:
        city: The city name to get weather for

    Returns:
        Weather information including temperature and conditions
    """
    # In a real app, you'd call a weather API here
    # For demo purposes, we'll return mock data
    import random

    conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]

    return {
        "city": city,
        "temperature_f": random.randint(60, 85),
        "temperature_c": random.randint(15, 30),
        "conditions": random.choice(conditions),
        "humidity": f"{random.randint(30, 80)}%"
    }


# Add it to your agent's tools
assistant = LlmAgent(
    name="assistant",
    model='gemini-3-flash-preview',
    instruction="""You are a helpful assistant that can:
    - Answer general questions
    - Perform basic math calculations
    - Tell the user what time it is
    - Check the weather for any city

    When reporting weather, format it nicely for the user.
    """,
    tools=[add_numbers, multiply_numbers, get_current_time, get_weather]
)

Project Structure Best Practices

As your agent grows, organize your code like this:

my-adk-project/
├── .env                    # API keys (don't commit this!)
├── agent.py               # Agent definition and App
├── tools/
│   ├── __init__.py
│   ├── math_tools.py      # Math-related tools
│   ├── time_tools.py      # Time-related tools
│   └── weather_tools.py   # Weather tools
├── instructions/
│   └── assistant.txt      # Agent instructions (for longer prompts)
└── tests/
    └── test_agent.py      # Agent tests

Example tools/math_tools.py:

"""Math tools for the assistant agent."""


def add_numbers(a: float, b: float) -> float:
    """Add two numbers together."""
    return a + b


def multiply_numbers(a: float, b: float) -> float:
    """Multiply two numbers together."""
    return a * b


def divide_numbers(a: float, b: float) -> float:
    """Divide the first number by the second.

    Args:
        a: The dividend
        b: The divisor (must not be zero)

    Returns:
        The quotient of a divided by b
    """
    if b == 0:
        return {"error": "Cannot divide by zero"}
    return a / b

Example instructions/assistant.txt:

You are a helpful assistant with the following capabilities:

## Core Functions
- Answer general knowledge questions
- Perform mathematical calculations using available tools
- Provide current time information
- Check weather conditions for any city

## Behavior Guidelines
1. Always use tools for calculations - don't compute in your head
2. Be friendly and conversational
3. If you don't know something, say so honestly
4. When providing weather info, format it nicely

## Response Style
- Keep responses concise but helpful
- Use bullet points for lists
- Include relevant details without overwhelming the user

Updated agent.py:

"""Main agent definition."""

from pathlib import Path
from google.adk.agents import LlmAgent
from google.adk.apps.app import App

from tools.math_tools import add_numbers, multiply_numbers, divide_numbers
from tools.time_tools import get_current_time
from tools.weather_tools import get_weather


def load_instructions(filename: str) -> str:
    """Load agent instructions from a file."""
    path = Path(__file__).parent / "instructions" / filename
    return path.read_text()

assistant = LlmAgent(
    name="assistant",
    model='gemini-3-flash-preview',
    instruction=load_instructions("assistant.txt"),
    tools=[
        add_numbers,
        multiply_numbers,
        divide_numbers,
        get_current_time,
        get_weather
    ]
)

app = App(
    name="my_assistant",
    root_agent=assistant
)

Common Pitfalls and Solutions

1. “My tool isn’t being called”

Problem: The LLM ignores your tool.

Solution: Make sure your docstring clearly explains when to use the tool:

# Bad - vague description
def process_data(data: str) -> str:
    """Process the data."""
    pass

# Good - clear use case
def format_phone_number(phone: str) -> str:
    """Format a phone number into standard (XXX) XXX-XXXX format.

    Use this when the user provides a phone number that needs formatting,
    or when you need to validate/standardize a phone number.

    Args:
        phone: A phone number in any format (e.g., "1234567890", "123-456-7890")

    Returns:
        Formatted phone number like "(123) 456-7890"
    """
    pass

2. “Tool errors aren’t handled gracefully”

Solution: Return error information instead of raising exceptions:

def divide_numbers(a: float, b: float) -> dict:
    """Divide two numbers."""
    if b == 0:
        return {"error": "Cannot divide by zero", "suggestion": "Please provide a non-zero divisor"}
    return {"result": a / b}

3. “My agent is slow”

Solution:

  • Use a faster model for simple tasks (gemini-3-flash-preview vs gemini-3-pro-preview)
  • Make tools async for I/O operations
  • Use caching for repeated API calls

Complete Code

Here’s the final, complete code for your first agent:

"""
My First ADK Agent - Complete Example
"""

from datetime import datetime
from google.adk.agents import LlmAgent
from google.adk.apps.app import App


# Tools
def add_numbers(a: float, b: float) -> float:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number

    Returns:
        The sum of a and b
    """
    return a + b


def multiply_numbers(a: float, b: float) -> float:
    """Multiply two numbers together.

    Args:
        a: First number
        b: Second number

    Returns:
        The product of a and b
    """
    return a * b


def get_current_time() -> str:
    """Get the current date and time.

    Returns:
        Current date and time as a string
    """
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def get_weather(city: str) -> dict:
    """Get the current weather for a city.

    Args:
        city: The city name to get weather for

    Returns:
        Weather information including temperature and conditions
    """
    import random
    conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
    return {
        "city": city,
        "temperature_f": random.randint(60, 85),
        "conditions": random.choice(conditions)
    }


# Agent
assistant = LlmAgent(
    name="assistant",
    model='gemini-3-flash-preview',
    instruction="""You are a helpful assistant that can:
    - Answer general questions
    - Perform basic math (use add_numbers, multiply_numbers)
    - Tell the current time (use get_current_time)
    - Check weather for any city (use get_weather)

    Always use tools for calculations. Be friendly and helpful.
    """,
    tools=[add_numbers, multiply_numbers, get_current_time, get_weather]
)


# App
app = App(
    name="my_first_agent",
    root_agent=assistant
)

Run with:

adk web

Resources


📚 Series Navigation: This is Post 1 of the Google ADK Tutorial Series.
→ Next: LiteLLM Integration: Making Your ADK Agents Model-Agnostic