Skip to Content

Google ADK Tutorial: From Basic Agents to Production-Ready AI Orchestration

Google ADK Tutorial

A comprehensive guide to building Multi-Agent AI Systems from Scratch

1. Introduction to Google ADK

Welcome to the Google Agent Development Kit (ADK) user guide. Traditional AI chatbots are often linear and reactive—you ask a question, and they respond without reasoning across complex real-world actions. Multi-Agent systems solve this by breaking tasks down among specialized agents.

In this guide, you will learn how to go from a simple conversational agent to a fully orchestrated team of AI agents capable of planning an elaborate trip to Tokyo!

Prerequisites: You need Python 3.9+ and a Google Cloud billing-enabled project (or Google AI Studio API Keys).

Installation

pip install google-adk
pip install python-dotenv

2. Creating Your First Agent

Every ADK agent requires three core components: name, model, and instruction.

Basic Flight Agent Code

import asyncio
from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
from dotenv import load_dotenv

# Load API keys (e.g., GOOGLE_API_KEY) from .env
load_dotenv()

flight_agent = Agent(
    name="flight_agent",
    model="gemini-2.5-flash",
    description="Tell me the optimal route between flights.",
    instruction="You are a helpful travel assistant. Help the user analyze flight setups."
)

async def main():
    runner = InMemoryRunner()
    await runner.run_debug(
        agent=flight_agent,
        prompt="How many layovers are there between New York and Tokyo?"
    )

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

3. Adding Memory and Context

Without memory, an agent starts from zero with every input. By adding sessions and state, the agent can remember user preferences (like your favorite travel destinations).

LLMAgent with Session Service

from google.adk.agents import LlmAgent
from google.adk.services import InMemorySessionService
from google.adk.tools import preload_memory
from google.adk.runners import InMemoryRunner

# We upgrade to LlmAgent to better leverage state
context_flight_agent = LlmAgent(
    name="flight_agent",
    model="gemini-2.5-flash",
    description="A flight agent with memory bridging.",
    instruction="You are a travel assistant. Remember the users departure city.",
    tools=[preload_memory]
)

# Start a tracked session with memory
session_service = InMemorySessionService()
runner = InMemoryRunner(session_service=session_service, app_name="TravelApp")

# Agent conversations are appended and recalled seamlessly

4. Empowering Agents with Tools

Tools are like appliances for an AI "chef". They allow the LLM to access live web data, calculate formulas, or interact with external APIs.

A. Built-In Tools (Search & Code Execution)

from google.adk.tools import google_search
from google.adk.code_executors import BuiltInCodeExecutor

# Agent with Google Search access
search_agent = Agent(
    name="sightseeing_expert",
    model="gemini-2.5-flash",
    instruction="Use Google Search to find up-to-date sightseeing spots.",
    tools=[google_search]
)

# Agent with Code Execution for budgeting
budget_agent = Agent(
    name="finance_expert",
    model="gemini-2.5-flash",
    instruction="Calculate precise budget expenses.",
    code_executor=BuiltInCodeExecutor()
)

B. Custom Python Tools

You can wrap any standard Python function with ADK so your agent can dynamically invoke it using the docstrings as a guide.

from google.adk.tools import FunctionTool
import requests

def get_weather(latitude: float, longitude: float) -> str:
    """Fetch the exact current weather mapping for specific coordinates."""
    # (Mocked API call)
    return "The current weather is 17.9°C"

get_weather_tool = FunctionTool(get_weather)

weather_flight_agent = Agent(
    name="flight_agent",
    model="gemini-2.5-flash",
    description="Use get_weather to fetch conditions.",
    instruction="If user asks about weather via lat/longitude, call get_weather tool.",
    tools=[get_weather_tool] 
)

5. Multi-Agent Orchestration

A monolith agent trying to handle hotels, flights, and activities simultaneously becomes fragile. Instead, ADK uses Coordinators resolving sub-agents.

Parallel vs Sequential Agents

Running agents in parallel speeds up queries that don't depend on each other. Sequential agents wait for prior dependencies.

from google.adk.agents import Agent, ParallelAgent, SequentialAgent
from google.adk.tools import google_search

# 1. Define distinct Specialists
flight_specialist = Agent(name="flight_specialist", model="gemini-2.5-flash", instruction="Find routes.")
hotel_expert = Agent(name="hotel_expert", model="gemini-2.5-flash", tools=[google_search], instruction="Suggest hotels.")
activity_curator = Agent(name="activity_curator", model="gemini-2.5-flash", tools=[google_search], instruction="Plan tourism activities.")

