Google ADK: Modular AI Agent Development from Scratch

Google Agent Development Kit (ADK) のチュートリアルへようこそ!🚀

ADKは、高度なAIエージェントを構築、デプロイ、管理するためのPythonフレームワークです。これにより、推論、ツールの使用、チームとしての協力が可能なエージェントを作成するための構成要素が提供されます。

このノートブックでは、天気アシスタント用のシンプルながら強力な「エージェントチーム」を構築します。以下の内容を扱います:

  • Agents & Tools: コア推論エンジンとその機能。
  • Agent-to-Agent Communication: エージェント同士が協力し、タスクを委任する方法。
  • Session Management: 会話の状態と記憶を管理する方法。

Step 1: Setup and API Keys

まず、必要なライブラリをインストールし、APIキーを設定します。.env ファイルを使用して安全に管理します。

  1. 必要なパッケージをインストールします:
    pip install google-adk python-dotenv
    
  2. このノートブックと同じディレクトリに .env という名前のファイルを作成し、Google APIキーを追加します:
    GOOGLE_API_KEY="YOUR_GEMINI_API_KEY"
    

それでは、ノートブックにキーを読み込みましょう。

import os
import asyncio
from dotenv import load_dotenv
import logging

# Load environment variables from .env file
load_dotenv()

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")

# Configure ADK to use the API key directly
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"

# Define the default model to use across the notebook
MODEL = "gemini-2.5-flash"

