ADK で毎朝お天気を通知するエージェントを作る

ADK で毎朝お天気を通知するエージェントを作る

2025.12.20

こんにちは、すらぼです。好きな言葉は「自動化」です。

この記事は クラスメソッド Google Cloud Advent Calendar 2025 15日目の記事です。

今回は ADK を使って、 使用すべきツールを適切に判断できるエージェント を作成してみます。
シナリオとしては、天気情報から今日の服装や傘の要否などを毎朝通知してくれるエージェントを作ってみます。

やること

  • 天気予報を取得して、その日の服装や傘についてのアドバイスをSlackに通知してくれるエージェントを作る
  • エージェントに2つの tools を使って、上記を実装する。
    • 気象情報の取得用 tools
    • Slack 通知用 tools

エージェントに2つの tools を渡して、これを適切に使ってもらえるように実装していきます。

流れ

今回は、以下のような流れで実装してみます。

  • ローカルで実装
    • Web UI で実装&動作確認
      • 気象情報を取得できるようにする
      • Slack に通知できるようにする
    • 単一の API エンドポイントで動作させる
  • Cloud Run にデプロイする
    • API が問題なく動作することを確認
  • 自動化する
    • Cloud Scheduler で呼び出す

今回、最終的には Web UI は使わないのですが、挙動の確認などがかなり便利なので Web UI を使って最初の実装は進めていきます。

ローカルでの実装

では、実際にやっていきましょう。まずローカルでの実装を進めていきます。

adk の初期化

まずは、作業ディレクトリを初期化します。

# uv の初期化
uv init -p 3.14 .

# google-adk のインストール
uv add google-adk

# エージェントの作成(今回は weather_agent )
uv run adk create weather_agent

adk create コマンドを実行すると、対話 UI が表示されます。

今回は以下のように設定しました。

  • Choose model:
      1. gemini-2.5-flash
  • Choose a backend
      1. Vertex AI
  • Enter Google Cloud project ID
    • 使用するプロジェクトID
  • Enter Google Cloud region
    • us-central1

CleanShot 2025-12-16 at 20.05.07.png

今回は Cloud Run にそのままデプロイすることを想定して Vertex AI にしました。利用料に応じた料金がかかるのでご注意ください。
リージョンは us-central1 を指定しています。東京リージョン (asia-northeast1) をはじめ、アジアリージョンでは多くのモデルは利用できないため、こちらもご注意ください。

サポートされるリージョンは以下のページをご確認ください。

https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-flash?hl=ja

adk create が完了すると、以下のようなファイルが作成されます。

./weather_agent:
    - .env
    - __init__.py
    - agent.py

この時点で、すでに会話可能な状態になります。 gcloud auth application-default login したのち、以下のコマンドを実行してみます。

uv run adk run ./weather_agent/

対話UIが表示され、会話ができるようになっています。
以下のスクリーンショットでは、挨拶をした結果 root_agent から返事が返ってきていることが確認できますね。

CleanShot 2025-12-16 at 21.35.16.png

気象情報 tool を作る

では、エージェントの中身として、まずは tools を作ってみましょう。

tools を作成する際には、「関数の docstring を書くこと」 が非常に重要です。理由としては、ツールがどんな目的で利用でき、どのような引数を渡すことでレスポンスとして何が得られるのかをAIに説明することで、AIがより適切な判断を下せるようになるためです。

上記を踏まえて、まずは天気予報を取得する tool を作りましょう。

weather_agent/tools/weather_tool.py を作成し、以下のように設定します。

weather_agent/tools/weather_tool.py
weather_agent/tools/weather_tool.py
import requests
from datetime import datetime
from typing import Dict, Any