# 2. Parallel Coordination (run simultaneously)
parallel_coordinator = ParallelAgent(
    name="parallel_coordinator",
    description="Fetch flights, sightseeing, and hotel info in parallel.",
    sub_agents=[flight_specialist, hotel_expert, activity_curator]
)

# 3. Sequential Coordination (Master Trip Planner)
travel_planner_root = SequentialAgent(
    name="travel_planner",
    description="Orchestrate the trip plan by running parallel checks then summarizing findings.",
    sub_agents=[parallel_coordinator]
)

6. Production Safety & Deployment

Moving from a demo to production requires serious safety considerations inside your pipeline.

A. Implementing Filters

Input Filters defend against prompt injection (e.g. "ignore previous instructions"). Output Filters sanitize AI hallucinations before the user sees them.

def input_filter(user_input: str) -> bool:
    """Return False to block a prompt before it reaches the LLM."""
    if "ignore previous instructions" in user_input.lower():
        return False
    return True

def output_filter(response: str) -> str:
    """Sanitize the LLM response replacing harmful chunks."""
    if "forbidden phrase" in response.lower():
        return "Sorry, I cannot answer that."
    return response

B. Rate Limiting and Audit Logging

import json
from datetime import datetime, timedelta

# Audit logging 
def log_event(log_entry):
    timestamp = datetime.now().isoformat()
    entry = {"timestamp": timestamp, "log": log_entry}
    with open("audit.log", "a") as f:
        f.write(json.dumps(entry) + "\n")

# Rate limiting snippet logic using session counting
# If session request limit > max_requests in time_delta -> Block action

7. Complete Functional Example

Here is a fully integrated, runnable Python script that combines Memory, Custom Tools, Sequential & Parallel Orchestration, and Safety Filters into one cohesive Multi-Agent System.

import asyncio
import json
from datetime import datetime
from google.adk.agents import Agent, ParallelAgent, SequentialAgent
from google.adk.services import InMemorySessionService
from google.adk.tools import google_search, FunctionTool
from google.adk.runners import InMemoryRunner
from dotenv import load_dotenv

# 1. Setup & Safety Environment
load_dotenv()

def input_filter(user_input: str) -> bool:
    """Security block against prompt injections."""
    if "ignore previous instructions" in user_input.lower():
        print("[SECURITY] Blocked malicious input.")
        return False
    return True

def log_event(event_type: str, details: str):
    """Audit logging for compliance."""
    entry = {"timestamp": datetime.now().isoformat(), "type": event_type, "details": details}
    with open("travel_audit.log", "a") as f:
        f.write(json.dumps(entry) + "\n")

# 2. Custom Tools Definition
def get_weather(location: str) -> str:
    """Fetch current weather for a specific location."""
    # Mocking real API for the example
    return f"The current weather in {location} is sunny and 22°C."

weather_tool = FunctionTool(get_weather)

# 3. Define Specialist Agents
flight_agent = Agent(
    name="flight_specialist",
    model="gemini-2.5-flash",
    instruction="Find the best flight routes. Extract the arrival city for other agents."
)

hotel_agent = Agent(
    name="hotel_expert",
    model="gemini-2.5-flash",
    tools=[google_search],
    instruction="Suggest top-rated hotels in the arrival city."
)

activity_agent = Agent(
    name="activity_curator",
    model="gemini-2.5-flash",
    tools=[weather_tool],
    instruction="Plan activities based on the weather tool output."
)

# 4. Multi-Agent Orchestration
# Run Hotel and Activity searches in parallel after Flight is found
destination_experts = ParallelAgent(
    name="destination_experts",
    description="Find hotels and activities simultaneously.",
    sub_agents=[hotel_agent, activity_agent]
)

# Master orchestrator ensuring sequential logic
travel_master = SequentialAgent(
    name="travel_master",
    description="Coordinate the full trip.",
    sub_agents=[flight_agent, destination_experts],
    instruction="First find flights, then let destination experts find hotels and activities."
)

# 5. Main Execution Runner
async def main():
    user_prompt = "Plan a 3-day trip to Tokyo from New York."
    
    # Run Safety Check
    if not input_filter(user_prompt):
        return

    log_event("REQUEST", user_prompt)
    print("Starting ADK Multi-Agent System...")

    # Initialize Session Memory & Runner
    session_service = InMemorySessionService()
    runner = InMemoryRunner(session_service=session_service, app_name="TravelApp")
    
    # Execute Master Agent
    response = await runner.run(
        agent=travel_master,
        prompt=user_prompt
    )
    
    log_event("RESPONSE", response)
    print(f"\n--- Final Itinerary ---\n{response}")

if __name__ == "__main__":
    # Execute the async loop
    asyncio.run(main())
in AI
Zero to Hero: Building a Multi-Tier Cyber Security Operations Center with Google ADK