こんにちは!
KDDIアイレットの取り組みとして6月22日〜7月3日の期間で開催中の「Google Cloud Next '26 / Google I/O やってみた系ブログリレー」、本日は9日目の投稿です。
今回は「BigQuery Agent Analytics Plugin」を対象に、実際に検証してみた様子をお届けします!
前回の記事はこちらです。
BigQuery Agent Analytics Plugin とは
一般提供が開始されたBigQuery Agent Analytics Plugin は、ADKのPluginアーキテクチャと BigQuery Storage Write API を使って、エージェントのイベントをBigQuery のテーブルに直接ログとして書き込むプラグインです。
捕捉できる主なイベントは以下です(一部抜粋)。
| イベントタイプ | いつ記録されるか | 主なペイロード |
|---|---|---|
USER_MESSAGE_RECEIVED |
ユーザー入力が来たとき | テキスト |
LLM_REQUEST |
モデルにリクエストを送ったとき | プロンプト, モデル名, ツール |
LLM_RESPONSE |
モデルから応答が返ったとき | 応答テキスト, トークン数, レイテンシ |
TOOL_STARTING / TOOL_COMPLETED
|
ツール実行の開始/完了 | ツール名, 引数, 結果 |
TOOL_ERROR / LLM_ERROR
|
ツール/モデル呼び出しの失敗 | エラーメッセージ |
AGENT_RESPONSE |
最終的なエージェントの応答 | 応答内容 |
書き込みは 非同期なので、エージェントの応答速度を邪魔しません。
さらに 1.27.0以降 では、イベントタイプごとにビュー(v_llm_response、v_tool_completed など)が自動生成されます。本記事でもこのビューを活用します。
BigQuery Agent Analytics Plugin でログを溜め、BigQueryのAI関数(LLM-as-a-Judge) で採点する、という流れを実際にやってみます。
1. エージェントを作る
まずは評価対象になるテスト用のエージェントを作ります。今回は「ユーザーの質問に答えるFAQボット」を題材にします。あえて評価で差が出るようにしておきます。
faq_agent/agent.py:
import os
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.models.google_llm import Gemini
from google.adk.plugins.bigquery_agent_analytics_plugin import (
BigQueryAgentAnalyticsPlugin,
BigQueryLoggerConfig,
)
# --- 設定 ---
PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
DATASET_ID = os.environ.get("BQ_DATASET_ID")
BQ_LOCATION = os.environ.get("BQ_LOCATION")
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "True"
# --- ツール ---
def get_shipping_policy(region: str) -> dict:
"""指定された地域の配送ポリシーを返す。
Args:
region: 地域名(例: "japan", "us")
"""
policies = {
"japan": "通常配送は2〜3営業日、送料は5,000円以上で無料です。",
"us": "Standard shipping takes 5-7 business days. Free over $50.",
}
return {
"region": region,
"policy": policies.get(region.lower(), "該当地域のポリシーは未登録です。"),
}
# --- Plugin ---
bq_config = BigQueryLoggerConfig(
enabled=True,
create_views=True,
custom_tags={"env": "eval", "agent_version": "v1"},
)
bq_plugin = BigQueryAgentAnalyticsPlugin(
project_id=PROJECT_ID,
dataset_id=DATASET_ID,
table_id="agent_events",
config=bq_config,
location=BQ_LOCATION,
)
# --- エージェント ---
root_agent = Agent(
model="gemini-2.5-flash",
name="faq_agent",
instruction=(
"あなたはECサイトのFAQボットです。"
"配送ポリシーに関する質問には get_shipping_policy ツールを使って正確に答えてください。"
"ツールで得た情報以外は推測せず、わからない場合は正直にわからないと答えてください。"
),
tools=[get_shipping_policy],
)
# --- App ---
app = App(
name="faq_agent",
root_agent=root_agent,
plugins=[bq_plugin],
)
custom_tags に env や agent_version を入れておくと、後でSQLで「v1のevalランだけ採点する」のような絞り込みができて便利です。
2. エージェントを動かしてログを溜める
評価のためには、まず色々な質問を投げてログを作る必要があります。テスト用の質問リストを回すスクリプトを書きます。
run_eval_session.py:
import asyncio
import uuid
from google.adk.runners import InMemoryRunner
from google.genai import types
from faq_agent.agent import app, bq_plugin
# 評価用の質問セット
TEST_PROMPTS = [
"日本への配送って何日くらいかかりますか?",
"送料無料になる条件を教えて",
"How long does shipping to the US take?",
"海外配送はやってますか?",
"返品はできますか?",
]
async def main():
runner = InMemoryRunner(app=app)
user_id = "eval_user"
for prompt in TEST_PROMPTS:
session = await runner.session_service.create_session(
app_name=app.name, user_id=user_id, session_id=str(uuid.uuid4())
)
content = types.Content(role="user", parts=[types.Part(text=prompt)])
print(f"\n=== Q: {prompt} ===")
async for event in runner.run_async(
user_id=user_id, session_id=session.id, new_message=content
):
if event.is_final_response() and event.content:
print("A:", event.content.parts[0].text)
await bq_plugin.flush()
if __name__ == "__main__":
asyncio.run(main())
実行します。
python run_eval_session.py
これで agent_events テーブルに、5つの質問それぞれの ユーザー入力 → LLMリクエスト → ツール呼び出し → 最終応答 という一連のイベントが流れ込みました。
3. 溜まったログを確認する
BigQuery スタジオで中身を見てみます。
SELECT timestamp, event_type, agent, content
FROM `<PROJECT_ID>.adk_agent_logs.agent_events`
ORDER BY timestamp DESC
LIMIT 20;
content は event_type ごとに構造が変わる JSON型 です。生でほじくると大変なので、自動生成された ビュー を使います。
例えば「ユーザー入力」と「最終応答」をJOINして、(質問, 回答) のペアを作ってみます。
WITH questions AS (
SELECT
invocation_id,
session_id,
JSON_VALUE(content, '$.text_summary') AS user_question
FROM `<PROJECT_ID>.adk_agent_logs.agent_events`
WHERE event_type = 'USER_MESSAGE_RECEIVED'
),
answers AS (
SELECT
invocation_id,
response_text AS agent_answer
FROM `<PROJECT_ID>.adk_agent_logs.v_agent_response`
)
SELECT
q.user_question,
a.agent_answer
FROM questions q
JOIN answers a USING (invocation_id);
ここまでで「評価したい (質問, 回答) のペア」が SQL で取り出せる状態になりました。
4. LLM-as-a-Judge
BigQueryには Gemini をSQLから直接呼べるAI関数 が揃っています。代表的なものは以下。
| 関数 | 返り値 | 用途 |
|---|---|---|
AI.GENERATE |
STRING または STRUCT | 自由記述・構造化出力(スコア+理由など) |
AI.GENERATE_BOOL |
BOOL | 「正しい/正しくない」の二値判定 |
AI.GENERATE_INT / AI.GENERATE_DOUBLE
|
数値 | スコアを数値で出す |
AI.SCORE |
FLOAT64 | プロンプトに基づき行を採点してランキング |
AI.GENERATE 系は endpoint でモデルを自分で選べる汎用関数、AI.SCORE はモデルやパラメータを BigQuery 側がよしなに選んでくれるマネージド関数、という違いがあります。順番に試していきます。
4-1. 二値判定
AI.GENERATE_BOOL で「回答が質問に対して妥当か」をTrue/Falseで判定します。
WITH qa AS (
SELECT
q.invocation_id,
JSON_VALUE(q.content, '$.text_summary') AS user_question,
a.response_text AS agent_answer
FROM `<PROJECT_ID>.adk_agent_logs.agent_events` q
JOIN `<PROJECT_ID>.adk_agent_logs.v_agent_response` a
USING (invocation_id)
WHERE q.event_type = 'USER_MESSAGE_RECEIVED'
)
SELECT
user_question,
agent_answer,
AI.GENERATE_BOOL(
CONCAT(
'あなたはFAQボットの品質評価者です。',
'次の回答が、質問に対して事実として妥当で、わからないことを誠実に伝えているなら true、',
'ハルシネーションや的外れがあるなら false と判定してください。\n',
'質問: ', user_question, '\n',
'回答: ', agent_answer
),
endpoint => 'gemini-2.5-flash'
).result AS is_acceptable
FROM qa;
「わからないことを正直に答えた」ケースも true 判定されています。
評価基準(誠実さも加点する)がプロンプト通りに効いていることがわかります。
4-2. スコア+理由を同時に出す
二値だと荒すぎるので、「1〜5点のスコア」と「その理由」を取ってみます。AI.GENERATE の output_schema を使います。
WITH qa AS (
SELECT
q.invocation_id,
JSON_VALUE(q.content, '$.text_summary') AS user_question,
a.response_text AS agent_answer
FROM `<PROJECT_ID>.adk_agent_logs.agent_events` q
JOIN `<PROJECT_ID>.adk_agent_logs.v_agent_response` a
USING (invocation_id)
WHERE q.event_type = 'USER_MESSAGE_RECEIVED'
),
judged AS (
SELECT
user_question,
agent_answer,
AI.GENERATE(
CONCAT(
'あなたは厳格なFAQボット評価者です。以下の回答を評価してください。\n',
'評価軸: (1)事実の正確さ (2)質問への的確さ (3)不明点を誠実に扱っているか\n',
'score は 1(悪い)〜5(完璧) の整数。reasoning は日本語で簡潔に。\n',
'質問: ', user_question, '\n',
'回答: ', agent_answer
),
endpoint => 'gemini-2.5-flash',
output_schema => 'score INT64, reasoning STRING'
) AS judge
FROM qa
)
SELECT
user_question,
agent_answer,
judge.score,
judge.reasoning
FROM judged
ORDER BY judge.score ASC;
output_schema => 'score INT64, reasoning STRING' と書くだけで、返り値が judge.score(整数)と judge.reasoning(文字列)のSTRUCTになります。
4-3. ランキング
「どの回答が一番マシ/ダメか順位付けしたい」だけなら、専用の AI.SCORE が手軽です。プロンプトに沿って行をスコアリングしてくれます。
WITH qa AS (
SELECT
JSON_VALUE(q.content, '$.text_summary') AS user_question,
a.response_text AS agent_answer
FROM `<PROJECT_ID>.adk_agent_logs.agent_events` q
JOIN `<PROJECT_ID>.adk_agent_logs.v_agent_response` a
USING (invocation_id)
WHERE q.event_type = 'USER_MESSAGE_RECEIVED'
)
SELECT
user_question,
agent_answer,
AI.SCORE(
STRUCT(
user_question,
agent_answer,
'この回答が質問に対してどれだけ役立つかを1-5で評価してください'
)
) AS helpfulness
FROM qa
ORDER BY helpfulness DESC;
5. 評価結果を集計してダッシュボード化
採点結果をSQLで集計します。例えば「エージェントのバージョンごとの平均スコア」を出してみます。
ここでcustom_tags に入れた agent_version が効いてきます。
WITH qa AS (
SELECT
q.invocation_id,
-- custom_tags は attributes に入る
JSON_VALUE(q.attributes, '$.custom_tags.agent_version') AS agent_version,
JSON_VALUE(q.content, '$.text_summary') AS user_question,
a.response_text AS agent_answer
FROM `your-project.adk_agent_logs.agent_events` q
JOIN `your-project.adk_agent_logs.v_agent_response` a
USING (invocation_id)
WHERE q.event_type = 'USER_MESSAGE_RECEIVED'
),
judged AS (
SELECT
agent_version,
AI.GENERATE(
CONCAT(
'FAQ回答を1〜5で採点。score INT64のみ返す。\n',
'質問: ', user_question, '\n回答: ', agent_answer
),
endpoint => 'gemini-2.5-flash',
output_schema => 'score INT64'
).score AS score
FROM qa
)
SELECT
agent_version,
COUNT(*) AS n,
ROUND(AVG(score), 2) AS avg_score,
MIN(score) AS worst,
COUNTIF(score <= 2) AS num_bad
FROM judged
GROUP BY agent_version
ORDER BY avg_score DESC;
この結果テーブルをそのまま Looker Studio(データポータル) に繋げば、プロンプトを変えた v1 → v2 でスコアがどう動いたかを観測できます。
Tips:採点コストを抑える
AI.GENERATE 系は内部で Gemini を呼び出すので、行数が多いとそれなりに課金されます。
-
gemini-2.5-flashのような軽量モデルを使う -
model_paramsでthinking_budgetを絞る
AI.GENERATE_BOOL(
prompt,
endpoint => 'gemini-2.5-flash',
model_params => JSON '{"generation_config":{"thinking_config":{"thinking_budget":0}}}'
).result
まとめ
- ADKの BigQuery Agent Analytics Plugin を使えば、
plugins=[plugin]の一行でエージェントの全イベントを BigQuery に流せる - 溜まったログには イベント別ビュー が自動で用意されるので、
(質問, 回答)のペアがSQLで簡単に取り出せる - そのペアに対して
AI.GENERATE/AI.GENERATE_BOOL/AI.SCOREを呼ぶだけで、LLMを評価できる



