0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Toolsで外部API連携!【Agent Development Kit(ADK)で爆速AIエージェント開発②】

Last updated at Posted at 2025-07-27

はじめに

こんにちは。
この記事はAgent Development Kit(ADK)で爆速AIエージェント開発シリーズの第2回となります。
第1回の記事はこちらです。
たったの9行!ADKでシンプルなAIエージェントを構築しよう!【Agent Development Kit(ADK)で爆速AIエージェント開発①】
前回は、自己紹介をするだけのシンプルなエージェントを作成しました。
しかし、このシンプルなエージェントはリアルタイムに外部の情報得たり、インターネットから情報を検索することはできませんでした。

例えば、「横浜の今日の天気は?」と聞かれたとします。LLMは学習データに含まれる一般的な知識は持っていても、今この瞬間のリアルタイムな天気は知りません。

そこで今回は、エージェントに外部のAPIと連携する「ツール」の使い方を教えます。これにより、エージェントにリアルタイムな情報をもとに応答させたり、特定のタスクを実行させることができるようになります。

この記事のゴール

  • 自作のPython関数をFunctionToolを使ってエージェントのツールにする。
  • エージェントがユーザーの質問に応じて、適切なツールを自律的に判断し、実行できるようになる。
  • 複数のFunctionToolを登録する。

ADKにおける「ツール」とは?

ADKにおけるツールとは、エージェントにLLMの知識だけでは不可能な能力を与えるための強力な仕組みです。これにより、エージェントは外部の世界と対話し、情報を取得し、アクションを実行できるようになります。

ADKでは、様々な種類のツールが利用できます。

  • FunctionTool: 自作したPythonの関数を、ツールとしてエージェントに渡せます。今回のこれを使って天気情報を取得させます。
  • 組み込みツール: google_searchなど、ADKが標準で提供している便利なツールです。
  • Agent-as-a-Tool: 他のエージェント自体をツールとして呼び出す、高度な連携も可能です。

※ADKでは、1つのエージェントに2つ以上の組み込みツールを登録することはできません。2つ以上登録したい場合は、Agent-as-a-Toolを使う必要があります。こちらは別の記事で解説予定です。
今回は、自作のFunctionToolで天気APIと連携し、エージェントを作っていきましょう。

実習

天気予報ツールを作ろう!

まずは、エージェントに与えるツールを作成します。今回は、無料で利用できるOpenWeatherMapのAPIを使って、指定された都市の天気情報を取得する関数get_weatherを作成します。

trip_planner/agent.pyに以下のコードを追加してください。
※今回はツールの中身については解説しません。

trip_planner/agent.py
import os
import requests
from dotenv import load_dotenv
from datetime import date, datetime, timedelta, timezone

from google.adk.agents import LlmAgent
from google.adk.tools import google_search

load_dotenv()


