AIエージェントにレコード会社ごとの総再生回数を尋ねると、実際の2.5倍近い数字が自信満々に返ってくることがあります。原因の多くは、モデルがもっともらしいが誤った内容を作り出すハルシネーションではありません。複数の関連テーブルを結合するクエリで重複した行が生まれることにあります。BigQuery Graphのmeasureは、この問題をデータモデルの段階で解決します。計算を一度定義して対象のエンティティに結びつけておけば、人が書いたクエリでもAIエージェントが作ったクエリでも、各項目を必ず1回だけ数えます。
エラーなしで実行される誤った合計
ある音楽会社の小さなカタログを例に考えます。曲が5つ、それを歌うアーティスト、レコード会社2社、曲を収録した外部のプレイリスト4つがあります。そのうちレーベルAと呼ぶ1社が4曲を保有しています。
| 曲 | 再生回数 | 収録プレイリスト数 |
|---|---|---|
| 曲1 | 20億回 | 4 |
| 曲2 | 15億回 | 2 |
| 曲3 | 12億回 | 1 |
| 曲4 | 4億回 | 1 |
| 合計 | 51億回 | - |
手で足すと、レーベルAの再生回数は51億回です。ところが、AIエージェントを作るためのツールキットAgent Development Kit(ADK)で構築した分析アシスタントは、同じ質問に126億回と答えます。
さらに危険なのは、元になるクエリがエラーを一切出さないことです。SQLとしては正しいため、エンジンは警告なしで実行します。数百万行のテーブルでは数字が膨らんだことに気付く人はまずおらず、ユーザーは大きく間違った数字を信じたまま受け取ってしまいます。
1曲が4回数えられる仕組み
数字が膨らむのは、結合後に1行が複数行に増える「ジョインファンアウト」が原因です。曲1をクエリの中で追うと、仕組みがはっきりします。
- 結合前、曲1は曲テーブルの中で再生回数20億回の1行です。
- 曲とプレイリスト登録を結合すると、この行はプレイリストごとに1行ずつ、計4行になり、どの行も20億回という値をそのまま持ちます。
- SUM(streams)を実行すると20億回が4回足され、曲1だけで80億回と計上されます。
- 曲2は2つのプレイリストに収録されているため、15億回が30億回になります。
- 曲3と曲4はそれぞれ1つのプレイリストにしか収録されていないため、12億回と4億回のまま正しく残ります。
合わせると126億回となり、実際の51億回の約2.5倍です。1曲に多数のプレイリスト登録が対応するような一対多の関係や、多対多の関係をまたいで結合した後に列を合計すると、常に同じことが起こります。

▲ 結合後に重複した行
ルールはクエリではなくモデルに置く
すべてのクエリに重複排除のロジックを加えることもできます。しかし、いずれアナリストかAIへのプロンプトがそれを忘れます。BigQuery Graphは、このルールをデータモデルに移すことで、誰も覚えておく必要がないようにします。構成要素は3つです。
- ソーステーブル:BigQueryやクラウドストレージのバケットにある既存のデータです。コピーや物理的な変換は一切行いません。
- プロパティグラフ:既存のテーブルの上に関係を定義する層です。曲、アーティスト、レーベル、プレイリストがノードテーブルになり、プレイリスト登録がそれらをつなぐエッジになります。
- measure:グラフ定義に書き込む再利用可能な集計ルールです。たとえば曲のノードにMEASURE(SUM(streams)) AS total_streamsと書きます。ほかのエンティティにも独自のmeasureを持たせられ、レーベルのノードにMEASURE(SUM(marketing_budget)) AS total_budgetを置くこともできます。
重要なのは、measureがエンティティのキーに結びつく点です。曲のノードでKEY (song_id)を宣言すると、BigQueryは自動で重複を排除し、クエリがどのような単位で集計しても、その中で各曲を1回だけ数えます。
SQLでは、GRAPH_EXPANDでグラフを展開し、SUM(streams)の代わりにAGG(Songs_total_streams)を呼び出します。同じデータで並べて実行すると、SUMは126億回、AGGは正確に51億回を返します。
一度定義すれば集計単位を変えて再利用できる
measureは、新しい質問のたびに書き直す必要がありません。GROUP BYとSELECTの軸をレーベル名からプレイリスト名に切り替えれば、同じAGG(Songs_total_streams)の呼び出しでプレイリストごとの再生回数を正しく算出できます。
AIエージェントにも効果があります。最初と同じ自然言語の質問をもう一度投げると、アシスタントはレーベルAの再生回数を51億回と答えるようになります。変わったのはプロンプトではなく、データモデルです。
合計だけでは見えないもの
プロパティグラフはネットワーク全体を保持しているため(この例ではノード19個とエッジ22本)、数字を読むだけでなく、その背後にある経路をたどることができます。
レーベルAの51億回のうち、どれだけが単一のプレイリストに依存しているかを尋ねると、グラフは47億回、つまり92%が1つのプレイリストを経由していると答えます。4つの中で最大の、フォロワー3,500万人のプレイリストです。これは、それらの再生がすべてそのプレイリスト上で発生したという意味ではありません。レーベルのカタログが単一の流通チャネルに大きく依存しているという意味です。
そのプレイリストをグラフから切り離すと、リスクが具体的に見えてきます。
- レーベルのプレイリスト登録8件のうち3件が消えます。
- 曲3はどのプレイリストにも収録されていない状態になります。
- グラフのエッジは22本から16本に減ります。
単一の合計値では、こうした構造的な依存関係は見えません。フラットなSQL結合では失われる関係を保持しておくことで、アナリストとAIエージェントの双方が運用上のリスクを判断するための文脈を得られるようです。

▲ 単一プレイリストへの再生依存
構築に使うツール
プロパティグラフとmeasureを設定する方法はいくつかあります。
| 方法 | できること |
|---|---|
| BigQuery SQL | CREATE OR REPLACE PROPERTY GRAPH文でノード、キー、measureを定義 |
| BigQuery Studioのビジュアルモデラー | ノード、エッジ、measureを画面上で組み立て、DDLを手書きせずにmeasureをテスト |
| Agent Development Kit | 自分で構築するAIエージェントにBigQuery Graphを接続 |
| Knowledge Catalog | グラフのメタデータとスキーマを自動登録し、検索と管理に活用 |
自然言語でデータに関する質問に答えるBigQuery組み込みの会話型分析エージェントも、プロパティグラフを直接クエリできます。そのため、先に独自のアシスタントを作る必要はありません。
エージェントにデータを任せる前に確認すること
AIエージェントの誤った数字は、モデルのせいとは限りません。結合は行を繰り返して合計を静かに膨らませます。measureはエンティティの識別子を追跡することでそれを防ぎ、一度定義すれば以降のすべてのクエリで使えます。実践の出発点は次のとおりです。
- 曲とプレイリスト登録のような一対多の関係をまたいで結合した後に列を合計しているクエリを探します。
- よく使う指標はmeasureとしてプロパティグラフに移し、ノードのキーを宣言します。
- クエリとエージェントには、SUMではなくGRAPH_EXPAND内のAGGでmeasureを呼び出させます。
- DDLの手書きが負担なら、BigQuery Studioのビジュアルモデラーでグラフを作り、measureをテストします。
- 合計で止まらず、結果が単一のチャネルに偏りすぎていないかをグラフで確認します。