ワンクリックで
adk-developer
Comprehensive guide to building, orchestrating, and deploying agents with the Google Agent Development Kit (ADK).
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Comprehensive guide to building, orchestrating, and deploying agents with the Google Agent Development Kit (ADK).
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Expert guide and patterns for building Agent-Driven User Interfaces (A2UI) using the ADK.
Specialized role for designing interactive Agent-Driven User Interfaces (A2UI). Understands agent interactions, selects appropriate UI components, and creates UX design documents without generating code.
Deploys ADK agents to Vertex AI Agent Engine using the Python SDK and registers them with Gemini Enterprise.
Deploys ADK agents to Vertex AI Agent Engine using the standard Terraform pattern.
Specialized role for registering and managing ADK agents within Gemini Enterprise using cURL.
specialized role for designing high-quality ADK agent architectures without generating code.
| name | ADK Developer |
| description | Comprehensive guide to building, orchestrating, and deploying agents with the Google Agent Development Kit (ADK). |
This document serves as a long-form, comprehensive reference for building, orchestrating, and deploying AI agents using the Python Agent Development Kit (ADK). It covers every significant aspect with detailed code examples and in-depth best practices.
CRITICAL: This skill relies on local reference documentation that must be kept in sync with the official repository.
When starting a new session or tasks involving ADK:
python3 scripts/update_skill.py
[!IMPORTANT]
- Always use Vertex AI: Set
GOOGLE_GENAI_USE_VERTEXAI=True.- Configuration: Ask for
GOOGLE_CLOUD_PROJECTandGOOGLE_CLOUD_LOCATIONfrom the user.- Models: Use
gemini-2.5-flashor higher. NEVER usegemini-1.5-flashorgemini-1.5-pro.- Directory Structure: NEVER put code in the root folder. Always create specific agent subfolders (e.g.,
my_agent/).- Simplicity First: ALWAYS start with a single
LlmAgent. Only introduce Workflow/Multi-Agent architectures if the task complexity specifically demands it (e.g., rigid determinism, context limits). Do not over-engineer.- Process Boundaries: When asked to Design, produce the design artifact and STOP. Do not implement code until explicitly requested.
- Configuration Protocol: NEVER assume values for
GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION, or other secrets. ALWAYS ask the user for these values before creating configuration files.
LlmAgent)BaseAgent)Agent: The core intelligent unit. Can be LlmAgent (LLM-driven) or BaseAgent (custom/workflow).Tool: Callable function/class providing external capabilities (FunctionTool, OpenAPIToolset, etc.).Session: A unique, stateful conversation thread with history (events) and short-term memory (state).State: Key-value dictionary within a Session for transient conversation data.Memory: Long-term, searchable knowledge base beyond a single session (MemoryService).Artifact: Named, versioned binary data (files, images) associated with a session or user.Runner: The execution engine; orchestrates agent activity and event flow.Event: Atomic unit of communication and history; carries content and side-effect actions.InvocationContext: The comprehensive root context object holding all runtime information for a single run_async call.A well-structured ADK project is crucial for maintainability and leveraging adk CLI tools.
your_project_root/
├── my_first_agent/ # Each folder is a distinct agent app
│ ├── __init__.py # Makes `my_first_agent` a Python package (`from . import agent`)
│ ├── agent.py # Contains `root_agent` definition and `LlmAgent`/WorkflowAgent instances
│ ├── tools.py # Custom tool function definitions
│ ├── data/ # Optional: static data, templates
│ └── .env # Environment variables (API keys, project IDs)
├── my_second_agent/
│ ├── __init__.py
│ └── agent.py
├── requirements.txt # Project's Python dependencies (e.g., google-adk, litellm)
├── tests/ # Unit and integration tests
│ ├── unit/
│ │ └── test_tools.py
│ └── integration/
│ └── test_my_first_agent.py
│ └── my_first_agent.evalset.json # Evaluation dataset for `adk eval`
└── main.py # Optional: Entry point for custom FastAPI server deployment
adk web and adk run automatically discover agents in subdirectories with __init__.py and agent.py..env files are automatically loaded by adk tools when run from the root or agent directory.ADK allows you to define agents, tools, and even multi-agent workflows using a simple YAML format.
Basic Agent (root_agent.yaml):
# yaml-language-server: $schema=https://raw.githubusercontent.com/google/adk-python/refs/heads/main/src/google/adk/agents/config_schemas/AgentConfig.json
name: assistant_agent
model: gemini-2.5-flash
description: A helper agent that can answer users' various questions.
instruction: You are an agent to help answer users' various questions.
Loading Agent Config in Python:
from google.adk.agents import config_agent_utils
root_agent = config_agent_utils.from_config("{agent_folder}/root_agent.yaml")
LlmAgent)The LlmAgent is the cornerstone of intelligent behavior, leveraging an LLM for reasoning and decision-making.
LlmAgent Setupfrom google.adk.agents import Agent
def get_current_time(city: str) -> dict:
"""Returns the current time in a specified city."""
# Mock implementation
if city.lower() == "new york":
return {"status": "success", "time": "10:30 AM EST"}
return {"status": "error", "message": f"Time for {city} not available."}
my_first_llm_agent = Agent(
name="time_teller_agent",
model="gemini-2.5-flash", # Essential: The LLM powering the agent
instruction="You are a helpful assistant that tells the current time in cities. Use the 'get_current_time' tool for this purpose.",
description="Tells the current time in a specified city.", # Crucial for multi-agent delegation
tools=[get_current_time] # List of callable functions/tool instances
)
LlmAgent Configurationgenerate_content_config: Controls LLM generation parameters (temperature, token limits, safety).
from google.genai import types as genai_types
from google.adk.agents import Agent
gen_config = genai_types.GenerateContentConfig(
temperature=0.2, # Controls randomness (0.0-1.0), lower for more deterministic.
top_p=0.9, # Nucleus sampling: sample from top_p probability mass.
top_k=40, # Top-k sampling: sample from top_k most likely tokens.
max_output_tokens=1024, # Max tokens in LLM's response.
stop_sequences=["## END"] # LLM will stop generating if these sequences appear.
)
agent = Agent(
# ... basic config ...
generate_content_config=gen_config
)
input_schema & output_schema: Define strict JSON input/output formats using Pydantic models.
Warning: Using
output_schemaforces the LLM to generate JSON and disables its ability to use tools or delegate to other agents.
This is the most reliable way to make an LLM produce predictable, parseable JSON, which is essential for multi-agent workflows.
Define the Schema with Pydantic:
from pydantic import BaseModel, Field
from typing import Literal
class SearchQuery(BaseModel):
"""Model representing a specific search query for web search."""
search_query: str = Field(
description="A highly specific and targeted query for web search."
)
class Feedback(BaseModel):
"""Model for providing evaluation feedback on research quality."""
grade: Literal["pass", "fail"] = Field(
description="Evaluation result. 'pass' if the research is sufficient, 'fail' if it needs revision."
)
comment: str = Field(
description="Detailed explanation of the evaluation, highlighting strengths and/or weaknesses of the research."
)
follow_up_queries: list[SearchQuery] | None = Field(
default=None,
description="A list of specific, targeted follow-up search queries needed to fix research gaps. This should be null or empty if the grade is 'pass'."
)
Assign the Schema to an LlmAgent:
research_evaluator = LlmAgent(
name="research_evaluator",
model="gemini-2.5-pro",
instruction="""
You are a meticulous quality assurance analyst. Evaluate the research findings in 'section_research_findings' and be very critical.
If you find significant gaps, assign a grade of 'fail', write a detailed comment, and generate 5-7 specific follow-up queries.
If the research is thorough, grade it 'pass'.
Your response must be a single, raw JSON object validating against the 'Feedback' schema.
""",
output_schema=Feedback, # This forces the LLM to output JSON matching the Feedback model.
output_key="research_evaluation", # The resulting JSON object will be saved to state.
disallow_transfer_to_peers=True, # Prevents this agent from delegating. Its job is only to evaluate.
)
instruction)The instruction is critical. It guides the LLM's behavior, persona, and tool usage.
Example 1: Constraining Tool Use and Output Format
import datetime
from google.adk.tools import google_search
plan_generator = LlmAgent(
model="gemini-2.5-flash",
name="plan_generator",
description="Generates a 4-5 line action-oriented research plan.",
instruction=f"""
You are a research strategist. Your job is to create a high-level RESEARCH PLAN, not a summary.
**RULE: Your output MUST be a bulleted list of 4-5 action-oriented research goals or key questions.**
- A good goal starts with a verb like "Analyze," "Identify," "Investigate."
- A bad output is a statement of fact like "The event was in April 2024."
**TOOL USE IS STRICTLY LIMITED:**
Your goal is to create a generic, high-quality plan *without searching*.
Only use `google_search` if a topic is ambiguous and you absolutely cannot create a plan without it.
You are explicitly forbidden from researching the *content* or *themes* of the topic.
Current date: {datetime.datetime.now().strftime("%Y-%m-%d")}
""",
tools=[google_search],
)
Workflow agents (SequentialAgent, ParallelAgent, LoopAgent) provide deterministic control flow, combining LLM capabilities with structured execution.
SequentialAgent: Linear ExecutionExecutes sub_agents one after another in the order defined. The InvocationContext is passed along, allowing state changes to be visible to subsequent agents.
from google.adk.agents import SequentialAgent, Agent
document_pipeline = SequentialAgent(
name="SummaryQuestionPipeline",
sub_agents=[summarizer, question_generator], # Order matters!
description="Summarizes a document then generates questions."
)
ParallelAgent: Concurrent ExecutionExecutes sub_agents simultaneously. Useful for independent tasks to reduce overall latency. All sub-agents share the same session.state.
LoopAgent: Iterative ProcessesRepeatedly executes its sub_agents (sequentially within each loop iteration) until a condition is met or max_iterations is reached.
Termination: The loop stops if max_iterations is reached OR any sub-agent yields an event with actions.escalate = True.
CRITICAL ARCHITECTURE RULE: When using SequentialAgent or other deterministic workflows, never start the workflow until all required information is collected.
The Pattern:
LlmAgent):
session.state.SequentialAgent):
session.state, executes tools/sub-agents, writes results to session.state.LlmAgent):
Example Structure:
main_orchestrator = LlmAgent(
name="MainApp",
description="Orchestrator for the entire application.",
instruction="""You are the main coordinator.
1. First, transfer to `intake_agent` to gather the user's requirements.
2. Once `intake_agent` finishes and details are saved, transfer to `research_workflow` to process the data in the background. DO NOT do this yourself.
3. After the workflow completes, transfer to `presenter_agent` to share the results with the user.
""",
sub_agents=[
intake_agent, # Interactive: "What city do you want to research?"
research_workflow, # Headless: SequentialAgent[Search, Scrape, Summarize] -> writes to state['summary']
presenter_agent # Interactive: "Here is the summary for {city}. Any questions?"
]
)
A hierarchical (tree-like) structure of parent-child relationships defined by the sub_agents parameter.
session.state): The most common and robust method. Agents read from and write to the same mutable dictionary.transfer_to_agent): A LlmAgent can dynamically hand over control to another agent based on its reasoning.AgentTool): An LlmAgent can treat another BaseAgent instance as a callable tool.The Agent-to-Agent (A2A) Protocol enables agents to communicate over a network, even if they are written in different languages or run as separate services.
from google.adk.a2a.utils.agent_to_a2a import to_a2a
# root_agent is your existing ADK Agent instance
a2a_app = to_a2a(root_agent, port=8001)
from google.adk.a2a.remote_a2a_agent import RemoteA2aAgent
prime_checker_agent = RemoteA2aAgent(
name="prime_agent",
description="A remote agent that checks if numbers are prime.",
agent_card="http://localhost:8001/a2a/check_prime_agent/.well-known/agent.json"
)
BaseAgent)For unique orchestration logic that doesn't fit standard workflow agents, inherit directly from BaseAgent.
_run_async_implfrom google.adk.agents import BaseAgent
from google.adk.agents.invocation_context import InvocationContext
from google.adk.events import Event, EventActions
from typing import AsyncGenerator
import logging
class EscalationChecker(BaseAgent):
"""Checks research evaluation and escalates to stop the loop if grade is 'pass'."""
def __init__(self, name: str):
super().__init__(name=name)
async def _run_async_impl(
self, ctx: InvocationContext
) -> AsyncGenerator[Event, None]:
# 1. Read from session state.
evaluation_result = ctx.session.state.get("research_evaluation")
# 2. Apply custom Python logic.
if evaluation_result and evaluation_result.get("grade") == "pass":
logging.info(f"[{self.name}] Research passed. Escalating to stop loop.")
# 3. Yield an Event with a control Action.
yield Event(author=self.name, actions=EventActions(escalate=True))
else:
logging.info(f"[{self.name}] Research failed or not found. Loop continues.")
# Yielding an event without actions lets the flow continue.
yield Event(author=self.name)
ADK's model flexibility allows integrating various LLMs for different needs.
Note: Always use GOOGLE_GENAI_USE_VERTEXAI=True. Ask for GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION from the user. Use gemini-2.5-flash or higher model.
gcloud auth application-default login (recommended).GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID", GOOGLE_CLOUD_LOCATION="your-region" (environment variables).GOOGLE_GENAI_USE_VERTEXAI="True".LiteLlm provides a unified interface to 100+ LLMs (OpenAI, Anthropic, Cohere, etc.).
from google.adk.models.lite_llm import LiteLlm
agent_openai = Agent(model=LiteLlm(model="openai/gpt-4o"), ...)
Tools extend an agent's abilities beyond text generation.
from google.adk.tools.tool_context import ToolContext
def my_tool(param1: Type, param2: Type, tool_context: ToolContext) -> dict:dict (JSON-serializable).tool_context in docstring.FunctionTool, LongRunningFunctionTool, AgentTool.google_search, BuiltInCodeExecutor, VertexAiSearchTool, BigQueryToolset.LangchainTool, CrewaiTool.OpenAPIToolset, MCPToolset.ApiHubToolset, ApplicationIntegrationToolset.State: The Conversational ScratchpadA mutable dictionary within session.state for short-term, dynamic data.
context.state (in callbacks/tools) or LlmAgent.output_key.session.state['booking_step']).user:: Persistent for a user_id across all their sessions.app:: Persistent for app_name across all users and sessions.temp:: Volatile, for the current Invocation turn only.user Event.Runner calls agent.run_async(invocation_context).yields an Event (e.g., tool call, text response). Execution pauses.Runner processes the Event (applies state changes, etc.) and yields it to the client.Event Object: The Communication BackboneEvent.author: Source of the event ('user', agent name, 'system').Event.content: The primary payload (text, function calls, function responses).Event.actions: Signals side effects (state_delta, transfer_to_agent, escalate).Event.is_final_response(): Helper to identify the complete, displayable message.Callbacks are functions that intercept and control agent execution at specific points.
before_agent_callback, after_agent_callback.before_model_callback, after_model_callback.before_tool_callback, after_tool_callback.Example 2: Output Transformation with after_agent_callback
This callback takes an LLM's raw output (containing custom tags), uses Python to format it into markdown, and returns the modified content, overriding the original.
import re
from google.adk.agents.callback_context import CallbackContext
from google.genai import types as genai_types
def citation_replacement_callback(callback_context: CallbackContext) -> genai_types.Content:
"""Replaces <cite> tags in a report with Markdown-formatted links."""
# 1. Get raw report and sources from state.
final_report = callback_context.state.get("final_cited_report", "")
sources = callback_context.state.get("sources", {})
# 2. Define a replacer function for regex substitution.
def tag_replacer(match: re.Match) -> str:
short_id = match.group(1)
if not (source_info := sources.get(short_id)):
return "" # Remove invalid tags
title = source_info.get("title", short_id)
return f" [{title}]({source_info['url']})"
# 3. Use regex to find all <cite> tags and replace them.
processed_report = re.sub(
r'<cite\s+source\s*=\s*["\']?(src-\d+)["\']?\s*/>',
tag_replacer,
final_report,
)
processed_report = re.sub(r"\s+([.,;:])", r"\1", processed_report) # Fix spacing
# 4. Save the new version to state and return it to override the original agent output.
callback_context.state["final_report_with_citations"] = processed_report
return genai_types.Content(parts=[genai_types.Part(text=processed_report)])
When a tool requires user interaction (OAuth consent), ADK pauses and signals your Agent Client application.
runner.run_async() yields an event with a special adk_request_credential function call.auth_uri from auth_config in the event. Redirect user's browser.FunctionResponse for adk_request_credential with the captured callback URL.runner.run_async() is called again with this FunctionResponse.adk web: Launches a local web UI. CRITICAL: Do not pass the agent folder name to this command (e.g., adk web my_agent is WRONG). Just run adk web from the project root; it will automatically discover available agents.adk run: Command-line interactive chat.
--replay flag:
adk run <agent_path> --replay <json_file_path>
The JSON file must conform to this schema:
{
"state": {},
"queries": [
"User prompt step 1",
"User prompt step 2"
]
}
adk api_server: Launches a local FastAPI server exposing /run, /run_sse, etc.Fully managed, scalable service for ADK agents on Google Cloud.
from vertexai.preview import reasoning_engines
# Wrap your root_agent for deployment
app_for_engine = reasoning_engines.AdkApp(agent=root_agent, enable_tracing=True)
# Deploy
remote_app = agent_engines.create(
agent_engine=app_for_engine,
requirements=["google-cloud-aiplatform[adk,agent_engines]"],
display_name="My Production Agent"
)
Serverless container platform.
adk deploy cloud_run --project <id> --region <loc> ... /path/to/agentadk eval)Systematically assess agent performance using eval_cases defined in .evalset.json.
{
"eval_set_id": "weather_bot_eval",
"eval_cases": [
{
"eval_id": "london_weather_query",
"conversation": [
{
"user_content": {"parts": [{"text": "What's the weather in London?"}]},
"final_response": {"parts": [{"text": "The weather in London is cloudy..."}]},
"intermediate_data": {
"tool_uses": [{"name": "get_weather", "args": {"city": "London"}}]
}
}
],
"session_input": {"app_name": "weather_app", "user_id": "test_user", "state": {}}
}
]
}
BuiltInCodeExecutor.adk web UI: Visual trace inspector.runner.run_async() events and print relevant fields.async function with a return type of AsyncGenerator.audio/pcm) and Vision (image/jpeg, video/mp4) inputs.include_contents='none': For stateless utility agents.StreamingMode.SSE or BIDI.LlmAgent and mock tools.requirements.txt..env.max_iterations and max_llm_calls.try...except blocks in tools and callbacks.The following script demonstrates how to programmatically test an agent's output.
import asyncio
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from app.agent import root_agent
from google.genai import types as genai_types
async def main():
"""Runs the agent with a sample query."""
session_service = InMemorySessionService()
await session_service.create_session(
app_name="app", user_id="test_user", session_id="test_session"
)
runner = Runner(
agent=root_agent, app_name="app", session_service=session_service
)
query = "I want a recipe for pancakes"
async for event in runner.run_async(
user_id="test_user",
session_id="test_session",
new_message=genai_types.Content(
role="user",
parts=[genai_types.Part.from_text(text=query)]
),
):
if event.is_final_response():
print(event.content.parts[0].text)
if __name__ == "__main__":
asyncio.run(main())
Real-world insights from deploying ADK agents.
Context: When testing E2E with adk web or browser automation.
Context: Migrating tools between agents.
tools.py often misses external file dependencies (e.g., data.json).open(), read(), or load() to identify and copy required assets.Context: Combining Conversational UI with A2UI.
Context: Designing new agent architectures.
LlmAgent with well-defined tools is preferred. YAGNI (You Aren't Gonna Need It) applies to agents too.Context: User asks for "Design" or "Architecture".
.md file and wait. Generating implementation code immediately violates trust and wastes tokens if the design is rejected.Context: Designing the "Sandwich Pattern" (Interactive Intake -> Headless Workflow -> Interactive Review).
SequentialAgent as the root orchestrator. SequentialAgent runs its sub-agents back-to-back and does not natively halt or yield for intermediate multi-turn user inputs unless forced via explicit signals.LlmAgent. The LlmAgent can dynamically read the event stream, pause to let the user answer the Intake agent's questions, recognize when Intake is finished (state updated), and then transfer control to the headless SequentialAgent workflow in the background.Context: In a "Sandwich Pattern" where an interactive LlmAgent (Intake) finishes its job and needs to return control to the parent orchestrator so a headless workflow can run.
Problem: The Intake agent collects the last piece of information, says "Thank you, I will process this", but the system hangs waiting for the user to reply to a completed task. The orchestrator never resumes because the Intake agent is still considered the "active" agent.
Solution: The interactive agent MUST explicitly yield control back to the orchestrator when its task is complete.
disallow_transfer_to_parent=False on the interactive agent so it is allowed to return control.instruction, explicitly command it to "transfer back to your parent orchestrator" once all mandatory data is collected.tool_context.state["force_end"] = True to forcefully break out of the active interaction loop, allowing the orchestrator's event loop to detect the state change and route to the next workflow step.adk webContext: Running adk web to test a newly developed agent via the browser UI.
Problem: Running adk web from inside the specific agent's folder or running adk web [agent_name] can cause pathing issues, naming collisions, or prevent the app launcher from loading the agent correctly.
Solution: Always run adk web --port 8080 from the parent workspace directory (the root folder containing all agent subfolders) without specifying the agent name. This allows the ADK dev server to discover all agents, present the app selector UI properly, and maintain session isolation.