Master the Agent Development Kit (ADK) through 10 structured, progressive modules. Learn to build agents, compose tools, orchestrate multi-agent systems, and implement advanced patterns using local Ollama inference.
Project Status: Complete. All 10 modules tested and documented. Estimated learning time: 2-3 hours per module. Total commitment: 20-30 hours for comprehensive mastery.
Every ADK agent consists of three fundamental components:
%%{init: {theme: 'base', themeVariables: {primaryColor: '#2196F3', primaryBorderColor: '#1976D2', primaryTextColor: '#fff', fontSize: '16px'}}}%%
graph LR
A["Model<br/>(LLM Engine)"] -->|Reasoning| B["Orchestration<br/>(ADK Framework)"]
C["Tools<br/>(Functions)"] -->|Invokes| B
B -->|Generates| D["Action/Response"]
style A fill:#2196F3,stroke:#1565C0,color:#fff
style B fill:#FF9800,stroke:#E65100,color:#fff
style C fill:#9C27B0,stroke:#6A1B9A,color:#fff
style D fill:#4CAF50,stroke:#2E7D32,color:#fff
Every agent in ADK is built on the BaseAgent foundation. ADK provides three distinct categories to build sophisticated applications, each serving different needs:
LLM agents use Large Language Models as their core reasoning engine. They understand natural language, reason about problems, plan solutions, generate responses, and dynamically decide which tools to use. Ideal for flexible, language-centric tasks where the agent needs to make intelligent decisions.
Characteristics:
- Non-deterministic (decisions vary based on input and reasoning)
- Dynamic tool selection (LLM chooses when/which tool to call)
- Best for: Q&A, content generation, intelligent assistants
Example:
root_agent = LlmAgent(
model=LiteLlm(model="ollama_chat/qwen2.5:3b"),
instruction="You are a helpful assistant. Use available tools intelligently.",
tools=[search, calculate, summarize]
)Workflow agents control the execution flow of other agents in predefined, deterministic patterns. They don't use an LLM for flow control—instead, they enforce structured orchestration without dynamic decision-making.
Workflow Patterns:
- Sequential: Tool A → Tool B → Tool C (linear pipeline)
- Parallel: Tool A, B, C run simultaneously (gather results)
- Loop: Tool A → evaluate → retry if needed (iterative refinement)
Characteristics:
- Deterministic (execution order is guaranteed)
- Explicit control flow (you define the pattern)
- Best for: Multi-step pipelines, data processing, orchestration
Example:
pipeline = SequentialAgent(
name="DataPipeline",
sub_agents=[
fetch_agent, # Step 1
process_agent, # Step 2
save_agent # Step 3
]
)Custom agents allow you to implement unique operational logic, specific control flows, or specialized integrations not covered by standard agent types. Built by extending BaseAgent directly.
Characteristics:
- Can be either deterministic or non-deterministic
- Custom implementation of perception-thinking-action loop
- Best for: Specialized integrations, domain-specific workflows, novel patterns
Example:
from google.adk.agents import BaseAgent
class DatabaseAgent(BaseAgent):
def __init__(self, db_connection):
self.db = db_connection
def run(self, query: str):
# Custom logic: parse query, validate, execute
return self.db.execute(query)| Feature | LLM Agent | Workflow Agent | Custom Agent |
|---|---|---|---|
| Primary Function | Reasoning, Generation, Tool Use | Controlling Agent Execution Flow | Implementing Unique Logic/Integrations |
| Core Engine | Large Language Model (LLM) | Predefined Logic (Sequence, Parallel, Loop) | Custom Code |
| Determinism | Non-deterministic (Flexible) | Deterministic (Predictable) | Can be either, based on implementation |
| Primary Use | Language tasks, Dynamic decisions | Structured processes, Orchestration | Tailored requirements, Specific workflows |
| Tool Selection | LLM chooses dynamically | Enforced by workflow structure | Custom implementation |
| When to Use | Questions, creative tasks, intelligent routing | Guaranteed execution order, pipelines | Novel patterns, domain-specific logic |
All agent types in ADK extend from BaseAgent, providing a unified interface:
BaseAgent (Foundation)
├── LlmAgent (Language-based reasoning)
│ ├── Sub-agents for multi-agent systems
│ └── Can delegate to other agents
├── SequentialAgent (Sequential orchestration)
├── ParallelAgent (Parallel orchestration)
├── LoopAgent (Iterative orchestration)
└── Custom Agents (Your implementation)
The true power of ADK comes from combining agent types. Complex applications frequently employ multi-agent architectures where:
- LLM Agents handle intelligent, language-based task execution and decision-making
- Workflow Agents manage the overall process flow using deterministic patterns
- Custom Agents provide specialized capabilities or rules for unique integrations
This allows you to build sophisticated, hierarchical systems where specialized agents collaborate toward shared goals. See Module 09 for a detailed multi-agent example.
An agent is an autonomous system that perceives its environment, reasons about its state, and takes actions to achieve goals. In ADK, agents are composed of three fundamental components:
- Model: The reasoning engine (Large Language Model)
- Tools: Functions and capabilities the agent can invoke
- Orchestration: The framework managing the perception-thinking-action loop
This curriculum teaches you to build, test, and deploy agents that effectively combine these components.
For foundational concepts, see ADK LLM Agents documentation.
- Python 3.9 or higher (
python3 --version) - Ollama running locally (download from ollama.ai)
- Basic terminal command familiarity
- Approximately 4GB available RAM for local LLM inference
Refer to ADK Python Getting Started for complete setup instructions.
# 1. Install dependencies
cd adk-project
.venv/bin/pip install litellm google-adk
# 2. Pull a fast, tool-capable model
ollama pull qwen2.5:3b
# 3. Start the exploration environment
cd explorations
export OLLAMA_API_BASE="http://localhost:11434"
.venv/bin/adk webOpen http://localhost:8000 in your browser. ADK auto-discovers all root_agent definitions across modules and displays them as interactive tabs.
| Module | Concept | Duration | Reference |
|---|---|---|---|
| 01 — Hello Agent | Agent anatomy: model, name, description, instruction | 15 min | LLM Agents |
| 02 — Single Tool | Python function as agent capability | 15 min | Multi-Tool Agent |
| 03 — Multi Tool | Tool selection and sequencing | 20 min | Multi-Tool Agent |
| 04 — Input Schema | Pydantic-based input validation | 15 min | LLM Agents |
| 05 — Output Schema | Deterministic structured JSON responses | 20 min | LLM Agents |
| 06 — Session State | Application state management | 20 min | Sessions |
| 07 — Callbacks | Hooks for tool execution lifecycle | 15 min | Callbacks |
| 08 — Workflow Agents | Sequential, parallel, loop orchestration | 25 min | Workflow Agents |
| 09 — Multi-Agent Systems | Hierarchical agent composition | 25 min | LLM Agents |
| 10 — Advanced Tools | External system integration | 20 min | Custom Tools |
Total Commitment: 3+ hours of hands-on exploration and experimentation.
Learn agent anatomy and how tools work.
- 01 — Hello Agent → Understand agent parameters and configuration
- 02 — Single Tool → Write your first tool function
- 03 — Multi Tool → Watch agent select between multiple tools
Key Concept: A tool is simply a Python function with a docstring. The docstring tells the LLM what the tool does and when to use it.
Learn to control inputs, outputs, and application state.
- 04 — Input Schema → Validate user input with Pydantic
- 05 — Output Schema → Force structured JSON responses
- 06 — Session State → Track user state across conversation turns
Key Concept: Schemas enforce type safety. Input schemas validate before processing. Output schemas guarantee deterministic, parseable responses.
Build complex, production-ready systems.
- 07 — Callbacks → Debug agent reasoning with execution hooks
- 08 — Workflow Agents → Orchestrate multi-step flows deterministically
- 09 — Multi-Agent Systems → Delegate to specialized sub-agents
- 10 — Advanced Tools → Integrate with external systems securely
Key Concept: Complex agents are built by composing simpler patterns. Workflows guarantee execution order. Multi-agent systems scale by specialization.
Concept: An LLM agent is defined by four parameters: model, name, description, and instruction.
from google.adk.agents import LlmAgent
from google.adk.models.lite_llm import LiteLlm
root_agent = LlmAgent(
model=LiteLlm(model="ollama_chat/qwen2.5:3b"),
name="hello_agent",
description="A helpful pirate assistant",
instruction="You are a pirate. Answer all questions like a pirate."
)Why it Matters: Your instruction parameter shapes agent personality. The same model with different instructions produces different behavior.
Learn More: ADK LLM Agents
Concept: A Python function with a docstring becomes an agent tool. The docstring is critical—it's what the LLM reads to understand the tool's purpose.
def roll_die(sides: int) -> int:
"""Roll a die with N sides and return the result (1 to N)."""
return random.randint(1, sides)
root_agent = LlmAgent(
model=...,
tools=[roll_die]
)Why it Matters: Tool docstrings are the interface between your code and the LLM's reasoning. Clear, specific docstrings lead to correct tool usage.
Learn More: Multi-Tool Agent Tutorial
Concept: When an agent has multiple tools, it must reason about which tool to use and when. Tool chaining occurs when one tool's output becomes input to another.
# Agent has three tools available
tools=[add, multiply, is_prime]
# Instruction guides tool selection
instruction="""
When asked to calculate:
1. Perform the math operation (use add/multiply)
2. If the result seems interesting, check if it's prime
"""Why it Matters: Multi-tool agents solve complex problems through reasoning and sequencing.
Try This: Ask "Multiply 3 by 4, then check if it's prime" and watch the agent call multiply first, then is_prime.
Learn More: Multi-Tool Agent Tutorial
Concept: Pydantic models validate structured input before the agent processes it.
from pydantic import BaseModel
from typing import Optional
class MealOrder(BaseModel):
dish: str
quantity: int
spice_level: Optional[str] = None
root_agent = LlmAgent(
model=...,
input_schema=MealOrder
)Why it Matters: Type safety. Your agent code receives validated data, not raw strings.
Concept: Force agent responses to be valid JSON matching a schema. Responses are always deterministic and parseable.
from pydantic import BaseModel
class ProductInfo(BaseModel):
name: str
price: float
quantity: int
in_stock: bool
root_agent = LlmAgent(
model=...,
output_schema=ProductInfo
)Why it Matters: Parse responses as Python objects without string matching. No guessing about response format.
result = agent.run("iPhone 15 Pro for $999, 5 in stock")
print(result.price) # 999.0 (typed float, not string)
print(result.in_stock) # True (typed bool)Learn More: LLM Agents Documentation
Concept: Your code controls a Python dictionary separate from the LLM's conversation history. Use session state for business logic decisions.
session_state = {
"user_name": None,
"total_requests": 0,
"subscription_tier": "free"
}
# Your code extracts facts and updates state
import re
def process_message(message: str):
session_state["total_requests"] += 1
match = re.search(r"my name is\s+([a-zA-Z\s]+?)(?:\.|,|$)", message, re.IGNORECASE)
if match:
session_state["user_name"] = match.group(1).strip()
# Business logic: upgrade tier after 5 requests
if session_state["total_requests"] > 5:
session_state["subscription_tier"] = "premium"Why it Matters: Session state lets you make programmatic decisions based on accumulated facts, separate from the agent's own conversation memory.
Learn More: ADK Sessions
Concept: Hooks that execute before/after tool calls, for logging, validation, or modification.
def log_tool_call(tool_name, args):
"""Log before each tool call."""
print(f"Calling {tool_name} with args: {args}")
def validate_tool_call(tool_name, args):
"""Reject certain tools."""
if tool_name == "delete_file":
return False # Agent cannot use this tool
return True
root_agent = LlmAgent(
model=...,
callbacks={
"before_tool_call": log_tool_call,
"validate_tool_call": validate_tool_call
}
)Why it Matters: Gain visibility into agent reasoning. Intercept and modify tool behavior for security and monitoring.
Learn More: ADK Callbacks
Concept: Guarantee execution order using specialized orchestration agents. Unlike multi-tool (agent chooses), workflows enforce deterministic sequencing.
Sequential Workflow: Execute tools one after another. Each step receives the output of the previous step.
from google.adk.agents import SequentialAgent, LlmAgent
step1 = LlmAgent(name="Fetch", instruction="Fetch data")
step2 = LlmAgent(name="Process", instruction="Process the fetched data")
step3 = LlmAgent(name="Save", instruction="Save results")
pipeline = SequentialAgent(
name="DataPipeline",
sub_agents=[step1, step2, step3]
)Parallel Workflow: Execute multiple agents simultaneously, then combine results.
Loop Workflow: Execute an agent, evaluate, and retry if needed.
Why it Matters: Workflows are predictable. Multi-tool agents let the LLM choose order (sometimes unreliably). Workflows enforce control flow.
Learn More: ADK Workflow Agents
Concept: Hierarchical agent composition. A coordinator agent delegates requests to specialized sub-agents based on their descriptions and capabilities.
# Define specialist agents
math_agent = LlmAgent(
name="math_agent",
description="Solves math problems and calculations",
instruction="You are a math expert. Solve step by step."
)
story_agent = LlmAgent(
name="story_agent",
description="Writes creative fiction and stories",
instruction="You are a creative writer."
)
# Create coordinator
coordinator = LlmAgent(
name="coordinator",
description="Routes user requests to the best specialist",
instruction="""
You are a coordinator. When a user asks a question:
1. Identify which specialist is best suited
2. Transfer the request using transfer_to_agent(agent_name='specialist_name')
3. Let the specialist handle and return the answer
""",
sub_agents=[math_agent, story_agent]
)Agent Hierarchy:
- The coordinator is the parent agent
- math_agent and story_agent are sub-agents (children)
- ADK automatically sets
parent_agenton each sub-agent - An agent can only have one parent (single parent rule)
Why it Matters:
- Modularity: Each agent specializes in a domain
- Scalability: Add more specialists without coordinator complexity
- Maintainability: Sub-agents can be tested independently
- Efficiency: Reduces token consumption (smaller specialized models)
Delegation Mechanism: The coordinator uses transfer_to_agent() to invoke specialists. ADK's AutoFlow framework handles the delegation automatically.
Use Cases:
- Customer support: Route to billing_agent, technical_support_agent, sales_agent
- Content creation: Route to blog_writer_agent, social_media_agent, email_agent
- Research: Route to literature_search_agent, data_analysis_agent, synthesis_agent
%%{init: {theme: 'base', themeVariables: {primaryColor: '#FF9800', primaryBorderColor: '#E65100', primaryTextColor: '#fff', fontSize: '16px'}}}%%
graph TD
User["User Query"] --> Coordinator["Coordinator Agent<br/>(Router)"]
Coordinator -->|transfer_to_agent| Math["Math Agent<br/>(Specialist)"]
Coordinator -->|transfer_to_agent| Story["Story Agent<br/>(Specialist)"]
Coordinator -->|transfer_to_agent| Trivia["Trivia Agent<br/>(Specialist)"]
Math --> Result1["Calculation<br/>Result"]
Story --> Result2["Creative<br/>Response"]
Trivia --> Result3["Knowledge<br/>Response"]
Result1 --> Final["Final Answer"]
Result2 --> Final
Result3 --> Final
style Coordinator fill:#FF9800,stroke:#E65100,color:#fff,stroke-width:3px
style Math fill:#2196F3,stroke:#1565C0,color:#fff
style Story fill:#9C27B0,stroke:#6A1B9A,color:#fff
style Trivia fill:#4CAF50,stroke:#2E7D32,color:#fff
style Final fill:#FFC107,stroke:#FFA000,color:#000,stroke-width:2px
style User fill:#757575,stroke:#424242,color:#fff
Concept: Integration with external systems. Secure file I/O, code execution, and system integration.
from pathlib import Path
SAFE_DIR = Path.cwd()
def _validate_path(file_path: str) -> Path:
"""Prevent directory traversal attacks."""
path = (SAFE_DIR / file_path).resolve()
if not str(path).startswith(str(SAFE_DIR.resolve())):
raise ValueError(f"Path {file_path} outside allowed directory")
return path
def read_file(file_path: str) -> str:
"""Read and return file contents safely."""
try:
path = _validate_path(file_path)
with open(path, 'r') as f:
return f.read()
except ValueError as e:
return f"Access denied: {e}"
root_agent = LlmAgent(
model=...,
tools=[read_file, write_file, list_files]
)Why it Matters:
- Real system integration (files, databases, APIs)
- Security: Always validate paths to prevent traversal attacks
- Error handling: Return descriptive errors for agent visibility
Learn More: ADK Custom Tools
ADK supports multiple model providers. For local exploration with Ollama:
Fast Model (Recommended for Learning):
ollama pull qwen2.5:3b
# Export: OLLAMA_API_BASE="http://localhost:11434"
# Use: model=LiteLlm(model="ollama_chat/qwen2.5:3b")Powerful Model (For Complex Tasks):
ollama pull gemma4:latest
# Use: model=LiteLlm(model="ollama_chat/gemma4:latest")Learn more:
# Required for Ollama-based models
export OLLAMA_API_BASE="http://localhost:11434"
# Make permanent (add to ~/.zshrc or ~/.bashrc)
echo 'export OLLAMA_API_BASE="http://localhost:11434"' >> ~/.zshrc
source ~/.zshrc
# Verify it's set
echo $OLLAMA_API_BASEpython3 --version # Must be 3.9 or higher
ollama --version # Ollama installed
ollama ps # Ollama daemon running
curl http://localhost:11434/api/tags # API is respondingcd adk-project
source .venv/bin/activate # Activate virtual environment
.venv/bin/pip install --upgrade pip setuptools wheel
.venv/bin/pip install google-adk litellm# Pull fast model
ollama pull qwen2.5:3b
# Pull powerful model (optional)
ollama pull gemma4:latest
# Verify models are installed
ollama listexport OLLAMA_API_BASE="http://localhost:11434"
# Persist across sessions
echo 'export OLLAMA_API_BASE="http://localhost:11434"' >> ~/.zshrccd explorations
.venv/bin/adk webNavigate to http://localhost:8000. All 10 modules appear as interactive tabs.
Ollama daemon is not running.
ollama serve
# Verify: curl http://localhost:11434/api/tagsModel has not been pulled.
ollama pull qwen2.5:3b
ollama list # Verify installationYou're using a high-capability model (gemma4, ~10 seconds). Switch to qwen2.5:3b (~2 seconds) in agent.py:
model=LiteLlm(model="ollama_chat/qwen2.5:3b")Tool docstring is missing or vague. Make it specific:
def calculate_tax(amount: float, rate: float) -> float:
"""Calculate tax on an amount. Use when asked for tax calculation."""
return amount * rateADK not installed or virtual environment not activated.
source .venv/bin/activate
.venv/bin/pip install google-adk litellmExport the environment variable:
export OLLAMA_API_BASE="http://localhost:11434"
echo $OLLAMA_API_BASE # Verify- Multi-Agent + Output Schema: Specialists return deterministic JSON
- Session State + Callbacks: Comprehensive user journey tracking
- Workflow + Advanced Tools: Complex multi-step file processing pipelines
- Combine learned patterns for real use cases
- Deploy as REST API:
adk api_server - Implement error handling and monitoring
- Add long-term memory with vector embeddings
- Custom agents inheriting from BaseAgent
- MCP server integration
- Vector databases for retrieval-augmented generation
- Multi-language support (Java, Go, TypeScript)
Each module is completely independent:
explorations/
├── 01_hello_agent/
│ ├── agent.py # Agent configuration: model, name, description, instruction
│ └── README.md # Module-specific concept guide
├── 02_single_tool/
│ ├── agent.py # Single tool: function with docstring
│ └── README.md
├── 03_multi_tool/
│ ├── agent.py # Multiple tools: agent reasoning and selection
│ └── README.md
├── 04_input_schema/
│ ├── agent.py # Input validation with Pydantic
│ └── README.md
├── 05_output_schema/
│ ├── agent.py # Structured JSON output enforcement
│ └── README.md
├── 06_session_state/
│ ├── agent.py # Application state management
│ └── README.md
├── 07_callbacks/
│ ├── agent.py # Tool execution hooks and monitoring
│ └── README.md
├── 08_workflow_agents/
│ ├── agent.py # Deterministic execution orchestration
│ └── README.md
├── 09_multi_agent/
│ ├── agent.py # Hierarchical agent delegation
│ └── README.md
├── 10_advanced_tools/
│ ├── agent.py # External system integration and security
│ └── README.md
└── __init__.py # Package initialization
ADK's auto-discovery loads all root_agent definitions at startup. Each module appears as an interactive tab in the web interface. Modify and test modules independently.
-
Read the Module README First
- Explains concepts before code
- Identifies common mistakes
- Provides experimentation suggestions
-
Modify and Experiment
- Change instruction parameters
- Add or remove tools
- Test edge cases
-
Restart Web Interface
- ADK loads agents at startup
- Changes require restart:
adk web
-
Choose Appropriate Models
- qwen2.5:3b: Fast iteration (2 seconds)
- gemma4:latest: Complex reasoning (10+ seconds, more capable)
-
Test Independently
- Each module tab operates independently
- Cross-module dependencies are minimal
Getting Started:
Agents & Core Concepts:
Tools & Integration:
Advanced Features:
Model Configuration:
- Framework: Google Agent Development Kit (ADK)
- Language: Python 3.9+
- Local LLM: Ollama
- Model Provider: LiteLLM (supports 100+ LLM providers)
- Estimated Learning Time: 20-30 hours total
- Status: Complete and tested