def get_weather_forecast(lat: float = 35.6895, lon: float = 139.6917) -> Dict[str, Any]:
    """
    Open-Meteo APIから天気予報と過去の天気を取得する関数
    デフォルトは東京 (lat=35.6895, lon=139.6917)

    Args:
        lat (float): 緯度
        lon (float): 経度
        
    Returns:
        Dict[str, Any]: 天気情報を含む辞書
    """
    
    # 今日の日付を取得
    today = datetime.now()
    today_str = today.strftime('%Y-%m-%d')
    
    # 天気予報APIエンドポイント
    forecast_url = "https://api.open-meteo.com/v1/forecast"
    
    # パラメータ設定: 最高気温、最低気温、降水確率
    params = {
        "latitude": lat,
        "longitude": lon,
        "daily": ["weathercode", "temperature_2m_max", "temperature_2m_min", "precipitation_probability_max"],
        "timezone": "Asia/Tokyo",
        "past_days": 1 # 過去1日分(昨日)のデータも含める
    }
    
    try:
        response = requests.get(forecast_url, params=params)
        response.raise_for_status()
        data = response.json()
        
        daily = data.get('daily', {})
        time_list = daily.get('time', [])
        
        # 今日のインデックスを探す
        try:
            today_idx = time_list.index(today_str)
        except ValueError:
            # もし日付が見つからない場合は先頭と仮定
            today_idx = 0

        # 昨日のインデックス(なければ None)
        yesterday_idx = today_idx - 1 if today_idx > 0 else None
        
        weather_info = {
            "location": f"Lat: {lat}, Lon: {lon}",
            "today": {
                "date": today_str,
                "max_temp": daily['temperature_2m_max'][today_idx],
                "min_temp": daily['temperature_2m_min'][today_idx],
                "precip_prob": daily['precipitation_probability_max'][today_idx],
                "weather_code": daily['weathercode'][today_idx]
            }
        }
        
        if yesterday_idx is not None and yesterday_idx >= 0:
            weather_info["yesterday"] = {
                "date": time_list[yesterday_idx],
                "max_temp": daily['temperature_2m_max'][yesterday_idx],
                "min_temp": daily['temperature_2m_min'][yesterday_idx],
                "precip_prob": daily['precipitation_probability_max'][yesterday_idx],
                "weather_code": daily['weathercode'][yesterday_idx]
            }
            
            # 変化の計算
            temp_diff_max = weather_info["today"]["max_temp"] - weather_info["yesterday"]["max_temp"]
            temp_diff_min = weather_info["today"]["min_temp"] - weather_info["yesterday"]["min_temp"]
            
            weather_info["comparison"] = {
                "temp_diff_max": round(temp_diff_max, 1),
                "temp_diff_min": round(temp_diff_min, 1)
            }
        else:
            weather_info["comparison"] = "昨日との比較データなし"

        return weather_info

    except Exception as e:
        return {"error": str(e)}

tools を作ったら、エージェントが呼び出せるように設定します。

メインの weather_agent/agent.py を以下のように書き換えてみます。

weather_agent/agent.py
weather_agent/agent.py
from google.adk import Agent
from .tools.weather_tool import get_weather_forecast

root_agent = Agent(
    model='gemini-2.5-flash',
    name='weather_agent',
    description='A weather assistant that reports daily forecast to Slack.',
    instruction='''
    あなたは天気予報を通知する親切なアシスタントです。ユーザーの要望に対して、必要なツールを使用して回答を作成してください。
    
    また、気象情報については以下の項目を含めるようにしてください。
       - 今日の天気概況(天気、最高気温、最低気温、降水確率)
       - 昨日との気温差(寒くなったか、暑くなったか)
       - 気温に応じた服装へのアドバイス(例:「昨日より寒いのでコートが必要です」など)
       - 傘が必要かどうか
    ''',
    tools=[get_weather_forecast]
)

では、この状態から以下のコマンドで Web UI を起動してみます。

# プロジェクトルートで実行
uv run adk web .

Web UIが起動するので、天気を聞いてみます。

CleanShot 2025-12-16 at 22.21.34.png

実際に、今日の天気が返ってきました。
回答の前に、今追加した get_weather_forecast を使用していることがわかりますね。

Slack tools を作る

気象情報の tool ができたので、次は Slack tools を作っていきます。
本来は無理に tools にする必要はないんですが、活用ケースの紹介という目的でこの実装にしています。

事前準備: SlackAppを作成し、トークンを取得する

事前準備として、Slack App を作成しておきます。以下の記事がわかりやすいです。

https://dev.classmethod.jp/articles/post-messages-to-slack-channel/

注意点として、ワークスペースにインストールするときに インストールするボットユーザーがありません といったエラーが出るケースがあります。

CleanShot 2025-12-16 at 23.31.33.png

これは、Display Name が未入力のときに発生するエラーです。このエラーが発生した場合は、以下の部分が入力されているか確認してください。

CleanShot 2025-12-16 at 19.18.16.png

ワークスペースへのインストールが完了したら、以下の部分から OAuth Token をコピーします。コピーした値を、次の手順で使います。

CleanShot 2025-12-16 at 19.21.11.png

実装

では、 tool の実装に入ります。

まず、先ほどのトークンと、通知先チャンネルの ID を weather_agent/.env に追記します。

weather_agent/.env
GOOGLE_GENAI_USE_VERTEXAI=True
GOOGLE_CLOUD_PROJECT={{ プロジェクトID }}
GOOGLE_CLOUD_LOCATION=us-central1
LOCATION=us-central1

