G-gen の杉村です。Agent Development Kit(ADK)は、Google Cloud が提供する、AI エージェント開発のためのフレームワークです。当記事では、ADK の中核をなす LLM Agent と Workflow Agents、および Tools を解説します。
概要
Agent Development Kit(ADK)とは
Agent Development Kit(ADK)は、AI エージェントの開発、デプロイ、評価を効率化するために Google が開発したオープンソースのフレームワークです。2025年7月現在、Python および Java 向けのライブラリとして公開されています。
ADK を利用することで、開発者は通常のソフトウェア開発と同じような感覚で、モジュール化された再利用可能なコンポーネントを組み合わせて AI エージェントを開発できます。ADK は Google の生成 AI モデル Gemini や Vertex AI などのエコシステムに最適化されていますが、特定のモデルやデプロイ環境に依存しない設計思想を持っており、高い柔軟性と拡張性を備えています。
ADK のコンポーネント
ADK でアプリを開発する際、以下のようなコンポーネントを記述します。
Agents
タスクを実行する主体。LLM の推論能力を活用して自律的に動作する LLM Agent と、事前に定義された手順に従って動作する Workflow Agent がある。
Tools
エージェントがタスクをこなすために利用するプログラム等。外部 API の呼び出しや計算、データベース検索など、特定の処理を行う。
LLM Agent
概要
LLM Agent は、LLM の持つ推論能力を利用して、タスクを達成するために思考したり、どの Tool をどの順番で、どのようなパラメータで呼び出すかなどを自律的に判断するエージェントです。
ADK では基本的に、この LLM Agent を組み合わせて AI エージェントアプリケーションを開発しますので、最も基本的なコンポーネントであるといえます。
- 参考 : LLM Agent
実装例
LLM Agent を定義する際は、主に以下の要素を指定します。
属性 | 説明 |
---|---|
instruction | エージェントの役割、振る舞い、タスクの進め方などを自然言語で記述した指示書 |
model | 使用するモデル(gemini-2.5-flash など) |
tools | エージェントが利用可能なツール(道具)。Python の関数や他のエージェントなど |
以下は、都市ごとの現在時刻を返す簡単な Tool を持つ LLM Agent の Python サンプルコードです。このエージェントに「東京の天気は?」と質問すると、LLM は get_weather ツールを city="東京" という引数で呼び出すべきだと判断(Reasoning)し、実行します。そして、Tool から返された「晴れです。」という結果(Observation)を元に、最終的な回答を生成します。
import asyncio import datetime from google.adk.agents import LlmAgent from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService from google.genai import types def get_time(city: str): ''' # 概要 指定された都市の現在時刻を取得します。 # 引数 city (str): 対象の都市名 (Tokyo / Los Angels / London) ''' print(f"--- Tool: get_weather (city: {city}) ---") if "Tokyo" in city: tz = datetime.timezone(datetime.timedelta(hours=9)) elif "Los Angels" in city: tz = datetime.timezone(datetime.timedelta(hours=-7)) elif "London" in city: tz = datetime.timezone(datetime.timedelta(hours=0)) else: tz = None if tz: now = datetime.datetime.now(tz) else: now = None message = { "city": city, "datetime": now } return message # LLM Agentの定義 time_agent = LlmAgent( model="gemini-2.5-flash", name="time_agent", instruction=""" あなたは時刻案内をするエージェントです。 ユーザーから尋ねられた都市の現在時刻を答えてください。 利用可能なツールを使って情報を取得し、最終的な回答を生成してください。 """, tools=[get_time] ) # runner の定義 APP_NAME = "My Agent App" SESSION_SERVICE = InMemorySessionService() RUNNER = Runner( agent=time_agent, app_name=APP_NAME, session_service=SESSION_SERVICE, ) async def stream_agent_events(query: str, user_id: str, session_id: str): ''' # 概要 エージェントを実行します。 # 引数 query (str): エージェントに投げかけるクエリ(プロンプト) user_id (str): ユーザー ID session_id (str): セッション ID ''' # 既存セッションがなければ新規作成 session = await SESSION_SERVICE.get_session(app_name=APP_NAME, user_id=user_id, session_id=session_id) if not session: await SESSION_SERVICE.create_session(app_name=APP_NAME, user_id=user_id, session_id=session_id) # ユーザークエリを所定の形式で格納 content = types.Content(role='user', parts=[types.Part(text=query)]) # エージェントの実行 async for event in RUNNER.run_async(user_id=user_id, session_id=session_id, new_message=content): parts = event.content.parts message = event.content.parts[0].text if message: yield(f"--- Message: {message}") return async def main(): """ エージェントを実行し、ストリーミングされたメッセージを出力するメイン関数 """ user_id="test_user" session_id="test_session" query="東京の現在時刻は?" # async for を使って非同期ジェネレータを処理 async for message in stream_agent_events(query=query, user_id=user_id, session_id=session_id): print(message) # スクリプトのエントリーポイント if __name__ == "__main__": asyncio.run(main())
ソースコードの実行
ADK で記述したエージェントは、以下で紹介されているように、ADK に組み込まれた動作テスト用 Web UI を使って動作確認することができます。

