Google Agent Development Kit (ADK) Guide
Overview
ADK is Google's open-source framework for building AI agents powered by Gemini models. It supports single-agent and multi-agent architectures with built-in tool integration, state management, callbacks, guardrails, and deployment options.
Critical Rules
- Every agent package MUST have
__init__.pythat imports the agent module:from . import agent - Entry point MUST be
root_agent-- a module-level variable inagent.py(Python). Notagent, notmy_agent. - Set
GOOGLE_API_KEYin.envor configure Vertex AI credentials before running. - Always set
max_iterationsonLoopAgentto prevent unbounded execution. - One agent = one responsibility. Split agents with 5+ tools into specialists.
- Use
output_key+output_schemafor reliable data flow between agents -- not free text.
Documentation & Resources
For up-to-date API references and detailed guides beyond this skill, always consult:
- ADK Docs Index: https://google.github.io/adk-docs/llms.txt
- Official Samples: https://github.com/google/adk-samples (Python, Java, TypeScript)
Supported Languages
| Language | Package | Install |
|---|---|---|
| Python | google-adk | pip install google-adk |
| Java | com.google.adk:google-adk | Maven/Gradle |
| Go | google.golang.org/adk | go get |
| TypeScript | @google/adk | npm install @google/adk |
This guide shows Python examples. For Java, Go, and TypeScript patterns, see references/multi-language.md.
Quick Reference
| Task | Approach |
|---|---|
| Single agent | Agent or LlmAgent with tools and instructions |
| Sequential pipeline | SequentialAgent with ordered sub_agents |
| Parallel execution | ParallelAgent with independent sub_agents |
| Iterative refinement | LoopAgent with max_iterations or checker agent |
| Agent-as-tool | Wrap agent with AgentTool for on-demand delegation |
| Remote agent (A2A) | RemoteA2aAgent + to_a2a() for cross-service agents |
| Custom tools | Python functions with type hints + docstrings |
| Structured output | Pydantic model via output_schema + output_key |
| State management | callback_context.state and tool_context.state |
| MCP integration | MCPToolset with connection params |
| Runtime config | App object with compaction, resumability, plugins |
| Plugins (global hooks) | BasePlugin registered on Runner or App |
| Context compaction | EventsCompactionConfig on App for long conversations |
| Session rewind | runner.rewind_async() to undo interactions |
| Streaming (Live API) | LiveRequestQueue for bidirectional audio/video |
| Multi-model | Claude, Ollama, LiteLLM, vLLM via model adapters |
| Agent Skills | SkillToolset to load Skills from files or code |
| Testing | pytest with InMemoryRunner |
| Evaluation | EvalSet with .test.json, adk eval CLI, pytest |
Project Structure
Every ADK project follows this layout:
my_agent/
├── my_agent/
│ ├── __init__.py # Must import agent module
│ ├── agent.py # Defines root_agent (entry point)
│ ├── prompts.py # Instruction strings (optional)
│ ├── tools.py # Custom tool functions (optional)
│ ├── sub_agents/ # Sub-agent packages (optional)
│ └── shared_libraries/ # Callbacks, utilities (optional)
├── tests/
│ └── test_agent.py
├── pyproject.toml
└── .env # GOOGLE_API_KEY or GOOGLE_CLOUD_PROJECT
Critical: init.py
# my_agent/__init__.py
from . import agent
Critical: root_agent
The root_agent variable at module level is the framework entry point:
# my_agent/agent.py
from google.adk.agents import Agent
root_agent = Agent(
name="my_agent",
model="gemini-2.5-flash",
description="Brief description for agent discovery",
instruction="Detailed system prompt...",
tools=[...],
)
pyproject.toml
Minimal config: requires-python = ">=3.10", dependencies = ["google-adk"], build-backend setuptools. For full example and environment config, see references/advanced-patterns.md.
Agent Types
1. LlmAgent (Single Agent)
The fundamental building block. Wraps a single LLM call with tools and instructions.
from google.adk.agents import LlmAgent
agent = LlmAgent(
name="assistant",
model="gemini-2.5-flash",
description="General assistant",
instruction="""You are a helpful assistant.
Use the search tool when you need current information.""",
tools=[search_tool],
output_schema=ResponseModel, # Optional structured output
output_key="response", # State key for output
generate_content_config=types.GenerateContentConfig(
temperature=0.7,
),
)
2. SequentialAgent
Runs sub-agents in order. Output of each flows to the next via shared state.
from google.adk.agents import SequentialAgent
pipeline = SequentialAgent(
name="research_pipeline",
description="Research then summarize",
sub_agents=[
researcher_agent, # Step 1: writes to state["research"]
summarizer_agent, # Step 2: reads state["research"]
],
)
3. ParallelAgent
Runs sub-agents concurrently. Use for independent tasks.
from google.adk.agents import ParallelAgent
parallel = ParallelAgent(
name="multi_channel",
description="Send to all channels simultaneously",
sub_agents=[
email_agent,
slack_agent,
calendar_agent,
],
)
4. LoopAgent
Repeats sub-agents until termination. Two termination patterns:
Pattern A: Fixed iterations
from google.adk.agents import LoopAgent
loop = LoopAgent(
name="refinement_loop",
description="Iteratively refine output",
sub_agents=[writer_agent, critic_agent],
max_iterations=3,
)
Pattern B: Checker agent with escalate
# The checker agent uses tool_context.actions.escalate = True to stop
def check_quality(score: float, tool_context: ToolContext) -> str:
"""Check if quality meets threshold."""
if score >= 0.9:
tool_context.actions.escalate = True
return "Quality threshold met, stopping loop."
return "Quality below threshold, continuing refinement."
checker = Agent(
name="checker",
model="gemini-2.5-flash",
instruction="Evaluate the output quality and call check_quality.",
tools=[check_quality],
)
loop = LoopAgent(
name="quality_loop",
sub_agents=[generator_agent, checker],
)
5. Composing Agent Types
Nest agent types freely for complex workflows. Example: SequentialAgent containing a ParallelAgent containing LlmAgents. See references/advanced-patterns.md for hierarchical workflow examples.
Tools
Function Tools
Any Python function with type hints and a docstring becomes a tool:
def get_weather(city: str, units: str = "celsius") -> dict:
"""Get current weather for a city.
Args:
city: The city name to look up weather for.
units: Temperature units - 'celsius' or 'fahrenheit'.
Returns:
dict with temperature, conditions, and humidity.
"""
# Implementation
return {"temperature": 22, "conditions": "sunny", "humidity": 45}
agent = Agent(
name="weather_agent",
model="gemini-2.5-flash",
instruction="Help users check the weather.",
tools=[get_weather],
)
Requirements:
- Type hints on all parameters
- Docstring with description and Args section
- Return type annotation
Tools with State Access
Use ToolContext to read/write session state:
from google.adk.tools import ToolContext
def add_to_cart(item: str, quantity: int, tool_context: ToolContext) -> dict:
"""Add an item to the shopping cart."""
cart = tool_context.state.get("cart", [])
cart.append({"item": item, "quantity": quantity})
tool_context.state["cart"] = cart
return {"status": "added", "cart_size": len(cart)}
AgentTool (Agent-as-Tool)
Wrap an agent to use it as a tool for another agent:
from google.adk.tools.agent_tool import AgentTool
specialist = Agent(
name="code_reviewer",
model="gemini-2.5-pro",
instruction="Review code for bugs and best practices.",
)
coordinator = Agent(
name="coordinator",
model="gemini-2.5-flash",
instruction="Coordinate tasks. Use code_reviewer for code reviews.",
tools=[AgentTool(agent=specialist)],
)
Built-in Tools
from google.adk.tools import google_search
agent = Agent(
name="researcher",
tools=[google_search],
)
MCP Tools
from google.adk.tools.mcp_tool import MCPToolset, StdioConnectionParams
from mcp import StdioServerParameters
agent = Agent(
name="db_agent",
tools=[
MCPToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=["-y", "some-mcp-server"],
),
),
),
],
)
For advanced tool patterns (FunctionTool, ToolboxToolset, long-running tools), see references/tools-reference.md.
Callbacks
Callbacks intercept the agent lifecycle. Return None to proceed, return a value to short-circuit.
| Callback | Signature | Use Case |
|---|---|---|
before_agent_callback | (CallbackContext) | Initialize state |
before_tool_callback | (BaseTool, dict, CallbackContext) -> dict|None | Validate inputs, auto-approve |
after_tool_callback | (BaseTool, dict, ToolContext, dict) -> dict|None | Post-process results |
before_model_callback | (CallbackContext, LlmRequest) | Rate limit, safety filter |
def before_tool(tool, args, tool_context) -> dict | None:
"""Return dict to skip tool execution with that response."""
if tool.name == "approve_discount" and args.get("value", 0) > 50:
return {"status": "rejected", "reason": "Discount too large"}
return None # Proceed normally
agent = Agent(
name="guarded_agent",
model="gemini-2.5-flash",
instruction="...",
before_tool_callback=before_tool,
)
For rate limiting, input validation, and safety callbacks, see references/advanced-patterns.md.
State Management
State is a shared dictionary across agents, tools, and callbacks. Scopes: state["key"] (session), app:key (app-wide), user:key (user-wide).
Passing data between agents: Use output_key to write to state, read in next agent's instruction:
researcher = Agent(name="researcher", output_key="findings", output_schema=ResearchOutput, ...)
writer = Agent(name="writer", instruction="Write report based on state['findings'].", ...)
pipeline = SequentialAgent(name="pipeline", sub_agents=[researcher, writer])
Structured Output
Use Pydantic models for typed, validated agent output:
from pydantic import BaseModel
class AnalysisResult(BaseModel):
summary: str
key_findings: list[str]
confidence: float
recommendations: list[str]
agent = Agent(
name="analyzer",
model="gemini-2.5-flash",
instruction="Analyze the provided data and return structured results.",
output_schema=AnalysisResult,
output_key="analysis", # Stored in state["analysis"]
)
Running and Testing
Local Development
# Install
pip install google-adk
# Set API key
export GOOGLE_API_KEY="your-key"
# Run interactively
adk run my_agent
# Run with web UI
adk web my_agent
Testing with InMemoryRunner
import pytest
from google.adk.runners import InMemoryRunner
from google.genai import types
@pytest.mark.asyncio
async def test_agent():
runner = InMemoryRunner(agent=root_agent, app_name="test")
session = await runner.session_service.create_session(
user_id="test_user", app_name="test",
)
content = types.Content(
role="user", parts=[types.Part.from_text(text="Hello")],
)
events = []
async for event in runner.run_async(
user_id="test_user", session_id=session.id, new_message=content,
):
events.append(event)
assert "expected" in events[-1].content.parts[0].text.lower()
Evaluation
ADK provides built-in evaluation for tool correctness, response quality, and safety. Define eval cases in .test.json files and run with adk eval, pytest, or the web UI. See references/evaluation.md for eval data formats, all 8 metrics, and patterns.
Model Selection
Use gemini-2.5-flash for most agents (fast, cost-effective). Use gemini-2.5-pro for complex reasoning. Gemini 3 models (gemini-3-flash, gemini-3-pro) also supported. Non-Gemini models work too: Claude, Ollama, LiteLLM, vLLM. Configure via generate_content_config=types.GenerateContentConfig(temperature=0.2).
Design Patterns
| Pattern | When to Use | ADK Implementation |
|---|---|---|
| Sequential pipeline | Multi-step tasks with dependencies | SequentialAgent with ordered sub-agents |
| Fan-out / Fan-in | Independent tasks then synthesis | ParallelAgent → merger Agent |
| Reflection loop | Quality matters more than speed | LoopAgent with producer + critic agents |
| Dynamic routing | Diverse inputs need different handling | Parent Agent with sub_agents (Auto-Flow) |
| Layered fallback | Tool failures need graceful recovery | SequentialAgent: primary → fallback → response |
| Guardrailed agent | Safety/compliance requirements | before_model_callback + before_tool_callback |
| Resource tiering | Cost optimization under constraints | Different model per agent (Pro vs Flash) |
Key design rules:
- Split agents with 5+ tools into focused specialists. One agent = one responsibility.
- Pass data between agents via
output_key+output_schema(Pydantic) -- never rely on free text. - Set
max_iterationson everyLoopAgent. No exceptions. - Separate generation from evaluation -- use a different agent to critique (avoids self-review bias).
- Write precise sub-agent
descriptionfields -- they drive Auto-Flow routing decisions. - Embed reasoning steps in instructions: "1. Analyze 2. Plan 3. Execute 4. Verify".
- Include a fallback route for unclear inputs -- ambiguous requests must not be silently misrouted.
See also: advanced-patterns.md (App config, plugins, deployment, AG-UI, session rewind) | design-patterns.md (chaining, reflection, planning, guardrails, memory, HITL) | a2a-protocol.md (A2A server/client, agent cards) | troubleshooting.md (debugging, performance).
Decision Guide
When to use which agent type:
Single task, one LLM call? → Agent / LlmAgent
Steps must run in order? → SequentialAgent
Steps are independent? → ParallelAgent
Need iteration/refinement? → LoopAgent
Need on-demand delegation? → AgentTool
Remote agent, different service? → RemoteA2aAgent (A2A)
Complex multi-stage? → Compose agent types
When to use which tool type:
Simple function? → Python function with type hints
Need state access? → Add ToolContext parameter
Delegate to another agent? → AgentTool
Remote agent over network? → RemoteA2aAgent (A2A protocol)
External MCP server? → MCPToolset
Database access? → ToolboxToolset
Web search? → google_search (built-in)
Modular skill package? → SkillToolset