2026年5月26日

BigQuery agent analyticsをデータサイエンスエージェントに組み込んでみた!エージェントの動きをSQLで分析する


Content

こんにちは!みっちーです!



これまでの記事でデータサイエンスエージェントをADKで構築し、Agent Runtime(旧 Vertex AI Agent Engine)にデプロイし、考察サブエージェント(insight_agent)を追加したり、MCPのfetchツールで外部Webページの取得までできるようにしてきました。



Agent Runtimeにデプロイして運用できるようにしたのは良いのですが、「このエージェント、ちゃんと動いてるのかな...?」という疑問が頭から離れませんでした。root_agentがどのサブエージェントをどのくらい呼び出しているか、db_agentのSQL生成でエラーが起きていないか、ds_agentのPython分析のレイテンシは許容範囲か、が不明で、改善しようにも何から手をつければいいかわからない状態でした。



BigQuery agent analyticsがADKでGAになったというアップデートを見て、「これでエージェントの中身が見える!」と早速組み込んでみました。今回はその実装手順と、実際にどんなことがわかったかをレポートします。

前提条件

試した環境

    • エージェント:ADKで構築したデータサイエンスエージェント(root_agent + db_agent + ds_agent + insight_agent + podcast_agentのマルチエージェント構成)

    • BigQuery:同じGCPプロジェクト内にデータセットagent_analyticsを作成済み

    • 可視化:データポータル(旧 Looker Studio)で簡易ダッシュボードを作成

やってみた

① deploy.pyへのプラグイン組み込み

BigQueryAgentAnalyticsPlugingoogle-adkに内包されているため、追加のパッケージインストールは不要です。ただし、Agent Runtimeのビルド環境では依存ライブラリを明示的に指定する必要があるため、deploy.pyのrequirementsに以下の4パッケージを追加します。

    • google-cloud-bigquery

    • google-cloud-bigquery-storage

    • pyarrow

    • opentelemetry-api


次に、deployment/deploy.pyを修正します。agent.pyの変更は不要です。

変更前(元の create() 関数):

from vertexai.preview.reasoning_engines import AdkApp

def create(env_vars: dict[str, str]) -> None:
adk_app = AdkApp(
agent=root_agent,
enable_tracing=False,
)
remote_agent = agent_engines.create(
adk_app,
requirements=[AGENT_WHL_FILE],
extra_packages=[AGENT_WHL_FILE],
env_vars=env_vars,
)

変更後_AdkApp サブクラスと BQ プラグインを追加):

from vertexai.preview.reasoning_engines import AdkApp
from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin # 追加


class _AdkApp(AdkApp): # 追加: AdkApp.clone() の2つのバグを回避(①plugins未継承 ②MCPツールのdeepcopyエラー)
def clone(self):
return _AdkApp(
agent=self._tmpl_attrs.get("agent"),
enable_tracing=self._tmpl_attrs.get("enable_tracing"),
session_service_builder=self._tmpl_attrs.get("session_service_builder"),
artifact_service_builder=self._tmpl_attrs.get("artifact_service_builder"),
memory_service_builder=self._tmpl_attrs.get("memory_service_builder"),
env_vars=self._tmpl_attrs.get("env_vars"),
plugins=self._tmpl_attrs.get("plugins"), # ← これがないとデプロイ後にプラグインが消える
)


def create(env_vars: dict[str, str]) -> None:
bq_plugin = BigQueryAgentAnalyticsPlugin( # 追加
project_id=PROJECT,
dataset_id="agent_analytics",
)
adk_app = _AdkApp( # 変更: AdkApp → _AdkApp
agent=root_agent,
enable_tracing=False,
plugins=[bq_plugin], # 追加
)
remote_agent = agent_engines.create(
adk_app,
requirements=[
AGENT_WHL_FILE,
"google-cloud-bigquery", # 追加
"google-cloud-bigquery-storage", # 追加
"pyarrow", # 追加
"opentelemetry-api", # 追加
],
extra_packages=[AGENT_WHL_FILE],
env_vars=env_vars,
)

ポイントは2点です。① AdkAppを直接使わず _AdkApp サブクラスを定義して clone() をオーバーライドする(理由は後述のつまずきポイント参照)、② requirements に BQ 関連の4パッケージを追加する。あとは既存の Agent Runtime を削除してから再デプロイすれば完了です。

② BigQueryにデータが溜まったらSQLで分析する

