はじめに
こんにちは。
この記事は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
に以下のコードを追加してください。
※今回はツールの中身については解説しません。
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
ファイルに記述しておきましょう。
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
の定義を以下のように更新しましょう。
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つです。
-
tools=[get_weather]
:LlmAgent
のtools
パラメータに、先ほど作成したget_weather
関数をリスト形式で渡します。これだけで、ADKが自動的にget_weather
をFunctionTool
として認識し、エージェントが利用できるようになります。 -
instruction
の更新: エージェントにツールを渡しただけでは、いつ使えばいいのか分かりません。そこでinstruction
を更新し、「どんな時に、どのようにツールを使ってほしいか」を具体的に教えます。
今回は、「天気について聞かれたらget_weather
ツールを使ってね」「日付の指定がなければ"today"、明日なら"tomorrow"を引数にしてね」と、かなり具体的に指示しています。この丁寧な指示が、エージェントの精度を大きく左右します。
実際に動かしてみよう!
ターミナルでadk web
コマンドを実行して、開発UIを起動しましょう。
# trip-agent/ ディレクトリ(プロジェクトのルート)で実行
adk web .
ブラウザでhttp://localhost:8000
にアクセスし、エージェントとしてtrip_planner
を選択してください。
まずは挨拶をしてみましょう。
instructionの記述内容の通りに自己紹介をしてくれます。
では、今回の本題、天気について質問してみましょう。
すると、エージェントは次のように答えてくれるはずです。
LLMが持っているはずのない、リアルタイムな情報(APIから取得した情報)を返してくれました。大成功です!
ツールがどう呼ばれるのか確認しよう!
なぜエージェントは天気を答えられたのでしょうか?
adk web
の左側にあるEventsタブからリクエストの中身を確認してみましょう。
TripPlannerAgent
が「今日の東京の天気を教えて」というユーザーリクエストから「今日」「東京」というワードを抜き出し、date_str="Today"
、city="Tokyo"
という引数を抽出し、get_weather
関数を呼んでいることが分かります。
レスポンスの内容も見てみましょう。
呼び出しは成功したのでstatus
にはsuccess
が入っていますね。APIの呼び出し結果もcity_name="東京都"
,weather_description="薄い雲"
,temperature=29.74
とそれぞれの値に設定されていることが分かります。
この結果をもとに、Geminiが「今日の東京都の天気は薄い雲、気温は29.74度です。」という最終的なレスポンスを生成しています。
このように、LlmAgent
はユーザーとの対話とツールの結果を総合的に判断して、自然な応答を生成しているのです。
今度はToolを使わずに、LLM自身の知能と、先ほど調べた天気の情報をもとに、お出かけプランを回答してくれています。
FunctionToolをもう1つ追加してみよう!
次に簡単な電卓ツールを追加してみます。
まずはToolを作成します。
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}
次にLLMAgent
のinstruction
とtool
を修正します。
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です。
では早速動作確認してみましょう。
get_weather
ではなくcaluculator
を使って回答してくれていますね!
このように複数のToolを登録した場合でも、docstring
とinstruction
にしっかり使い方を記載してあげれば最適なToolを選んで実行してくれます。
まとめ
今回は、エージェントに「ツール」を与える方法を学びました。
①ツールの実体となる自作関数のdocstring
に使い方を記述
②LLMAgent
のinstruction
にいつどうやってツールを使うのかを記述
③tools
のリストに自作関数名を追加
これだけでエージェントにツールを追加できます。
簡単ですね!
これにより、エージェントは外部APIやWeb検索の能力を手に入れ、単なる対話モデルから、タスクを実行できるエージェントに進化できました!
次回、「Agent Development Kit(ADK)で爆速AIエージェント開発」シリーズの第3回では、ADKでマルチエージェントを構築する方法について解説します!
お楽しみに!