# Suppress verbose SDK warning about non-text parts when tools/structured outputs are present
class _SuppressNonTextParts(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        try:
            msg = record.getMessage()
        except Exception:
            msg = str(record.msg)
        return "non-text parts in the response" not in msg

for _name in ("google", "google.genai"):
    _logger = logging.getLogger(_name)
    _logger.addFilter(_SuppressNonTextParts())

if not GOOGLE_API_KEY:
    print("API key not found. Please create a .env file and add your GOOGLE_API_KEY.")
else:
    print("Successfully loaded API key.")
    print(f"Using model: {MODEL}")
Successfully loaded API key.
Using model: gemini-2.5-flash

Step 2: Define Tools and Specialist Agents

Tools は、エージェントに機能を提供するPython関数です。エージェントのLLMは関数のdocstringに依存して、ツールが何をするのか、いつ使用すべきかを理解します。そのため、明確なdocstringが重要です。

複雑なタスクに対する堅牢なアプローチは、複数の専門エージェントで構成されるAgent Teamを構築することです。ここでは、天気用と挨拶用の2つの専門エージェントを作成します。

from google.adk.agents import Agent
from typing import Optional

# --- Define Tools ---
def get_weather(city: str) -> dict:
    """Retrieves the current weather report for a specified city."""
    print(f"--- Tool: get_weather called for city: {city} ---")
    mock_db = {
        "new york": {"report": "The weather in New York is sunny with a temperature of 25°C."},
        "london": {"report": "It's cloudy in London with a temperature of 15°C."},
    }
    return mock_db.get(city.lower(), {"report": f"Sorry, I don't have weather for {city}."})


def say_hello(name: Optional[str] = None) -> str:
    """Provides a simple friendly greeting."""
    print(f"--- Tool: say_hello called ---")
    return f"Hello, {name}!" if name else "Hello there!"

# --- Define Specialist Agents ---
weather_agent = Agent(
    name="WeatherAgent",
    model=MODEL,
    description=(
        "Provides weather information for specific cities. Use this for any query that mentions weather, "
        "forecast, temperature, rain, sun, or a city/location."
    ),
    instruction=(
        "You are a helpful weather assistant. Always use the get_weather tool to obtain information. "
        "Respond concisely with the weather report."
    ),
    tools=[get_weather],
)

greeting_agent = Agent(
    name="GreetingAgent",
    model=MODEL,
    description=(
        "Handles only pure greetings such as 'hi', 'hello', or 'hey' with no other request. "
        "Do not handle any other topics (e.g., weather)."
    ),
    instruction=(
        "You are the Greeting Agent. Respond ONLY to standalone greetings (hi/hello/hey) with a short greeting. "
        "If the user asks about anything else (e.g., weather, cities, temperatures, or questions), do NOT answer; "
        "leave it to other agents."
    ),
    tools=[say_hello],
)

print("✅ Specialist agents and tools defined.")
✅ Specialist agents and tools defined.

Step 3: Root Agentを使用してエージェントチームを構築する

次に、マネージャーとして機能するRoot Agentを作成します。このエージェントはユーザーからのリクエストを受け取り、その指示とサブエージェントのdescriptionに基づいて、リクエストを自分で処理するか、適切な専門家に委任するかを決定します。

Root Agentにsub_agentsのリストを提供することで、ADKはこの自動委任を可能にします。これは、モジュール化され、スケーラブルなエージェントシステムを構築するための重要な機能です。

# The root agent orchestrates the team
root_agent = Agent(
    name="RootAgent",
    model=MODEL,
    instruction=(
        "You are the coordinator of an agent team. Route each user request to the most appropriate sub-agent.\n"
        "Routing rules:\n"
        "- If the user mentions weather, forecast, temperature, a city/location, or asks about conditions, delegate to WeatherAgent.\n"
        "- If the user gives a standalone greeting (hi/hello/hey) with no other request, delegate to GreetingAgent.\n"
        "- Do NOT answer yourself; always delegate to a sub-agent.\n"
        "- Never treat a weather-related question as a greeting."
    ),
    # Key step: Link the sub-agents to enable delegation
    sub_agents=[weather_agent, greeting_agent]
)
print(f"✅ Root Agent '{root_agent.name}' created with sub-agents: {[sa.name for sa in root_agent.sub_agents]}")
✅ Root Agent 'RootAgent' created with sub-agents: ['WeatherAgent', 'GreetingAgent']

Step 4: Run the Agent and Manage the Session

エージェントチームと対話するには、Runner を使用します。Runner は情報の流れを調整し、SessionService を使用して会話の記憶を管理します。これにより、エージェントが以前のターンを覚えることができます。InMemorySessionService は、初めて始める際に最適です。

ADK の run_async メソッドは非同期であるため、Jupyter 環境で実行する際には await を使用します。

from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
import sys
import io
import contextlib

runner = Runner(
    agent=root_agent,
    session_service=InMemorySessionService(),
    app_name="weather_tutorial_app"
)

class _FilterStream(io.TextIOBase):
    def __init__(self, underlying, substrings):
        self._underlying = underlying
        self._substrings = tuple(substrings)

    def write(self, s):
        try:
            text = str(s)
        except Exception:
            text = s
        if any(sub in text for sub in self._substrings):
            return len(text)
        return self._underlying.write(text)

    def flush(self):
        return self._underlying.flush()

async def call_agent(query: str, session_id: str):
    """Helper function to run an asynchronous query against the agent."""
    print(f"\n>>> User: {query}")

    # Filter noisy stderr warnings about non-text parts when tools are used
    filter_stream = _FilterStream(sys.stderr, ["non-text parts in the response"])
    with contextlib.redirect_stderr(filter_stream):
        # Use run_async to get events and collect only the final final-response
        final_text = None
        async for event in runner.run_async(
            user_id="user_123",
            session_id=session_id,
            new_message=types.Content(role='user', parts=[types.Part(text=query)])
        ):
            if event.is_final_response() and event.content:
                # capture text but don't print yet to avoid duplicates
                for part in event.content.parts:
                    if getattr(part, "text", None):
                        final_text = part.text
                        break
        if final_text:
            print(f"<<< Agent: {final_text}")

# Let's run a multi-turn conversation!
async def main():
    session_id = "session_abc"
    user_id = "user_123"

    # Ensure the session exists before running; required by InMemorySessionService
    await runner.session_service.create_session(
        app_name="weather_tutorial_app",
        user_id=user_id,
        session_id=session_id,
    )

    print("--- Starting conversation ---")

    # The root agent should delegate this to the GreetingAgent
    await call_agent("Hello there", session_id)

    # The root agent should delegate this to the WeatherAgent
    await call_agent("What is the weather in New York?", session_id)

# In a Jupyter Notebook, you can run the top-level async function with await
await main()
--- Starting conversation ---

>>> User: Hello there
--- Tool: say_hello called ---
<<< Agent: Hello there!

>>> User: What is the weather in New York?
--- Tool: get_weather called for city: New York ---
<<< Agent: The weather in New York is sunny with a temperature of 25°C.

Conclusion

おめでとうございます!Google ADK を使用してマルチエージェントチームを無事に構築できました。

ツールの定義、専門エージェントの作成、タスクを委任するルートエージェントの使用といった基本的な概念を学びました。このモジュール設計は、より複雑で高機能なエージェントシステムを構築するための基礎となります。

ここからは、SessionState を使用したメモリの追加、コールバックを使用した安全ガードレールの実装、または現実世界の API への接続といった、より高度なトピックを探求することができます。