デプロイ後、何度かエージェントとのやり取りを行い、データが溜まったところで、BigQueryコンソールを開いてクエリを実行してみました。書き込まれるテーブルは agent_analytics.agent_events で、スキーマは timestampevent_type(USER_MESSAGE_RECEIVED / LLM_REQUEST / AGENT_STARTING など)・agent(エージェント名)・session_idinvocation_idlatency_ms(JSON)・status(OK / ERROR)・attributes(JSON)などで構成されています。


実際に、こんなデータが入っています。

まず、エージェント別の呼び出し回数とエラー率を確認:

-- エージェント別の呼び出し回数とエラー率(直近7日間)
SELECT
agent,
COUNT(*) AS invocation_count,
COUNTIF(status = 'ERROR') AS error_count,
ROUND(COUNTIF(status = 'ERROR') / COUNT(*) * 100, 1) AS error_rate_pct
FROM `your-project.agent_analytics.agent_events`
WHERE event_type = 'AGENT_STARTING'
AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY agent
ORDER BY invocation_count DESC;



結果を見ると、bq_ml_agent が最も多く呼び出されており、次いで db_ds_multiagentdatabase_agentinsight_agentpodcast_agent と続く内訳でした。エラー率を確認すると、すべてのエージェントでエラーは0件——特に問題は起きていないようでひと安心です。もしエラーが発生した際はこのクエリで即座に検知できます。

次にLLM呼び出しのレイテンシをエージェント別に分析:

-- LLM呼び出しのレイテンシ分析(エージェント別、直近7日間)
SELECT
agent,
APPROX_QUANTILES(CAST(JSON_VALUE(latency_ms, '$.total_ms') AS INT64), 100)[OFFSET(50)] AS p50_ms,
APPROX_QUANTILES(CAST(JSON_VALUE(latency_ms, '$.total_ms') AS INT64), 100)[OFFSET(90)] AS p90_ms,
APPROX_QUANTILES(CAST(JSON_VALUE(latency_ms, '$.total_ms') AS INT64), 100)[OFFSET(99)] AS p99_ms
FROM `your-project.agent_analytics.agent_events`
WHERE event_type = 'LLM_RESPONSE'
AND latency_ms IS NOT NULL
AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY agent
ORDER BY p90_ms DESC;



エージェントごとのLLM呼び出しのp50/p90/p99が一覧できます。insight_agent はLLMレイテンシのp50が約13.5秒と最も高く、考察生成の重さが数字で確認できました。

次に、invocation全体(ユーザーへの応答返却まで)の所要時間を測るクエリも実行しました:

-- invocation単位の所要時間(INVOCATION_STARTING → INVOCATION_COMPLETED)
WITH durations AS (
SELECT
agent,
invocation_id,
TIMESTAMP_DIFF(
MAX(CASE WHEN event_type = 'INVOCATION_COMPLETED' THEN timestamp END),
MIN(CASE WHEN event_type = 'INVOCATION_STARTING' THEN timestamp END),
MILLISECOND
) AS duration_ms
FROM `your-project.agent_analytics.agent_events`
GROUP BY agent, invocation_id
HAVING MAX(CASE WHEN event_type = 'INVOCATION_COMPLETED' THEN timestamp END) IS NOT NULL
AND MIN(CASE WHEN event_type = 'INVOCATION_STARTING' THEN timestamp END) IS NOT NULL
)
SELECT
agent,
APPROX_QUANTILES(duration_ms, 100)[OFFSET(50)] AS p50_ms,
APPROX_QUANTILES(duration_ms, 100)[OFFSET(90)] AS p90_ms,
APPROX_QUANTILES(duration_ms, 100)[OFFSET(99)] AS p99_ms,
MAX(duration_ms) AS max_ms,
COUNT(*) AS n
FROM durations
GROUP BY agent
ORDER BY p90_ms DESC;



podcast_agent は音声生成を含むため、1回のinvocationで約102秒かかることが確認できました。さらに db_ds_multiagent → podcast_agent と連鎖するクエリでは、db_ds_multiagentのinvocation全体でエンドツーエンドで約111秒と、複数エージェントの処理時間が積み重なることも確認できました。並列化やストリーミングレスポンスの導入を検討する価値があるとわかりました。

③ データポータルでダッシュボードを作る

分析クエリをデータポータルと接続して、簡単なモニタリングダッシュボードを作りました。BigQueryのテーブルをデータソースとして直接追加できるので、設定自体は10分もあれば完了します。