def get_weather(city: str, date_str: str = "today") -> Dict[str, Any]:
    """指定された都市の指定された日付の天気予報を取得します。

    Args:
        city (str): 天気を調べたい都市の英語名 (例: "Tokyo", "Yokohama", "Osaka")。
        date_str (str): 予報を知りたい日付。
                        "today" (今日), "tomorrow" (明日), または "YYYY-MM-DD" 形式で指定します。
                        指定がない場合は今日の天気を返します。

    Returns:
        dict: 天気情報を含む辞書。
              成功時は {"status": "success", "city_name": "都市名", "weather_description": "天気", ...} の形式。
              失敗時は {"status": "error", "error_message": "エラー内容"} の形式。
    """
    api_key = os.getenv("OPENWEATHERMAP_API_KEY")

    today = date.today()
    try:
        if date_str.lower() == 'today':
            target_date = today
        elif date_str.lower() == 'tomorrow':
            target_date = today + timedelta(days=1)
        else:
            target_date = datetime.strptime(date_str, "%Y-%m-%d").date()
    except ValueError:
        return {
            "status": "error",
            "error_message": f"日付の形式が正しくありません。'{date_str}' は無効です。'today', 'tomorrow', 'YYYY-MM-DD' のいずれかで指定してください。"
        }

    if target_date == today:
        base_url = "http://api.openweathermap.org/data/2.5/weather"
        params = {"q": city, "appid": api_key, "units": "metric", "lang": "ja"}
        try:
            response = requests.get(base_url, params=params)
            response.raise_for_status()
            data = response.json()
            return {
                "status": "success",
                "city_name": data["name"],
                "weather_description": data["weather"][0]["description"],
                "temperature": data["main"]["temp"]
            }
        except requests.exceptions.RequestException as e:
            return {"status": "error", "error_message": f"今日の天気の取得中にAPIリクエストで問題が発生しました。{e}"}
        except KeyError:
            return {"status": "error", "error_message": "今日の天気のAPIレスポンス形式が不正です。"}

    elif today < target_date <= today + timedelta(days=5):
        base_url = "http://api.openweathermap.org/data/2.5/forecast"
        params = {"q": city, "appid": api_key, "units": "metric", "lang": "ja"}
        try:
            response = requests.get(base_url, params=params)
            response.raise_for_status()
            data = response.json()
            city_name = data["city"]["name"]

            target_forecasts = []
            jst = timezone(timedelta(hours=9))
            for forecast in data["list"]:
                forecast_dt_jst = datetime.fromtimestamp(forecast["dt"], tz=jst)
                if forecast_dt_jst.date() == target_date:
                    target_forecasts.append(forecast)
            
            if not target_forecasts:
                return {
                    "status": "error",
                    "error_message": f"{city_name}{target_date.strftime('%Y年%m月%d日')}の予報データが見つかりませんでした。"
                }

            max_temp = max(f["main"]["temp_max"] for f in target_forecasts)
            min_temp = min(f["main"]["temp_min"] for f in target_forecasts)
            
            noon_forecast = min(
                target_forecasts, 
                key=lambda f: abs(12 - datetime.fromtimestamp(f["dt"], tz=jst).hour)
            )
            weather_description = noon_forecast["weather"][0]["description"]
            
            return {
                "status": "success",
                "city_name": city_name,
                "date": target_date.strftime('%Y年%m月%d日'),
                "weather_description": weather_description,
                "max_temperature": max_temp,
                "min_temperature": min_temp
            }
        except requests.exceptions.RequestException as e:
            return {"status": "error", "error_message": f"天気予報の取得中にAPIリクエストで問題が発生しました。{e}"}
        except (KeyError, IndexError):
            return {"status": "error", "error_message": "天気予報のAPIレスポンス形式が不正です。"}

    else:
        return {
            "status": "error",
            "error_message": "5日後までの天気予報のみ対応しています。"
        }

APIキーが必要なので、OpenWeatherMapにサインアップしてAPIキーを取得し、プロジェクトルートの.envファイルに記述しておきましょう。

.env
OPENWEATHERMAP_API_KEY="YOUR_API_KEY_HERE"

ここで重要なのが、関数のdocstringです。

"""指定された都市の指定された日付の天気予報を取得します。

Args:
    city (str): 天気を調べたい都市の英語名 (例: "Tokyo", "Yokohama", "Osaka")。
    date_str (str): 予報を知りたい日付。
                    "today" (今日), "tomorrow" (明日), または "YYYY-MM-DD" 形式で指定します。
                    指定がない場合は今日の天気を返します。

Returns:
    dict: 天気情報を含む辞書。
          成功時は {"status": "success", "city_name": "都市名", "weather_description": "天気", ...} の形式。
          失敗時は {"status": "error", "error_message": "エラー内容"} の形式。
"""

ADKは、このdocstringと引数の型ヒントを解析し、LLMが「このツールは何をするもので、どうやって使えばいいのか」を理解するための情報として利用します。
つまり、分かりやすいdocstringを書くことが、賢いエージェントにつながるのです。