あるいは、以下の記事のように Vertex AI Agent Engine にデプロイして、リモートから呼び出して実行することもできます。
しかし今回の記事では、Cloud Run などにホストされたアプリケーションに組み込むことを想定して、異なる方法で実行します。
まずは、前述のソースコードを main.py として保存します。
実行環境には、pip install google-adk
で ADK をインストールします。必要に応じて venv を用意してください。
さらに、以下のようなコマンドを実行して、環境変数を定義します。これらの環境変数は、ADK が Gemini モデルを呼び出す先の API エンドポイントを決めるのに必要です。.env
ファイルなどに書き込んでおき、実行前に source .env
コマンドを実行する方法で変数を定義しても構いません。実際の動作環境では、Cloud Run service のリビジョンに環境変数で定義する想定です。
export GOOGLE_GENAI_USE_VERTEXAI="TRUE" export GOOGLE_CLOUD_PROJECT="my-project" export GOOGLE_CLOUD_LOCATION="global"
上記を実施したうえで main.py
を実行すると、以下のように出力されます。
$ python main.py --- Tool: get_weather (city: Tokyo) --- --- Message: 東京の現在時刻は2025年7月21日9時36分です。
Runner と非同期処理
前述のソースコードの関数 stream_agent_events
は、asyncio.run
によって非同期実行されています。asyncio
は Python の標準ライブラリであり、並行処理を実現するための機能です。
Runner は、エージェントを動かすためのエンジンです。run_async でプロンプトを渡して実行すると、イベントを順次返します。このソースコードでは非同期関数 stream_agent_events
の中で Runner を実行しており、エージェントにより生成された結果は yield によって順次、呼び出し元に返されて print されます。
これにより、複数のエージェントによって長い処理が行われる場合でも、処理の進行状況をユーザーに知らせることができます。
Workflow Agents
概要
Workflow Agents は、あらかじめ定義された順番でサブエージェント(子エージェント)を呼び出すためのエージェントです。Workflow Agents の動作は決定論的、つまり事前に定義したとおりに動くものであり、必ず指定した順番でサブエージェントが実行されます。モデルの指定も存在しません。
- 参考 : Workflow Agents
Workflow Agents には以下の種類があります。
名称 | 説明 |
---|---|
Sequential Agents | サブエージェントを指定した順番で順次実行 |
Parallel Agents | サブエージェントを並列で実行 |
Loop Agents | サブエージェントを指定した順番で順次実行。指定条件が満たされるまで繰り返す |
Workflow Agents は、その制御性の高さから以下のようなユースケースに適しています。
- 顧客からの問い合わせを定型的なプロセスで処理する
- 例: 内容判断 → チケット起票 → 担当者割り当て
- 決まった手順で行われるデータ処理やバッチ処理の自動化
- ビジネスプロセスオートメーション(BPA)
実装例
サブエージェントを順次実行する Sequential Agents の実装例を以下に示します。
import asyncio import datetime import logging from google.adk.agents import LlmAgent, SequentialAgent from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService from google.adk.tools import google_search from google.genai import types def get_time(city: str): ''' # 概要 指定された都市の現在時刻を取得します。 # 引数 city (str): 対象の都市名 (Tokyo / Los Angels / London) ''' print(f"--- Tool: get_weather (city: {city}) ---") if "Tokyo" in city: tz = datetime.timezone(datetime.timedelta(hours=9)) elif "Los Angels" in city: tz = datetime.timezone(datetime.timedelta(hours=-7)) elif "London" in city: tz = datetime.timezone(datetime.timedelta(hours=0)) else: tz = None if tz: now = datetime.datetime.now(tz) else: now = None message = { "city": city, "datetime": now } return message # 時刻案内エージェント time_agent = LlmAgent( model="gemini-2.5-flash", name="time_agent", instruction=""" あなたは時刻案内をするエージェントです。 tools を使ってユーザーが住んでいる都市の現在時刻を取得します。 """, tools=[get_time], output_key = "city_and_datetime" ) # 雑学エージェント trivia_agent = LlmAgent( model="gemini-2.5-flash", name="time_agent", instruction=""" # 役割 あなたは雑学を披露するエージェントです。 ユーザーが住んでいる国にとって、今日がなんの日か(その国の祝日、記念日など)を調べて回答します。 どうしても特別な日でないときは、最も近い祝日や記念日などを返してください。 # ユーザーが住んでいる国と日付 {city_and_datetime} """, tools=[google_search] ) # シーケンシャルエージェント root_agent = SequentialAgent( name="root_agent", sub_agents=[time_agent, trivia_agent], description="本日日付を取得し、次にその日に関する雑学を披露する", ) # runner の定義 APP_NAME = "My Agent App" SESSION_SERVICE = InMemorySessionService() RUNNER = Runner( agent=root_agent, app_name=APP_NAME, session_service=SESSION_SERVICE, ) async def stream_agent_events(query: str, user_id: str, session_id: str): ''' # 概要 エージェントを実行します。 # 引数 query (str): エージェントに投げかけるクエリ(プロンプト) user_id (str): ユーザー ID session_id (str): セッション ID ''' # 既存セッションがなければ新規作成 session = await SESSION_SERVICE.get_session(app_name=APP_NAME, user_id=user_id, session_id=session_id) if not session: await SESSION_SERVICE.create_session(app_name=APP_NAME, user_id=user_id, session_id=session_id) # ユーザークエリを所定の形式で格納 content = types.Content(role='user', parts=[types.Part(text=query)]) # エージェントの実行 async for event in RUNNER.run_async(user_id=user_id, session_id=session_id, new_message=content): parts = event.content.parts message = event.content.parts[0].text if message: yield(f"--- Message: {message}") return async def main(): """ エージェントを実行し、ストリーミングされたメッセージを出力するメイン関数 """ user_id="test_user" session_id="test_session" query="私は東京在住です。" # async for を使って非同期ジェネレータを処理 async for message in stream_agent_events(query=query, user_id=user_id, session_id=session_id): print(message) # # ロギングを設定(必要に応じてコメントを外す) # logging.basicConfig( # level=logging.DEBUG, # format='%(asctime)s - %(levelname)s - %(name)s - %(message)s' # ) # スクリプトのエントリーポイント if __name__ == "__main__": asyncio.run(main())
実行結果
出力は以下のようになります。
$ python sequential.py --- Tool: get_weather (city: Tokyo) --- --- Message: 現在の時刻は、2025年7月21日9時40分です。 --- Message: 東京にお住まいのあなたにとって、2025年7月21日は「海の日」です。 海の日とは、海の恩恵に感謝し、海洋国家である日本の繁栄を願う国民の祝日です。 毎年7月の第3月曜日と定められており、2025年は7月21日がこれに当たります。 世界で唯一、「海の日」を国民の祝日としているのは日本だけです。
time_agent
と trivia_agent
の情報の受け渡しに使われている output_key
については、以下の記事も参照してください。
詳細のロギング
前述のソースコード内でコメントアウトしているロギング設定の行から # を削除してコメントでなくすると、ADK の処理の詳細が標準出力に出力されます。エージェントがどのような順番で呼び出され、またどのようなプロンプトで呼び出されたのかがわかるため、デバッグやチューニングのときに役立ちます。
以下は、一部を省略していますが、詳細なロギング出力の結果です。
2025-07-21 01:11:24,627 - DEBUG - asyncio - Using selector: EpollSelector 2025-07-21 01:11:24,926 - INFO - google_adk.google.adk.models.google_llm - Sending out request, model: gemini-2.5-flash, backend: GoogleLLMVariant.VERTEX_AI, stream: False 2025-07-21 01:11:24,926 - INFO - google_adk.google.adk.models.google_llm - LLM Request: ----------------------------------------------------------- System Instruction: あなたは時刻案内をするエージェントです。 tools を使ってユーザーが住んでいる都市の現在時刻を取得します。 You are an agent. Your internal name is "time_agent". ----------------------------------------------------------- Contents: {"parts":[{"text":"私は東京在住です。"}],"role":"user"} ----------------------------------------------------------- Functions: get_time: {'city': {'type': <Type.STRING: 'STRING'>}} ----------------------------------------------------------- 〜〜〜〜〜(略)〜〜〜〜〜 --- Tool: get_weather (city: Tokyo) --- 2025-07-21 01:11:27,100 - INFO - google_adk.google.adk.models.google_llm - Sending out request, model: gemini-2.5-flash, backend: GoogleLLMVariant.VERTEX_AI, stream: False 2025-07-21 01:11:27,101 - INFO - google_adk.google.adk.models.google_llm - LLM Request: ----------------------------------------------------------- System Instruction: あなたは時刻案内をするエージェントです。 tools を使ってユーザーが住んでいる都市の現在時刻を取得します。 You are an agent. Your internal name is "time_agent". ----------------------------------------------------------- Contents: {"parts":[{"text":"私は東京在住です。"}],"role":"user"} {"parts":[{"function_call":{"args":{"city":"Tokyo"},"name":"get_time"}}],"role":"model"} {"parts":[{"function_response":{"name":"get_time","response":{"city":"Tokyo","datetime":"2025-07-21T10:11:26.994246+09:00"}}}],"role":"user"} ----------------------------------------------------------- Functions: get_time: {'city': {'type': <Type.STRING: 'STRING'>}} ----------------------------------------------------------- 〜〜〜〜〜(略)〜〜〜〜〜 LLM Request: ----------------------------------------------------------- System Instruction: # 役割 あなたは雑学を披露するエージェントです。 ユーザーが住んでいる国にとって、今日がなんの日か(その国の祝日、記念日など)を調べて回答します。 どうしても特別な日でないときは、最も近い祝日や記念日などを返してください。 # ユーザーが住んでいる国と日付 東京の現在時刻は2025-07-21T10:11:26.994246+09:00です。 You are an agent. Your internal name is "time_agent". ----------------------------------------------------------- Contents: {"parts":[{"text":"私は東京在住です。"}],"role":"user"} {"parts":[{"function_call":{"args":{"city":"Tokyo"},"name":"get_time"}}],"role":"model"} {"parts":[{"function_response":{"name":"get_time","response":{"city":"Tokyo","datetime":"2025-07-21T10:11:26.994246+09:00"}}}],"role":"user"} {"parts":[{"text":"東京の現在時刻は2025-07-21T10:11:26.994246+09:00です。"}],"role":"model"} {"parts":[{"text":"Continue processing previous requests as instructed. Exit or provide a summary if no more outputs are needed."}],"role":"user"} ----------------------------------------------------------- Functions: -----------------------------------------------------------
シーケンシャルにエージェントが呼び出されており、またシステムインストラクションに指定した output_key である {city_and_datetime}
が正しく反映されていることもわかります。
Tools
概要
Tools は、LLM Agent が道具として使うプログラムです。Tools には以下のような種類があります。
名称 | 説明 |
---|---|
Function tools | コーディングされた関数を実行する。外部処理を呼び出したり、計算処理を行わせる等。また Agent-as-a-Tool ではエージェントから他のエージェントを tool として呼び出し可能。 |
Build-in tools | ADK に組み込みの Tool。Google 検索実行ツール、コード生成・実行ツール、Vertex AI Search での検索実行ツール、BigQuery ツールなど。例えば自然言語の指示に基づいて BigQuery へクエリするといった処理を、コードを書くことなく実現できる。 |
Third party tools | CrewAI や LangChain などで定義された Tools を実行。 |
Google Cloud tools | Apigee API Hub、Application Integration、MCP Toolbox for Database などを呼び出し。 |
MCP tools | エージェントから MCP server を呼び出し。 |
LLM Agent は Tools を呼び出すかどうか、またどういったパラメータで呼び出すかを自律的に判断し、実行します。これにより、ADK アプリケーションは LLM が持つ能力を超えて、さまざまなタスクを行うことができます。
- 参考 : Tools
関数の記述
LLM が適切に Tools を呼び出すためには、例えば Function tools においては、適切な方法で関数を記述することが重要です。
わかりやすい関数名、引数名、Tool 名
LLM が適切に判断して Tools を呼び出すためには、関数名や引数名、Tool 名などを、LLM が理解しやすい明確でわかりやすい名称にする必要があります。一般的すぎる名称は避け、例えばその関数が何をするものなのか明確になるよう、動詞_目的語()
のような名称とします。
Docstring を記述する
関数の Docstring(関数の説明)を適切に記載することが重要です。LLM はこれらを読み取り、Tools を呼び出すべきかどうか、またどのような引数を与えるべきかを判断します。
引数はシンプルに
Tools の関数の引数は少なめに、シンプルにします。複雑すぎると、LLM が関数を適切に使用できません。また、データ型もカスタムのものより str や int などシンプルなものが望ましいです。
戻り値は辞書型
Tools の実行結果を LLM が適切に使用できるよう、戻り値のフォーマットも重要です。Python においては辞書型(dictionary)、Java においては Map 型で返すのが理想的です。
要約のスキップ
先程の2つのサンプルソースコードでは、簡単な Function tools を使って現在時刻を取得しました。
通常、Function tools 等が実行されると、tool の実行結果は LLM に渡され、LLM はそれを解釈して最終回答を生成します。しかし、例えば tool の実行結果をそのままユーザーに返したい場合や、あるいは処理時間短縮のために tool の実行結果を LLM に解釈させずに次のエージェントに渡したいケースもあります。
その場合、要約をスキップさせることで、LLM による解釈をスキップして tool の実行結果をそのまま LLM Agent の最終回答とさせることができます。
先程の Sequential agents のサンプルコードの tool 関数定義を、以下のように修正します。変更点は、ToolContext を import した点と、関数の引数に tool_context: ToolContext
を追加した点、また tool_context.actions.skip_summarization = True
を追加した点です。
# 〜〜〜〜〜(略)〜〜〜〜〜 from google.adk.tools import google_search, ToolContext # 〜〜〜〜〜(略)〜〜〜〜〜 def get_time(city: str, tool_context: ToolContext): ''' # 概要 指定された都市の現在時刻を取得します。 # 引数 city (str): 対象の都市名 (Tokyo / Los Angels / London) ''' # ツールの結果は要約されずそのままアウトプットとして扱われる tool_context.actions.skip_summarization = True print(f"--- Tool: get_weather (city: {city}) ---") if "Tokyo" in city: tz = datetime.timezone(datetime.timedelta(hours=9)) elif "Los Angels" in city: tz = datetime.timezone(datetime.timedelta(hours=-7)) elif "London" in city: tz = datetime.timezone(datetime.timedelta(hours=0)) else: tz = None if tz: now = datetime.datetime.now(tz) else: now = None message = { "city": city, "datetime": now } return message
これにより、関数が return した結果は LLM によって解釈されず、そのまま time_agent の最終回答として、次のエージェントに渡されます。
しかしこのとき、ロギングを詳細に出力していると、次に実行されたエージェント trivia_agent のシステムインストラクションで、output_key が反映されていないことに気が付きます。詳細ログは以下のようになりました。
----------------------------------------------------------- System Instruction: # 役割 あなたは雑学を披露するエージェントです。 ユーザーが住んでいる国にとって、今日がなんの日か(その国の祝日、記念日など)を調べて回答します。 どうしても特別な日でないときは、最も近い祝日や記念日などを返してください。 # ユーザーが住んでいる国と日付 You are an agent. Your internal name is "time_agent". -----------------------------------------------------------
# ユーザーが住んでいる国と日付
の下には、本来 {city_and_datetime}
があるため、tool が返した辞書型のデータ(city
と datetime
)が入っているべきです。しかし、要約をスキップした場合は、output_key が次のエージェントに渡されません。これは、output_key に値が代入されるのは LLM による最終回答生成後であるためであると考えられます。要約をスキップした場合は output_key に値が入りません。
しかしこのような状況でも、エージェント trivia_agent は以下のような回答を生成しました。
東京にお住まいのあなたにとって、2025年7月21日は「海の日」です。海の日とは、「海の恩恵に感謝するとともに、海洋国日本の繁栄を願う日」として制定された国民の祝日です。 この日は毎年7月の第3月曜日と定められており、2025年は7月21日がこれに当たります。 また、海の日以外にも、7月21日には「日本三景の日」、「神前結婚記念日」、「自然公園の日」、「烏骨鶏の日」といった様々な記念日があります。
これは、LLM Agent は output_key で情報を渡されなくても、これまで実行された LLM Agent や Tools の実行結果をコンテキストとして保持しており、そのコンテキストに基づいて生成を行っているためであると考えられます。詳細ロギングを確認すると、trivia_agent の実行時に以下のようなログが出力されていました。
2025-07-21 01:35:40,257 - INFO - google_adk.google.adk.models.google_llm - LLM Request: ----------------------------------------------------------- System Instruction: # 役割 あなたは雑学を披露するエージェントです。 ユーザーが住んでいる国にとって、今日がなんの日か(その国の祝日、記念日など)を調べて回答します。 どうしても特別な日でないときは、最も近い祝日や記念日などを返してください。 # ユーザーが住んでいる国と日付 You are an agent. Your internal name is "time_agent". ----------------------------------------------------------- Contents: {"parts":[{"text":"私は東京在住です。"}],"role":"user"} {"parts":[{"function_call":{"args":{"city":"Tokyo"},"name":"get_time"}}],"role":"model"} {"parts":[{"function_response":{"name":"get_time","response":{"city":"Tokyo","datetime":"2025-07-21T10:35:40.174769+09:00"}}}],"role":"user"} ----------------------------------------------------------- Functions: -----------------------------------------------------------
Contents:
のブロックの中にある function_response
という行が、関数の実行結果です。
# ユーザーが住んでいる国と日付
の直下にあるべき output_key は空文字列になっているものの、Contents として含まれている情報を LLM Agent がコンテキストとして使って生成を行っていることがわかります。output_key を使ったほうが、より複雑なプロンプトで役割やタスク、背景、フォーマットを指定する際に効果的であると考えられますが、output_key を使わなくても、ある程度の精度で他のエージェントや tools の出力結果がコンテキストとして使われることがわかります。
杉村 勇馬 (記事一覧)
執行役員 CTO / クラウドソリューション部 部長
元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。
Follow @y_sugi_it