ダッシュボードには以下を配置しました:

    • 1日あたりのインタラクション数の時系列グラフ

    • ツール別(call_db_agent / call_ds_agent / call_insight_agent / call_mcp_tool)呼び出し回数とエラー率の棒グラフ

    • p50/p90/p99レイテンシの数値カード

    • 直近のエラー一覧テーブル



このダッシュボードを毎朝確認するようにすれば、「なんとなく動いている」から「データを見て改善する」サイクルに変革できそうです!

つまずいたポイント・気づいたこと

① BigQueryへの書き込みコストに注意

インタラクションデータはリアルタイムでBigQueryにストリーミングされます。データサイエンスエージェントは1回の質問で、複数のツールを呼び出す可能性があるため、トラフィックが多くなるとBigQueryへの書き込み量が想定以上に増える可能性があります。最初はバッファリング設定でバッチ書き込みに変えると費用を抑えられます。

② オープンソースなのでカスタマイズの自由度が高い

BigQuery agent analyticsはOSSとして公開されているため、コールバックのロジックを独自にカスタマイズできます。デフォルトでは収集されないメタデータ(どのユーザーがどのBigQueryテーブルに対して質問したか、など)を追加で記録したい場合も、コードを修正するだけで対応できます。

AdkApp.clone() がプラグインを引き継がない(ADK バグ)

最もハマったのが、AdkAppplugins=[bq_plugin] を渡してデプロイしても、BigQueryにデータが一切書き込まれないという問題です。これは Agent Runtime にデプロイする場合特有の問題で、ローカル実行時は plugins をそのまま渡せば動きます。Agent Runtime が内部でアプリをシリアライズする際に AdkApp.clone() を呼び出しますが、このメソッドが plugins を引き継いでいないADKのバグがあります(ADK 1.33.0 時点)。

デプロイは成功し、クエリへの応答も正常に返ってくるのに、BigQueryには0件のまま——という状態が続き、原因特定に時間がかかりました。対処法は上記コード例のとおり、AdkApp のサブクラス(_AdkApp)を定義して clone() をオーバーライドし、plugins を明示的に引き継ぐことです。

なお、MCPのfetchツールを組み込んでいる場合はもう一つ問題があります。MCPツールはオープン中のファイルハンドルを保持しているため、AdkApp デフォルトの copy.deepcopy() が失敗します。_AdkAppclone() では deepcopy を使わず引数を直接受け渡す実装になっているため、この問題も同時に解消されます。

まとめ

BigQuery agent analyticsをデータサイエンスエージェントに組み込んでみて、「エージェントのブラックボックスが開いた」感覚がありました。エージェント別の呼び出し回数やレイテンシが数字で見えるようになったことで、今後は「どのエージェントを最適化すべきか」「どこでボトルネックが起きているか」をデータドリブンで判断できそうです。改善サイクルを回すための土台が整ったと感じています!

    • 機能はgoogle-adkに内包済み——追加パッケージインストール不要、deploy.py_AdkApp サブクラスと plugins=[BigQueryAgentAnalyticsPlugin(…)] を追加するだけ

    • 要注意:AdkApp をそのまま使うと clone() がプラグインを引き継がない ADK バグがある(ADK 1.33.0 時点)。_AdkApp サブクラスで clone() をオーバーライドして回避する

    • agent.py は変更不要

    • agent_analytics.agent_events テーブルに event_typeagentlatency_ms(JSON)・status 等が記録され、BigQuery SQL でエージェント別エラー率やレイテンシ分析ができる

    • データポータルと組み合わせると簡単なモニタリングダッシュボードが作れる

    • マルチエージェント構成では書き込み量が多くなるため、コストはトラフィック量次第で設計時に試算を

ADKのデータサイエンスエージェントを本番運用しているが「中身が見えていない」という方は、ぜひBigQuery agent analyticsを導入してみてください。データドリブンなエージェント改善のサイクルが回り始めますよ!

システムサポートでは、Google Cloudの導入や活用を支援しております。

Google Cloudを導入したい・導入したけど使いこなせていない…という方は、お気軽にご相談ください!


Google Cloud 導入・活動支援に関するご相談はこちら

2026年5月26日 BigQuery agent analyticsをデータサイエンスエージェントに組み込んでみた!エージェントの動きをSQLで分析する

Category Google Cloud

ご意見・ご相談・料金のお見積もりなど、
お気軽にお問い合わせください。

お問い合わせはこちら