+ # Slack Configuration
+ SLACK_BOT_TOKEN={{ 取得したトークン }}
+ SLACK_CHANNEL_ID={{ 通知先チャンネルID }}

そして、 weather_agent/tools/slack_tool.py を作成します。内容は以下の通りです。

weather_agent/tools/slack_tool.py
weather_agent/tools/slack_tool.py
import requests
import os
from typing import Optional

def send_slack_notification(message: str, channel_id: Optional[str] = None) -> str:
    """
    Slackにメッセージを送信する関数 (OAuth Token使用)。
    
    Args:
        message (str): 送信するメッセージ本文
        channel_id (str, optional): 送信先のチャンネルID (e.g., C12345678)。
                                    指定がない場合は環境変数 SLACK_CHANNEL_ID を使用します。
        
    Returns:
        str: 送信結果のステータス
    """
    token = os.environ.get('SLACK_BOT_TOKEN')
    if not token:
        return "Error: SLACK_BOT_TOKEN environment variable is missing."

    if channel_id is None:
        channel_id = os.environ.get('SLACK_CHANNEL_ID')

    if not channel_id:
        return "Error: Channel ID is missing. Please provide it as an argument or set SLACK_CHANNEL_ID environment variable."

    url = "https://slack.com/api/chat.postMessage"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    payload = {
        "channel": channel_id,
        "text": message
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response_data = response.json()
        
        if response.status_code == 200 and response_data.get("ok"):
            return "Success: Message sent to Slack."
        else:
            error_msg = response_data.get("error", "Unknown error")
            return f"Error: Failed to send message. API Error: {error_msg}"
            
    except Exception as e:
        return f"Error: {str(e)}"

そして、追加した tool を weather_agent/agent.py に追記します。

from google.adk import Agent
from .tools.weather_tool import get_weather_forecast
+ from .tools.slack_tool import send_slack_notification

root_agent = Agent(
    model='gemini-2.5-flash',
    name='weather_agent',
    description='A weather assistant that reports daily forecast to Slack.',
    instruction='''
    あなたは天気予報を通知する親切なアシスタントです。
    
    ユーザーに天気を聞かれたら、以下のように回答してください。
    1. `get_weather_forecast` ツールを使用して、東京(デフォルト)の天気を取得してください。
       - 特に指定がなければ緯度経度はデフォルト値を使用してください。
    2. 取得したデータを分析し、以下の項目を含むメッセージを作成してください
       - 今日の天気概況(天気、最高気温、最低気温、降水確率)
       - 昨日との気温差(寒くなったか、暑くなったか)
       - 気温に応じた服装へのアドバイス(例:「昨日より寒いのでコートが必要です」など)
       - 傘が必要かどうか
    ''',
+     tools=[get_weather_forecast, send_slack_notification]
)

これで、Slack に投稿することができるようになりました。

試しに、エージェントを起動して「今日の天気を調べて、Slackに通知して」と送ってみます。

天気を調べてSlackに通知してもらう

先ほど設定した Slack tool を使い、Slack に通知するところまでできるようになっています。
Slack には以下のようにメッセージが届いています。

天気を通知してもらったSlackのメッセージ

これで、「天気を調べて、Slack に通知するエージェント」が完成しました!

単一の API エンドポイントで動作させる

次に、Cloud Scheduler で自動実行するためのエンドポイントを作成します。

というのも、 ADK でやり取りをするためには、セッションの作成が必要になります。つまり セッション作成 → リクエスト実行といった2つの手順を踏む必要がある ため、APIを2回叩く必要があります。Cloud Scheduler だけで自動実行するにはこれだと考えることが増えてしまうので、自動実行用のエンドポイントを1つ作成します。

まず、 以下のコマンドで uvicorn をインストールします。

$ uv add uvicorn

次に、 main.py を以下のように書き換えます。

main.py
main.py
import uuid
from fastapi import Request
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.genai.types import Content, Part
from google.adk.cli.fast_api import get_fast_api_app
from weather_agent.agent import root_agent

app = get_fast_api_app(agents_dir=".", web=False)

@app.post("/daily_run")
async def daily_run(request: Request):
    # セッション作成
    session_service = InMemorySessionService()
    runner = Runner(agent=root_agent, app_name="weather_agent", session_service=session_service)
    session_id = str(uuid.uuid4())
    user_id = "scheduler"
    app_name = "weather_agent"
    await session_service.create_session(app_name=app_name, user_id=user_id, session_id=session_id)
    
    # プロンプト設定
    query_text = "東京の今日の天気を調べて、Slackに通知して"
    content = Content(role="user", parts=[Part(text=query_text)])
    
    # Execute agent
    async for event in runner.run_async(
        user_id=user_id,
        session_id=session_id,
        new_message=content
    ):
        pass # Simply consume events to trigger execution
    
    await session_service.delete_session(app_name=app_name, user_id=user_id, session_id=session_id)

    return {"status": "ok", "session_id": session_id}

これで準備は完了です。まず、以下のコマンドでサーバーを起動します。

uv run uvicorn main:app --host 0.0.0.0 --port 8080 --reload --env-file weather_agent/.env

この状態で、別のターミナルを起動して以下のコマンドで動作を確認してみましょう。

curl -X POST http://localhost:8080/daily_run

成功すると、以下のような成功レスポンスと共にSlackにメッセージが届くことが確認できると思います。

ターミナルの実行結果

Cloud Run にデプロイする

今回は gcloud run deploy コマンドを使ってデプロイを行います。 adk コマンドを使わない理由としては、自作の Dockerfile を利用する必要があるためです。

まず、 Artifact Registory を作成し、イメージをプッシュします。

# 環境変数の設定
export PROJECT_ID=<your-project-id>
export REGION=us-central1
export SLACK_BOT_TOKEN=<your-token>
export SLACK_CHANNEL_ID=<your-channel-id>

gcloud config set project $PROJECT_ID

# Artifact Registry リポジトリの作成 (初回のみ)
gcloud artifacts repositories create adk-repo --repository-format=docker \
    --location=$REGION --description="Docker repository for ADK agents"

# ビルド & プッシュ
gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/adk-repo/weather-agent:latest

イメージがプッシュできたら、次のコマンドでデプロイします

gcloud run deploy weather-agent \
    --image $REGION-docker.pkg.dev/$PROJECT_ID/adk-repo/weather-agent:latest \
    --platform managed \
    --region $REGION \
    --no-allow-unauthenticated \
    --memory 1Gi \
    --timeout 300s \
    --set-env-vars GOOGLE_CLOUD_PROJECT=$PROJECT_ID \
    --set-env-vars GOOGLE_GENAI_USE_VERTEXAI=true \
    --set-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
    --set-env-vars SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN \
    --set-env-vars SLACK_CHANNEL_ID=$SLACK_CHANNEL_ID

デプロイが完了すると、以下のように Service URL が表示されます。

gcloud run deploy

このURLの末尾に /daily_run を付与して、実行してみます。

export SERVICE_URL=<your-service-url>

curl -X POST "$SERVICE_URL/daily_run" \
    -H "Authorization: bearer $(gcloud auth print-identity-token)" \
    -H "Content-Type: application/json"

daily_run

こちらでも、エラーが出ずに成功していればOKです。Slackにも通知が届いているはずです。

Cloud Scheduler で自動化する

最後に、これを自動化していきます。まず、サービスアカウントを作成し、権限を付与します。

# サービスアカウント作成
gcloud iam service-accounts create weather-invoker --display-name "Weather Agent Invoker"

# 権限付与
gcloud run services add-iam-policy-binding weather-agent \
    --region $REGION \
    --member="serviceAccount:weather-invoker@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/run.invoker"

次に、毎日実行するように設定します。この例では、毎朝7時に実行するようにしてみます。

gcloud scheduler jobs create http weather-daily-job \
    --schedule "0 7 * * *" \
    --time-zone "Asia/Tokyo" \
    --uri "$SERVICE_URL/daily_run" \
    --http-method POST \
    --oidc-service-account-email "weather-invoker@$PROJECT_ID.iam.gserviceaccount.com" \
    --location $REGION

実際に Cloud Scheduler が作成されていることがコンソールから確認できたら、強制実行で動作確認をしてみます。

強制実行

無事に Slack に通知が届いていたら完了です!

最後に

以上で、ADKでのお天気通知エージェントの構築が完了しました。今回のシナリオ程度のユースケースであればスクリプトによる処理でも完結させることはできますが、ツールを適切に判断して使用することが求められるケースでは、ADK を使うことで「何をすべきか」という点もAIに判断させることができ、複雑なタスクを自動化することも可能になってきます。

また、自動実行するようなケースになると、セッションの管理も必要になります。ここは単純な Vertex AI API を使用する場合とは異なるポイントのため、ユースケース次第では単なるスクリプトで完結させるのも良いかもしれません。

色々な活用の方法がある ADK なので、皆さんも業務を改善したり、自分の日常生活をちょっと良くしたりといった活用方法をぜひ考えてみてください。
この記事が、少しでもそういったアイデアの助けになれば幸いです。

以上、すらぼでした!

この記事をシェアする

FacebookHatena blogX

関連記事