エージェントにツールを登録し、使い方を教えよう!

関数が準備できたら、次はツールの設定と、ツールの使い方を指示します。LlmAgentの定義を以下のように更新しましょう。

trip_planner/agent.py
root_agent = LlmAgent(
    name="TripPlannerAgent",
    model="gemini-2.5-pro",
    instruction="""
    あなたはお出かけプランを提案する、親切で優秀なアシスタントです。
    まず自己紹介をして、いつ、どこに行きたいかユーザーに尋ねてください。

    ユーザーからの質問に応じて、以下のツールを適切に利用してください。

    - **天気予報 (`get_weather`)**: 天気を尋ねられた場合に使用します。
      - このツールは、都市名(`city`)と日付(`date_str`)を引数に取ります。
      - 成功すると、`city_name`や`weather_description`を含む辞書を返します。その内容を元に自然な文章で応答してください。

    - **上記以外の一般的なお出かけプランの相談**については、あなたの知識を活かして、ユーザーと楽しく対話を進めてください。
    """,
    description="天気予報機能を持ったお出かけプラン提案アシスタント",
    tools=[
        get_weather, 
    ],
)

変更点は2つです。

  1. tools=[get_weather]: LlmAgenttoolsパラメータに、先ほど作成したget_weather関数をリスト形式で渡します。これだけで、ADKが自動的にget_weatherFunctionToolとして認識し、エージェントが利用できるようになります。
  2. instructionの更新: エージェントにツールを渡しただけでは、いつ使えばいいのか分かりません。そこでinstructionを更新し、「どんな時に、どのようにツールを使ってほしいか」を具体的に教えます。

今回は、「天気について聞かれたらget_weatherツールを使ってね」「日付の指定がなければ"today"、明日なら"tomorrow"を引数にしてね」と、かなり具体的に指示しています。この丁寧な指示が、エージェントの精度を大きく左右します。

実際に動かしてみよう!

ターミナルでadk webコマンドを実行して、開発UIを起動しましょう。

# trip-agent/ ディレクトリ(プロジェクトのルート)で実行
adk web .

ブラウザでhttp://localhost:8000にアクセスし、エージェントとしてtrip_plannerを選択してください。

まずは挨拶をしてみましょう。
instructionの記述内容の通りに自己紹介をしてくれます。
スクリーンショット 2025-07-25 22.15.05のコピー.png

では、今回の本題、天気について質問してみましょう。
すると、エージェントは次のように答えてくれるはずです。

スクリーンショット 2025-07-25 22.03.02のコピー.png

LLMが持っているはずのない、リアルタイムな情報(APIから取得した情報)を返してくれました。大成功です!

ツールがどう呼ばれるのか確認しよう!

なぜエージェントは天気を答えられたのでしょうか?
adk webの左側にあるEventsタブからリクエストの中身を確認してみましょう。
スクリーンショット 2025-07-25 22.01.12のコピー.png
TripPlannerAgentが「今日の東京の天気を教えて」というユーザーリクエストから「今日」「東京」というワードを抜き出し、date_str="Today"city="Tokyo"という引数を抽出し、get_weather関数を呼んでいることが分かります。

レスポンスの内容も見てみましょう。
スクリーンショット 2025-07-25 22.01.40.png
呼び出しは成功したのでstatusにはsuccessが入っていますね。APIの呼び出し結果もcity_name="東京都",weather_description="薄い雲",temperature=29.74とそれぞれの値に設定されていることが分かります。
この結果をもとに、Geminiが「今日の東京都の天気は薄い雲、気温は29.74度です。」という最終的なレスポンスを生成しています。

このように、LlmAgentはユーザーとの対話とツールの結果を総合的に判断して、自然な応答を生成しているのです。

今度は観光スポットについて聞いてみましょう。
スクリーンショット 2025-07-25 22.02.52.png

