単一のエージェントに「調査して、分析して、レポートを書いて」と頼むと、途中で文脈が混乱したり、一つのステップが失敗したときに全体が止まったりします。これはエージェントの能力の問題ではなく、設計の問題です。
Google Agent Development Kit(ADK)が用意しているのは、この問題への構造的な答えです。オーケストレーターとサブエージェントに役割を分け、それぞれが自分の仕事に集中できる仕組みを作れます。3ヶ月ほど実際のプロジェクトで使い続けて分かったのは、「どこで境界を引くか」がほぼ全てを決めるということです。
ADK の基本構造を把握する
ADK では、エージェントを3種類のパターンで組み合わせます。
- LlmAgent: LLM を呼び出して判断・生成を行うエージェント
- SequentialAgent: サブエージェントを順番に実行するオーケストレーター
- ParallelAgent: サブエージェントを並列実行するオーケストレーター
これに加えて、LoopAgent(条件が満たされるまで繰り返す)があります。複雑なワークフローも、この4種類の組み合わせで表現できます。
まずインストールから始めます。
pip install google-adk実例:競合調査ワークフローを作る
具体的なユースケースとして、「競合他社の新機能を調査してサマリーレポートを生成する」ワークフローを作ります。このワークフローには3つのサブエージェントが必要です。
- SearchAgent: 指定された競合他社についてウェブ検索を実行
- AnalysisAgent: 検索結果を分析して重要ポイントを抽出
- ReportAgent: 分析結果からサマリーレポートを生成
from google.adk.agents import LlmAgent, SequentialAgent, ParallelAgent
from google.adk.tools import google_search
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
# ① 各競合社を検索するエージェント(並列実行用)
def create_search_agent(company_name: str) -> LlmAgent:
return LlmAgent(
name=f"search_{company_name}",
model="gemini-2.0-flash",
instruction=f"""
{company_name} の最新機能リリースや製品アップデートを検索してください。
過去30日以内の情報に絞り、URLとともに箇条書きで返してください。
""",
tools=[google_search],
output_key=f"search_result_{company_name}",
)
# ② 検索結果を分析するエージェント
analysis_agent = LlmAgent(
name="analysis_agent",
model="gemini-2.0-flash",
instruction="""
前のステップで収集された競合他社の情報を分析してください。
以下の観点で整理してください。
- 最も注目すべき機能変更は何か
- 自社に影響する可能性がある変化は何か
- 競合間での差別化ポイントはどこか
""",
output_key="analysis_result",
)
# ③ レポートを生成するエージェント
report_agent = LlmAgent(
name="report_agent",
model="gemini-2.0-flash-thinking-exp", # 推論モデルで品質向上
instruction="""
分析結果をもとに、経営層向けの競合動向サマリーレポートを作成してください。
- 全体概要(3文以内)
- 要注意の動向(優先度順に3点)
- 推奨アクション(具体的に2点)
という構成でまとめてください。
""",
output_key="final_report",
)並列実行とシーケンシャル実行を組み合わせる
複数の競合社を同時に調査してから、その結果をまとめて分析する流れを作ります。
COMPETITORS = ["OpenAI", "Anthropic", "Mistral"]
# 競合社ごとの検索エージェントを並列実行
parallel_search = ParallelAgent(
name="parallel_search",
sub_agents=[create_search_agent(c) for c in COMPETITORS],
)
# 全体のワークフロー:検索(並列)→ 分析 → レポート
competitive_analysis_workflow = SequentialAgent(
name="competitive_analysis",
sub_agents=[parallel_search, analysis_agent, report_agent],
)ParallelAgent の中身は ADK が自動でスレッド管理します。3社の検索が並列で走るので、逐次実行に比べて時間が約3分の1になります。
セッションと状態管理の扱い方
各エージェントが output_key に設定したデータは、セッションの state に自動で格納されます。次のエージェントはそのまま参照できます。
session_service = InMemorySessionService()
runner = Runner(
agent=competitive_analysis_workflow,
app_name="competitive_analysis_app",
session_service=session_service,
)
async def run_analysis():
session = await session_service.create_session(
app_name="competitive_analysis_app",
user_id="user_001",
)
message = types.Content(
role="user",
parts=[types.Part(text="競合調査を実行してください")],
)
async for event in runner.run_async(
user_id="user_001",
session_id=session.id,
new_message=message,
):
if event.is_final_response():
print(event.content.parts[0].text)
# セッション状態から中間結果も取得できる
final_session = await session_service.get_session(
app_name="competitive_analysis_app",
user_id="user_001",
session_id=session.id,
)
print("分析結果:", final_session.state.get("analysis_result"))
print("最終レポート:", final_session.state.get("final_report"))エラー伝播をどう設計するか
実運用で一番悩んだのはエラー処理です。並列実行中の一つのサブエージェントが失敗したとき、デフォルトでは例外がそのまま上位に伝播します。
この挙動が望ましくない場合は、エラーをキャッチして graceful degradation を実装します。
def create_resilient_search_agent(company_name: str) -> LlmAgent:
return LlmAgent(
name=f"search_{company_name}",
model="gemini-2.0-flash",
instruction=f"""
{company_name} の最新情報を検索してください。
検索が失敗した場合は「{company_name}: 情報取得失敗」とだけ返してください。
絶対にエラーをそのまま返さないでください。
""",
tools=[google_search],
output_key=f"search_result_{company_name}",
)LlmAgent に「失敗したときの振る舞い」を instruction で明示する方が、try-except でラップするよりも安定していました。LLM がエラーメッセージを自然に吸収してくれるからです。ただし、ツールの呼び出し自体が例外を投げるケースは別途ハンドリングが必要です。
ローカルでのテスト方法
ADK にはローカルで動作確認できる Web UI が付いています。
adk webブラウザで http://localhost:8000 を開くと、エージェントの実行ステップや状態の変化をリアルタイムで確認できます。本番デプロイ前に動作を視覚的に確認できるのは、デバッグの効率を大きく上げてくれます。
境界の引き方が全てを決める
マルチエージェント設計で最終的に重要なのは、各エージェントの「責任範囲の明確さ」です。一つのエージェントに複数の役割を持たせると、プロンプトが複雑になり、テストも難しくなります。
「このエージェントは何をするエージェントか?」が一文で言えないなら、分割を検討してください。ADK のシーケンシャル・並列の組み合わせは柔軟なので、後から構造を変えるのも比較的容易です。
次のステップとして、output_key で受け渡すデータの型を Pydantic で定義して構造化すると、エージェント間のインタフェースが明示的になります。実運用ではこれをやっておくと、後からの変更がずっと楽になります。