Autonomous AI Agents with Google's Agent Development Kit (ADK)
Build production-ready AI agents using Google's engineering-first framework with modular components, telemetry, and deep Gemini integration.
Most AI agent frameworks treat agents as glorified chatbots with tool access. Google's Agent Development Kit (ADK) takes a fundamentally different approach: agents are software systems with testing, telemetry, orchestration, and deployment patterns borrowed from production engineering.
ADK is model-agnostic but deeply integrated with Google's Gemini and Vertex AI. It's designed for enterprises building agents that need to scale, monitor, test, and deploy reliably—not hobbyist experiments that break in production.
ADK vs. Simple Prompts: The Engineering Difference
Understanding what makes ADK unique requires understanding how it differs from simple prompt-based agent frameworks. This isn't just about features—it's about philosophical approach.
🏗️ Software Engineering vs. Prompt Engineering
Simple Frameworks: Write a prompt, call an LLM, parse the response, maybe call a tool. If it breaks, add more instructions to the prompt and hope for the best.
ADK: Modular components (agents, tools, orchestrators), unit-testable functions, structured telemetry, version-controlled configuration, deployment pipelines, and monitoring dashboards. When it breaks, logs tell you exactly where and why.
ADK brings software engineering practices to AI agent development. Agents are composed of discrete, testable components. Tool calls have schemas and validation. Execution traces are captured automatically. State is managed explicitly rather than hidden in prompt context.
- Modularity: Components are isolated, reusable, and independently testable
- Separation of Concerns: The
Agentholds logic, but theRunnerholds the memory and execution power. - Testing: ADK agents can be unit tested, integration tested, and evaluated systematically
- Deployment: Agents deploy to Vertex AI with monitoring, versioning, and rollback capabilities
The difference between a demo and production isn't how well it works in ideal conditions—it's how gracefully it handles the unexpected.
Creating Your First ADK Agent
Let's build a simple ADK agent. Even at the most basic level, you'll see the engineering-first approach. We define the agent declaratively, but to actually run it, we use a Session Service to track memory and a Runner to execute the interaction.
# Step 1: Install Google ADK
pip install google-adk google-genai
# Step 2: Set your Google AI Studio API Key
import os
os.environ["GEMINI_API_KEY"] = "YOUR_API_KEY_HERE"
ADK explicitly separates the "brain" (the Agent) from the "conversation" (the Session). This ensures that memory doesn't accidentally leak between different users. Here is how we set it up properly:
from google.adk import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
# 1. Define the agent (The Brain)
assistant_agent = Agent(
name="research_assistant",
instruction="A research assistant that helps answer questions with reasoning.",
model="gemini-2.5-pro" # Note: older 1.5 models are deprecated!
)
# 2. Setup Session Service (The Memory Drawer)
session_service = InMemorySessionService()
# 3. Explicitly create the session first (Must await the coroutine)
await session_service.create_session(
app_name="research_app",
user_id="user_01",
session_id="session_01"
)
# 4. Instantiate the base Runner, linking our custom session service
runner = Runner(
app_name="research_app",
agent=assistant_agent,
session_service=session_service
)
# 5. Run the query through the runner!
query = "Explain quantum entanglement in simple terms, using an analogy."
user_message = types.Content(role="user", parts=[types.Part(text=query)])
event_stream = runner.run(
user_id="user_01",
session_id="session_01",
new_message=user_message
)
# 6. Stream the text output cleanly
for event in event_stream:
if event.content and event.content.parts:
for part in event.content.parts:
if part.text:
print(part.text, end="")
🤖 Agent Response:
Query: Explain quantum entanglement in simple terms, using an analogy.
Response:
Imagine you have a pair of "magic coins." These coins have a peculiar property: each pair is magically linked. They are guaranteed to land on opposite faces, but you don't know which will be which.
Now, you take one coin to the Moon, and I stay on Earth. At an agreed-upon time, you flip your coin. Let's say yours lands on Heads. Here is the magic: at that exact instant, my coin on Earth immediately becomes Tails.
Adding Tools: Building a Math Calculator
Let's upgrade our agent with function calling capabilities so it can perform real mathematics. ADK uses standard Python functions as tools.
Best Practice: Always use type hints (like num1: float) in your tool functions. This tells the LLM exactly what type of data it needs to pass into your function!
from google.adk.tools import FunctionTool
# Step 1: Define the calculator tool function
def calculator(operation: str, num1: float, num2: float) -> str:
"""
Perform basic arithmetic operations.
Args:
operation: One of 'add', 'subtract', 'multiply', 'divide'
num1: First number
num2: Second number
Returns:
Result of the calculation
"""
operations = {
'add': lambda a, b: a + b,
'subtract': lambda a, b: a - b,
'multiply': lambda a, b: a * b,
'divide': lambda a, b: a / b if b != 0 else "Error: Division by zero"
}
result = operations.get(operation, lambda a, b: "Invalid operation")(num1, num2)
return f"{num1} {operation} {num2} = {result}"
# Step 2: Pass the tool into the Agent configuration
agent_with_math = Agent(
name="math_assistant",
model="gemini-2.5-pro",
instruction="Use the calculator tool for math operations.",
tools=[
FunctionTool(calculator)
]
)
With just the `FunctionTool` wrapper, the ADK automatically reads your docstring and generates a strict JSON schema for the AI to follow. When the user asks a math question, the AI will intercept it, run the Python code, and then formulate an answer!
Adding Tools: Connecting a Live Weather API
Simulated data is nice, but real agents talk to the outside world. Let's create a second tool that fetches actual, real-time weather using the free Open-Meteo API.
This function will secretly do two things: First, it will convert the city name the user typed into latitude/longitude coordinates. Second, it will fetch the live temperature using those coordinates.
import requests
def get_live_weather(city: str) -> str:
"""
Get the current live weather for a given city.
Args:
city: The name of the city (e.g., 'Tokyo', 'Hyderabad')
"""
try:
# 1. Convert city name to latitude/longitude
geo_url = f"https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=en&format=json"
geo_response = requests.get(geo_url).json()
if not geo_response.get("results"):
return f"Could not find coordinates for city: {city}"
lat = geo_response["results"][0]["latitude"]
lon = geo_response["results"][0]["longitude"]
# 2. Fetch the live weather using the coordinates
weather_url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t=temperature_2m&temperature_unit=celsius"
weather_response = requests.get(weather_url).json()
current_temp = weather_response.get("current", {}).get("temperature_2m")
return f"The current live temperature in {city} is {current_temp}°C."
except Exception as e:
return f"Error fetching weather data: {str(e)}"
Now, by passing FunctionTool(get_live_weather) into our Agent, it has the ability to pull live data from the internet seamlessly.
Observability: Inside the Agent
ADK's telemetry system captures structured execution data that integrates with Google Cloud's monitoring stack. Let's examine what observability data ADK captures when our agent uses those new tools we just built.
📊 What ADK Captures Automatically
Request Traces: Every agent invocation with inputs, outputs, and timing
Tool Calls: Function name, parameters, results, and execution time
Model Interactions: Prompts sent, responses received, token counts
Decision Points: Why the agent chose specific tools or actions
Error Context: Stack traces, retry attempts, failure modes
📡 Under-the-Hood Tool Trace:
[Agent] Analyzing query: requires weather lookup AND calculation
[Tool Call 1] get_live_weather(city="Hyderabad")
[Tool Result 1] "The current live temperature in Hyderabad is 35.5°C."
[Tool Call 2] calculator(operation="multiply", num1=22.5, num2=4.0)
[Tool Result 2] "22.5 multiply 4.0 = 90.0"
[Agent] Synthesizing results...
This telemetry enables several critical production capabilities: performance monitoring (identify slow tools or inefficient prompts), cost tracking (track token usage per agent per task), and debugging (reproduce failures with full execution context).
Agents that can't explain their reasoning aren't ready for production. Transparency isn't optional—it's fundamental.
Production-Ready Agents
We've built agents with ADK that demonstrate the engineering-first approach Google takes to autonomous AI. Here's what makes ADK different from experimental frameworks:
1 Engineering Discipline
ADK brings software engineering practices to agent development: modular architecture with testable components, explicit session management, and deployment pipelines. Agents are software systems, not scripts.
2 Built-in Observability
Telemetry isn't an add-on—it's fundamental. Every agent execution generates structured trace data showing exactly what happened and why. This enables debugging, performance optimization, and compliance auditing.
Complete Working Code: Agent with Live Weather & Math
You can copy and paste this entire block into your IDE or Colab notebook to see the tools running together in real-time.
import os
import requests
from google.adk import Agent
from google.adk.tools import FunctionTool
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
# 0. Set API Key
os.environ["GEMINI_API_KEY"] = "YOUR_API_KEY_HERE"
# 1. Define Tools
def get_live_weather(city: str) -> str:
"""Get the current live weather for a given city."""
try:
geo_url = f"https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=en&format=json"
geo_response = requests.get(geo_url).json()
lat = geo_response["results"][0]["latitude"]
lon = geo_response["results"][0]["longitude"]
weather_url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t=temperature_2m&temperature_unit=celsius"
weather_response = requests.get(weather_url).json()
current_temp = weather_response.get("current", {}).get("temperature_2m")
return f"The current live temperature in {city} is {current_temp}°C."
except Exception as e:
return f"Error fetching weather data: {str(e)}"
def calculator(operation: str, num1: float, num2: float) -> str:
"""Perform basic arithmetic operations ('add', 'subtract', 'multiply', 'divide')."""
operations = {
'add': lambda a, b: a + b,
'subtract': lambda a, b: a - b,
'multiply': lambda a, b: a * b,
'divide': lambda a, b: a / b if b != 0 else "Error: Division by zero"
}
result = operations.get(operation, lambda a, b: "Invalid operation")(num1, num2)
return f"{num1} {operation} {num2} = {result}"
# 2. Create the Agent
agent_with_tools = Agent(
name="live_assistant",
model="gemini-2.5-pro",
instruction="Use the calculator for math operations and get_live_weather for real-time weather.",
tools=[
FunctionTool(calculator),
FunctionTool(get_live_weather)
]
)
# 3. Setup Session and Runner
session_service = InMemorySessionService()
await session_service.create_session(
app_name="live_tool_app",
user_id="user_01",
session_id="session_live_01"
)
runner = Runner(
app_name="live_tool_app",
agent=agent_with_tools,
session_service=session_service
)
# 4. Execute the Query
query = "What is the live temperature right now in Hyderabad, India? Also, calculate 22.5 multiplied by 4."
user_message = types.Content(role="user", parts=[types.Part(text=query)])
print(f"Query: {query}\n\nResponse:\n")
event_stream = runner.run(
user_id="user_01",
session_id="session_live_01",
new_message=user_message
)
# 5. Print out the response cleanly
for event in event_stream:
if event.content and event.content.parts:
for part in event.content.parts:
if part.text:
print(part.text, end="")
You've learned to build production-ready AI agents with Google's ADK. Welcome to production-grade AI development!