今度はToolを使わずに、LLM自身の知能と、先ほど調べた天気の情報をもとに、お出かけプランを回答してくれています。

FunctionToolをもう1つ追加してみよう!

次に簡単な電卓ツールを追加してみます。
まずはToolを作成します。

trip_planner/agent.py
def calculator(
    number1: float, 
    number2: float, 
    operation: Literal["add", "subtract", "multiply", "divide"]
) -> Dict[str, Any]:
    """2つの数値の四則演算を実行します。

    Args:
        number1 (float): 1つ目の数値。
        number2 (float): 2つ目の数値。
        operation (str): 実行する演算。"add"(足し算), "subtract"(引き算), "multiply"(掛け算), "divide"(割り算)から選択。

    Returns:
        dict: 計算結果を含む辞書。
              成功時は {"status": "success", "result": 計算結果}。
              失敗時は {"status": "error", "error_message": "エラー内容"}。
    """
    if operation == "add":
        result = number1 + number2
    elif operation == "subtract":
        result = number1 - number2
    elif operation == "multiply":
        result = number1 * number2
    elif operation == "divide":
        if number2 == 0:
            return {"status": "error", "error_message": "ゼロで割ることはできません。"}
        result = number1 / number2
    else:
        return {"status": "error", "error_message": f"無効な演算です: {operation}"}
    
    return {"status": "success", "result": result}

次にLLMAgentinstructiontoolを修正します。

trip_planner/agent.py
root_agent = LlmAgent(
    name="TripPlannerAgent",
    model="gemini-2.5-pro",
    instruction="""
    あなたはお出かけプランを提案する、親切で優秀なアシスタントです。
    まず自己紹介をして、いつ、どこに行きたいかユーザーに尋ねてください。

    ユーザーからの質問に応じて、以下のツールを適切に利用してください。

    - **天気予報 (`get_weather`)**: 天気を尋ねられた場合に使用します。
      - このツールは、都市名(`city`)と日付(`date_str`)を引数に取ります。
      - 成功すると、`city_name`や`weather_description`を含む辞書を返します。その内容を元に自然な文章で応答してください。

    - **電卓 (`calculator`)**: 四則演算が必要な場合に使用します。
      - 例えば「123円のパンを5個買うといくら?」や「56 * 78は?」のような質問に答えるために使ってください。
      - このツールは、2つの数値(`number1`, `number2`)と演算の種類(`operation`)を引数に取ります。
      - 成功すると、`result`キーを持つ辞書を返すので、その値をユーザーに伝えてください。

    - **上記以外の一般的なお出かけプランの相談**については、あなたの知識を活かして、ユーザーと楽しく対話を進めてください。
    """,
    description="天気予報と計算機能を持ったお出かけプラン提案アシスタント",
    tools=[
        get_weather,
        calculator, 
    ],
)

このようにinstructionsに追加したToolの使い方を記述し、toolsのリストに追加したTool名を追加してあげるだけでOKです。

では早速動作確認してみましょう。
スクリーンショット 2025-07-25 22.15.19.png
get_weatherではなくcaluculatorを使って回答してくれていますね!
このように複数のToolを登録した場合でも、docstringinstructionにしっかり使い方を記載してあげれば最適なToolを選んで実行してくれます。

まとめ

今回は、エージェントに「ツール」を与える方法を学びました。
①ツールの実体となる自作関数のdocstringに使い方を記述
LLMAgentinstructionにいつどうやってツールを使うのかを記述
toolsのリストに自作関数名を追加
これだけでエージェントにツールを追加できます。
簡単ですね!

これにより、エージェントは外部APIやWeb検索の能力を手に入れ、単なる対話モデルから、タスクを実行できるエージェントに進化できました!

次回、「Agent Development Kit(ADK)で爆速AIエージェント開発」シリーズの第3回では、ADKでマルチエージェントを構築する方法について解説します!
お楽しみに!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?