BigQuery ハンズオン教科書|2026-08 版
第 14 章

生成 AI とベクトル検索 — Gemini × BigQuery

AI 関数でテキストを SQL から生成・構造化し、エンベディングで意味検索する

この章で学ぶこと

前提: この章は Gemini Enterprise Agent Platform(旧称 Vertex AI)の呼び出し課金と接続リソースが前提のため、課金プロジェクト必須(ハンズオン全体で概算数十円程度)。

BigQuery の生成 AI を 2 つに整理する

「BigQuery で生成 AI を使う」という言葉は、性質の異なる 2 つの機能を指しています。ひとつはSQL の中から呼び出す関数群(AI.GENERATE・AI.GENERATE_TEXT・AI.GENERATE_EMBEDDING など)。もうひとつはBigQuery Studio の UI を支援する機能群(データキャンバス・データインサイト・会話型分析)で、まとめて「Gemini in BigQuery」と呼ばれます。前者はクエリに組み込んで自動化するためのもの、後者は人間が対話しながらデータを探索するためのものです。この章は両方を扱いますが、主役は SQL から呼ぶ AI 関数です。

SQL から呼ぶ AI 関数Gemini in BigQuery(UI 支援)
代表例AI.GENERATE, AI.GENERATE_TEXT, AI.GENERATE_EMBEDDINGデータキャンバス、データインサイト、会話型分析
使う場所クエリ・パイプライン・アプリケーションBigQuery Studio のコンソール操作
目的推論結果をテーブルの列として自動生成する人間が自然言語でデータを探索・SQL 作成する
課金クエリのコンピュート料金 + モデル呼び出し料金SQL code assist・Python code assist・Data Canvas・Data preparation は追加料金なし(Gemini in BigQuery Pricing Overview に明記。データインサイトは別課金体系)

関数側にはさらに 2 系統あります。「汎用(general-purpose)」の AI.GENERATE 系は、プロンプトとモデルを自分で組み立てる自由度の高い関数群です。「管理型(managed)」の AI.IF・AI.SCORE・AI.CLASSIFY・AI.AGG は、フィルタリング・スコアリング・分類・集約という定型タスクに特化し、プロンプト設計を意識せずに使える上位ラッパーです。本章では基礎となる汎用関数を中心に扱います。

実務の視点: 「まず管理型関数(AI.CLASSIFY など)で足りるか」を先に検討し、それで表現しきれない場合に AI.GENERATE へ降りるのが無駄のない選び方です。汎用関数はプロンプトの書き方次第で出力が揺れますが、管理型関数は目的が固定されている分、チーム内のレビューコストが下がります。

命名にはもうひとつ注意が必要です。BigQuery の生成 AI 機能は長らくVertex AIという基盤を呼び出す形で提供されてきましたが、2026 年 8 月時点の公式ドキュメントでは、接続・権限・リモートモデル作成のページがそろってGemini Enterprise Agent Platformという名称に置き換わっています。料金ページにも「Gemini Enterprise Agent Platform Colab Enterprise (formally Vertex AI)」という表記があり、改称であることが明記されています。ただし IAM ロールの ID(roles/aiplatform.user)や API のエンドポイントドメイン(aiplatform.googleapis.com)は旧名のままで、隣接するドキュメントページや過去のリリースノートにも「Vertex AI」表記が残る過渡期です。本章はドキュメント現物の表記に合わせて「Gemini Enterprise Agent Platform」を主表記とし、初出で旧称を添えます。

テキスト生成・エンベディング生成の関数名にも同様の整理が起きています。旧来の ML.GENERATE_TEXT・ML.GENERATE_EMBEDDING は現在も動作しますが、ML.GENERATE_EMBEDDING のリファレンスページ自体に「AI.GENERATE_EMBEDDING は同じ機能を出力列名を簡略化して提供する。新規クエリでは AI.GENERATE_EMBEDDING の使用を推奨する」という注記があります。テキスト生成の現行の使い分けガイドが比較しているのも AI.GENERATE_TEXT と AI.GENERATE の 2 つで、ML.GENERATE_TEXT は登場しません。本章では一貫して AI.* 系列を使います。

リモートモデルと接続の配管

AI 関数が Gemini モデルを呼び出す経路は 2 つあります。ひとつは接続なしで済ませるエンドユーザー認証、もうひとつはBigQuery Connection 経由です。

スカラー版:AI.GENERATE(エンドユーザー認証・接続不要) AI.GENERATE endpoint 指定のみ エンドユーザー認証 aiplatform.user Gemini Enterprise Agent Platform テーブル値版:AI.GENERATE_TEXT(接続経由・本章の主役) CREATE MODEL 接続経由 CLOUD_RESOURCE 接続(自動生成 SA) aiplatform.user Gemini Enterprise Agent Platform
図 14-1 AI.GENERATE は呼び出す本人の IAM 権限だけで直接 Gemini Enterprise Agent Platform を呼べる。AI.GENERATE_TEXT・AI.GENERATE_TABLE は CLOUD_RESOURCE 接続のサービスアカウントを経由する配管が必須で、本章が主に扱う構成はこちら。

エンドユーザー認証は、呼び出す本人が roles/bigquery.jobUser と roles/aiplatform.user(表示名は「Agent Platform User」)を持っていれば、接続もリモートモデルも作らずにその場で呼び出せます。AI.GENERATE の endpoint 引数にモデル名を渡すだけです。手軽な反面、48 時間を超える長時間クエリでは接続経由の認証を使うことが推奨されています。

接続経由は、CLOUD_RESOURCE 接続を作り、そのサービスアカウントに Agent Platform User 権限を与え、CREATE MODEL ... REMOTE WITH CONNECTION でリモートモデルを作成する 3 段構えです。AI.GENERATE_TEXT・AI.GENERATE_TABLE はこのリモートモデルを経由してしか呼べません。

bq mk --connection --location=US \
  --connection_type=CLOUD_RESOURCE \
  gemini_conn

bq show --connection --location=US PROJECT_ID.us.gemini_conn
# 出力の serviceAccountId をメモする

取得したサービスアカウントに権限を付与します。BigQuery の GRANT 文でも、コンソール・gcloud でも可能です。

GRANT `roles/aiplatform.user`
ON PROJECT `PROJECT_ID`
TO "connection:us.gemini_conn";

権限が揃ったら、接続を指定してリモートモデルを作成します。

CREATE OR REPLACE MODEL `PROJECT_ID.handson_ch14.gemini_flash`
REMOTE WITH CONNECTION `PROJECT_ID.us.gemini_conn`
OPTIONS (ENDPOINT = 'gemini-2.5-flash');

ENDPOINT には gemini-2.5-flash のようなモデル名、またはリージョンを含むエンドポイント URL のどちらも指定できます。公式の構文定義上 ENDPOINT は省略できない必須オプションで、既定値は用意されていません。接続とデータセットは同一ロケーションに置く必要があり、本書は一貫して US を使います。

課金の考え方は 2 階建てです。①クエリ自体の BigQuery コンピュート料金(AI 関数の入出力は小さいことが多く、通常わずか)と、②呼び出し先モデルの生成 AI 利用料金(入力・出力のトークン数に応じた従量課金)が別々にかかります。SQL code assist・Python code assist・データキャンバス・Data preparation という Gemini in BigQuery のコア機能はすべての BigQuery コンピュートオプションで追加料金なしと明記されていますが、これは UI 支援機能の話であり、AI 関数がモデルを呼び出す従量課金とは別枠です。なおデータインサイトはこのコア機能の範囲外で、別課金体系(Data Cloud Agent pricing)が案内されています。最新の単価は Gemini in BigQuery Pricing Overview で確認してください。

テキスト生成とマルチモーダル分析

テキストを生成する関数は、スカラー版とテーブル値版で性格が異なります。同じ非対称性はエンベディング生成にもあり、まとめて理解すると見通しが良くなります。

AI.GENERATE テキスト・スカラー・Gemini のみ AI.GENERATE_TEXT テキスト・テーブル値・複数モデル AI.EMBED エンベディング・スカラー AI.GENERATE_EMBEDDING エンベディング・テーブル値 バッチ化 バッチ化 埋め込みへ 埋め込みへ
図 14-2 AI 関数はスカラー(1 行に 1 値を返す)とテーブル値(テーブル全体をバッチ処理する)の 2 系統で対をなす。テキスト生成は AI.GENERATE / AI.GENERATE_TEXT、エンベディング生成は AI.EMBED / AI.GENERATE_EMBEDDING が同じ関係にある。
AI.GENERATE(スカラー)AI.GENERATE_TEXT(テーブル値)
対応モデルGemini のみGemini・Anthropic Claude・Llama・Mistral 等
認証エンドユーザー認証のみで可リモートモデル作成が必須
戻り値STRUCT(.result で文字列、output_schema 指定でカスタム構造)入力テーブル + result 等の列を追加
向く用途単発の要約・分類・構造化抽出テーブル全体へのバッチ推論、複数モデルの比較

AI.GENERATE は prompt を先頭の位置引数として渡し、endpoint・output_schema などは名前付き引数で渡します。

CREATE OR REPLACE TABLE `PROJECT_ID.handson_ch14.support_notes` AS
SELECT * FROM UNNEST([
  STRUCT(1 AS note_id, 'Customer says the app crashes when uploading files larger than 50MB on iOS 18.' AS note_text),
  STRUCT(2 AS note_id, '配送が3日遅れて届いたとのご連絡。梱包は問題なかった。' AS note_text),
  STRUCT(3 AS note_id, 'Billing was charged twice for the same subscription this month.' AS note_text)
]);

SELECT
  note_id,
  AI.GENERATE(
    CONCAT('次のサポートメモを日本語で1文に要約してください: ', note_text),
    endpoint => 'gemini-2.5-flash'
  ).result AS summary_ja
FROM `PROJECT_ID.handson_ch14.support_notes`
ORDER BY note_id;

output_schema を指定すると、自由文の代わりに指定した型でそのまま列が返ります。既定の result 列がカスタムスキーマに置き換わり、後続の SQL で JOIN や WHERE にそのまま使えるのが利点です。

SELECT
  note_id,
  AI.GENERATE(
    note_text,
    endpoint => 'gemini-2.5-flash',
    output_schema => 'category STRING, urgency STRING, one_line_ja STRING'
  ).*
FROM `PROJECT_ID.handson_ch14.support_notes`
ORDER BY note_id;

テーブル全体へバッチで推論をかけるには AI.GENERATE_TEXT を使います。入力側のテーブルまたはサブクエリには prompt という名前の STRING 列が必須です(別名の列しかない場合はエイリアスで prompt に変換します)。具体的な実行はハンズオン 14-2 で扱います。

AI.GENERATE はテキストだけでなく、ObjectRef 値を経由して画像・音声・動画・PDF も同じ関数で扱えます。第 10 章で扱ったオブジェクトテーブルには、ObjectRef 型の値を持つ ref 列が自動的に含まれているため、追加の変換なしにそのままプロンプトへ渡せます。

-- 前提: 第10章で例示した product_images(object_metadata = 'SIMPLE')のようなオブジェクトテーブルがある場合
SELECT
  uri,
  AI.GENERATE(
    ('この商品画像から気づいた点を日本語で1文述べてください', ref),
    endpoint => 'gemini-2.5-flash'
  ).result AS image_comment
FROM `PROJECT_ID.handson_ch10.product_images`;

動画を分析する場合は最初の 2 分までしか評価されない点、画像・音声・PDF は Gemini API がサポートする MIME タイプに限られる点に注意してください。

意味検索・RAG は 3 段階で組み立てます。①テキストをベクトル化する、②ベクトル列を持つテーブルに保存する、③似ているベクトルを検索する。検索の内部方式も、データが増えるにつれて総当たりから近似へ切り替えます。

ドキュメント群 AI.GENERATE_ EMBEDDING ユーザーの質問 AI.GENERATE_ EMBEDDING VECTOR_SEARCH (総当たり/ANN) 保存ベクトル 質問ベクトル 関連文書 AI.GENERATE _TEXT(回答)
図 14-3 ドキュメントと質問の両方を AI.GENERATE_EMBEDDING で同じベクトル空間に写像し、VECTOR_SEARCH で関連ドキュメントを引き当て、AI.GENERATE_TEXT / AI.GENERATE に文脈として渡して回答を生成する。これが RAG の最小構成。

エンベディング生成にも AI.EMBED(スカラー)と AI.GENERATE_EMBEDDING(テーブル値)の対があります。テーブル値版は入力に content という名前の STRING 列を要求し、出力は embedding という 1 列の ARRAY<FLOAT64> にまとまります。task_type には検索対象なら RETRIEVAL_DOCUMENT、検索クエリなら RETRIEVAL_QUERY を指定し、同じ埋め込み空間でも役割に応じたベクトルを生成させます。テキスト用のモデルは gemini-embedding-001(GA)・text-embedding-005・text-multilingual-embedding-002 が現行、gemini-embedding-2-preview はプレビューです(旧来の text-embedding-004 は 2026 年 1 月に非推奨化され、後継は gemini-embedding-001)。画像とテキストを同じ空間に埋め込むマルチモーダル用途には multimodalembedding@001 を使います。

CREATE OR REPLACE MODEL `PROJECT_ID.handson_ch14.embed_model`
REMOTE WITH CONNECTION `PROJECT_ID.us.gemini_conn`
OPTIONS (ENDPOINT = 'gemini-embedding-001');

ベクトル列を持つテーブルができたら、VECTOR_SEARCH 関数で似ているベクトルを探します。ベクトルインデックスが無い状態では、全行との距離を計算する総当たり(brute force)検索が行われます。距離指標は EUCLIDEAN(既定)・COSINE・DOT_PRODUCT から選べます。行数が増えると総当たりのコストが線形に増えるため、CREATE VECTOR INDEX で近似最近傍探索(Approximate Nearest Neighbor, ANN)に切り替えます。

総当たり(インデックスなし) クエリ 全行との距離を計算 インデックスあり(ANN) クエリ 候補クラスタ 対象外クラスタ 近いクラスタだけを探索
図 14-4 インデックスが無ければクエリから全行へ距離計算する(総当たり)。インデックスがあれば、クラスタや木構造で候補を絞り込んでから距離計算するため、近似だが高速になる。
CREATE OR REPLACE VECTOR INDEX faq_embedding_idx
ON `PROJECT_ID.handson_ch14.faq_embeddings`(embedding)
OPTIONS (index_type = 'IVF', distance_type = 'COSINE');

インデックスタイプは 2 種類あります。IVF(Inverted File Index)は k-means クラスタリングでベクトル空間を分割し、少数バッチのクエリに向きます。TreeAH は Google ScaNN ベースの木構造と Asymmetric Hashing を組み合わせ、数百件以上のクエリベクトルをまとめて処理する大規模バッチに向きますが、対象テーブルが 2 億行以下であることが適用条件です。どちらもインデックス対象のテーブルが 10 MB 未満の場合はインデックスが populate されず、VECTOR_SEARCH は自動的に総当たりへフォールバックします。これは意図された挙動で、小さいテーブルではインデックスを作る意味が薄いためです。populate 状況は INFORMATION_SCHEMA.VECTOR_INDEXES ビューの coverage_percentage 列で確認できます。

実務の視点: 検索対象が数千行程度なら、インデックスを作らず総当たりのままで十分なことが多く、まず動くものを作ってから行数とレイテンシの実測を見てインデックス化を判断するのが無駄がありません。同時に投げるクエリベクトルの数(バッチ規模)によって IVF と TreeAH の向き不向きが変わる点も選定時に確認してください。

Gemini in BigQuery のアシスト機能

ここからは SQL の外側、BigQuery Studio の UI 上で Gemini が支援する機能です。いずれも裏側で AI 関数を直接書く代わりに、対話や自然言語でその作業を肩代わりします。

Gemini in BigQuery データキャンバス 探索・可視化 データインサイト テーブル理解を自動化 会話型分析 業務ユーザーと対話 SQL 生成・説明 SQL 作成を支援
図 14-5 データキャンバスは探索と可視化、データインサイトはテーブル理解の自動化、会話型分析は業務ユーザー向けの対話、SQL 生成・説明は日常的なクエリ作成の補助という異なる利用場面を担う。

データキャンバス(BigQuery data canvas)は、自然言語プロンプトとグラフィカルな DAG(有向非環グラフ)でテーブルの発見・結合・クエリ実行・可視化を行うインターフェースです。1 つの調査を複数の分岐に広げながら、SQL を書かずに試行錯誤できます。利用には BigQuery Studio User(roles/bigquery.studioUser)と Gemini for Google Cloud User(roles/cloudaicompanion.user)の権限が必要です。

データインサイト(Data insights)は、テーブルのメタデータから Gemini が自然言語の疑問文とそれに対応する SQL を自動生成し、パターン検出・異常検知・データ品質の把握を助けます。複数テーブルの関係を示すインタラクティブな関係グラフも生成します。列レベルのアクセス制御がかかっている場合は、全列への読み取りアクセスが必要です。

会話型分析(Conversational analytics)は、テーブル・ビュー・UDF・カスタムメタデータ・検証済みクエリを「知識ソース」として束ねたデータエージェントを作成し、業務ユーザーが自然言語でデータに問いかけられるようにする機能です。エージェントは質問に応じて自動で SQL を生成し、どの知識ソースを使うか自身で判断します。2026 年 7 月に GA(一般提供)となりました。生成 AI の一般的な限界として、もっともらしいが事実と異なる出力をすることがある点は運用上意識してください。

SQL 生成・説明機能はより地味ですが日常的に効きます。自然言語から SQL を生成する機能と SQL クエリの説明機能は GA、コメントからの SQL 変換やクエリ補完はプレビューです。これら Gemini in BigQuery のコア機能は、追加料金なしですべての BigQuery コンピュートオプションで利用できます。

ハンズオン 14-1|AI.GENERATE でリモートモデル無しにテキスト生成・構造化抽出を試す

接続もリモートモデルも作らず、AI.GENERATE だけでテキスト要約と構造化抽出を試します。課金プロジェクト必須(gemini-2.5-flash への軽い呼び出し数回、概算数円未満)。

  1. まだ付与していない場合、自分自身に Agent Platform User 権限を付与します(YOUR_EMAIL は自分のログインアカウント)。
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="user:YOUR_EMAIL" \
  --role="roles/aiplatform.user"
  1. 演習用データセットと、要約対象の小さなテーブルを作成します。
bq mk --location=US --dataset PROJECT_ID:handson_ch14

CREATE OR REPLACE TABLE `PROJECT_ID.handson_ch14.support_notes` AS
SELECT * FROM UNNEST([
  STRUCT(1 AS note_id, 'Customer says the app crashes when uploading files larger than 50MB on iOS 18.' AS note_text),
  STRUCT(2 AS note_id, '配送が3日遅れて届いたとのご連絡。梱包は問題なかった。' AS note_text),
  STRUCT(3 AS note_id, 'Billing was charged twice for the same subscription this month.' AS note_text)
]);
  1. AI.GENERATE で各メモを日本語 1 文に要約します。
-- 処理: リテラルテーブル3行のみ(無料枠内目安)。呼び出し: gemini-2.5-flashへ3回
SELECT
  note_id,
  AI.GENERATE(
    CONCAT('次のサポートメモを日本語で1文に要約してください: ', note_text),
    endpoint => 'gemini-2.5-flash'
  ).result AS summary_ja
FROM `PROJECT_ID.handson_ch14.support_notes`
ORDER BY note_id;

期待される出力: note_id ごとに日本語 1 文の要約が返ります(生成 AI のため文言は実行のたびに多少変わります)。

  1. 今度は output_schema を指定し、自由文の代わりに構造化された列を取得します。
SELECT
  note_id,
  AI.GENERATE(
    note_text,
    endpoint => 'gemini-2.5-flash',
    output_schema => 'category STRING, urgency STRING, one_line_ja STRING'
  ).*
FROM `PROJECT_ID.handson_ch14.support_notes`
ORDER BY note_id;

期待される出力: category・urgency・one_line_ja の 3 列が独立したフィールドとして返ります。result 列は output_schema 指定時には現れません。

後片付け: 続けてハンズオン 14-2 に進む場合はデータセットを残しておいて構いません。ここで終了する場合は削除します。

bq rm -r -f -d PROJECT_ID:handson_ch14
ハンズオン 14-2|CLOUD_RESOURCE 接続とリモートモデルを組み、AI.GENERATE_TEXT でバッチ推論する

本章の主役である接続経由の配管を実際に組み、テーブル全体へバッチで感情分類をかけます。課金プロジェクト必須(接続作成 + 3 行分のバッチ推論、概算数円)。ハンズオン 14-1 で作成したデータセットを使い回します(削除済みの場合は bq mk --location=US --dataset PROJECT_ID:handson_ch14 で再作成してください)。

  1. CLOUD_RESOURCE 接続を作成し、サービスアカウント ID を確認します。
bq mk --connection --location=US \
  --connection_type=CLOUD_RESOURCE \
  gemini_conn

bq show --connection --location=US PROJECT_ID.us.gemini_conn
# 出力の serviceAccountId をメモする
  1. 接続のサービスアカウントに Agent Platform User 権限を付与します。
GRANT `roles/aiplatform.user`
ON PROJECT `PROJECT_ID`
TO "connection:us.gemini_conn";
  1. 接続を指定してリモートモデルを作成します。
CREATE OR REPLACE MODEL `PROJECT_ID.handson_ch14.gemini_flash`
REMOTE WITH CONNECTION `PROJECT_ID.us.gemini_conn`
OPTIONS (ENDPOINT = 'gemini-2.5-flash');
  1. 感情分類の対象となる小さなレビューテーブルを作成します。
CREATE OR REPLACE TABLE `PROJECT_ID.handson_ch14.reviews` AS
SELECT * FROM UNNEST([
  STRUCT(1 AS review_id, 'The onboarding flow was smooth but support replied a day late.' AS review_text),
  STRUCT(2 AS review_id, '素晴らしい製品です。ただ価格がやや高いと感じました。' AS review_text),
  STRUCT(3 AS review_id, 'Crashed twice during setup, would not recommend yet.' AS review_text)
]);
  1. AI.GENERATE_TEXT でテーブル全体にバッチ推論をかけます。入力サブクエリには prompt 列が必須です。
-- 処理: リテラルテーブル3行のみ。呼び出し: gemini-2.5-flashへ3回(概算数円未満)
SELECT
  review_id,
  review_text,
  result AS sentiment
FROM AI.GENERATE_TEXT(
  MODEL `PROJECT_ID.handson_ch14.gemini_flash`,
  (
    SELECT
      review_id,
      review_text,
      CONCAT('次のレビューの感情を POSITIVE / NEUTRAL / NEGATIVE の一語で答えてください: ', review_text) AS prompt
    FROM `PROJECT_ID.handson_ch14.reviews`
  ),
  STRUCT(16 AS max_output_tokens, 0.0 AS temperature)
)
ORDER BY review_id;

期待される出力: review_id=1 は NEUTRAL 寄り、2 は POSITIVE、3 は NEGATIVE といった判定が sentiment 列に返ります(生成 AI のため文言は多少ばらつきます)。AI.GENERATE_TEXT は入力テーブルの列に result・status などの列を追加して返す点を確認してください。

後片付け: 続けてハンズオン 14-3 に進む場合は接続とデータセットを残しておいて構いません。ここで終了する場合は削除します。

bq rm -r -f -d PROJECT_ID:handson_ch14
bq rm --connection --location=US PROJECT_ID.us.gemini_conn
ハンズオン 14-3|AI.GENERATE_EMBEDDING と VECTOR_SEARCH で意味検索し、ベクトルインデックスの制約を確認する

社内 FAQ を想定した意味検索を組み立て、ベクトルインデックスの 10 MB 制約を実際に確認します。課金プロジェクト必須(エンベディング生成数回、概算数円未満)。ハンズオン 14-2 で作成した接続を使い回します(削除済みの場合は再作成してください)。

  1. エンベディング用のリモートモデルを作成します。
CREATE OR REPLACE MODEL `PROJECT_ID.handson_ch14.embed_model`
REMOTE WITH CONNECTION `PROJECT_ID.us.gemini_conn`
OPTIONS (ENDPOINT = 'gemini-embedding-001');
  1. FAQ のテーブルを作成し、AI.GENERATE_EMBEDDING でベクトル化します。入力サブクエリには content 列が必須です。
CREATE OR REPLACE TABLE `PROJECT_ID.handson_ch14.faqs` AS
SELECT * FROM UNNEST([
  STRUCT(1 AS faq_id, '返品はどのくらいの期間まで可能ですか' AS question),
  STRUCT(2 AS faq_id, '注文をキャンセルする方法を教えてください' AS question),
  STRUCT(3 AS faq_id, '配送にかかる日数はどれくらいですか' AS question),
  STRUCT(4 AS faq_id, 'パスワードを忘れた場合はどうすればいいですか' AS question),
  STRUCT(5 AS faq_id, '支払い方法にはどんな種類がありますか' AS question)
]);

-- 処理: 5行のみ。呼び出し: gemini-embedding-001へ5回(概算数円未満)
CREATE OR REPLACE TABLE `PROJECT_ID.handson_ch14.faq_embeddings` AS
SELECT faq_id, question, embedding
FROM AI.GENERATE_EMBEDDING(
  MODEL `PROJECT_ID.handson_ch14.embed_model`,
  (SELECT faq_id, question, question AS content FROM `PROJECT_ID.handson_ch14.faqs`),
  STRUCT('RETRIEVAL_DOCUMENT' AS task_type)
);
  1. 質問をその場でベクトル化し、VECTOR_SEARCH で総当たり検索します(インデックスはまだ作成していません)。
SELECT base.faq_id, base.question, distance
FROM VECTOR_SEARCH(
  TABLE `PROJECT_ID.handson_ch14.faq_embeddings`,
  'embedding',
  (
    SELECT embedding
    FROM AI.GENERATE_EMBEDDING(
      MODEL `PROJECT_ID.handson_ch14.embed_model`,
      (SELECT 'パスワードがわからなくなった' AS content),
      STRUCT('RETRIEVAL_QUERY' AS task_type)
    )
  ),
  top_k => 2
)
ORDER BY distance;

期待される出力: faq_id=4(パスワードの FAQ)が distance の小さい 1 位として返ります。

  1. ベクトルインデックスを作成し、populate 状況を確認します。
CREATE OR REPLACE VECTOR INDEX faq_embedding_idx
ON `PROJECT_ID.handson_ch14.faq_embeddings`(embedding)
OPTIONS (index_type = 'IVF', distance_type = 'COSINE');

SELECT table_name, index_name, coverage_percentage
FROM `PROJECT_ID.handson_ch14`.INFORMATION_SCHEMA.VECTOR_INDEXES
WHERE index_status = 'ACTIVE';

期待される出力: coverage_percentage は 0 です。faq_embeddings は数 KB しかなく 10 MB の閾値に届かないため、インデックスは作成されても populate されません。この状態でも手順 3 の VECTOR_SEARCH は総当たりへ自動的にフォールバックして動作を続けます。実運用でインデックスの効果を得るには、対象テーブルが 10 MB 以上に育ってから真価を発揮する点を覚えておいてください。

後片付け:

bq rm -r -f -d PROJECT_ID:handson_ch14
bq rm --connection --location=US PROJECT_ID.us.gemini_conn

つまずきポイント

Vertex AI と Gemini Enterprise Agent Platform の表記ゆれに戸惑う: 2026 年時点のドキュメントは改称の過渡期で、CREATE MODEL・接続・権限のページは「Gemini Enterprise Agent Platform」で統一されている一方、IAM ロール ID(roles/aiplatform.user)や API ドメイン(aiplatform.googleapis.com)、一部の隣接ページや過去のリリースノートには旧称「Vertex AI」が残っています。どちらも同じ基盤を指しているという前提で読み進めてください。
接続のサービスアカウントへの権限付与漏れで Permission denied になる: CLOUD_RESOURCE 接続を作成しただけでは呼び出せません。接続に紐づくサービスアカウントへ roles/aiplatform.user(Agent Platform User)を明示的に付与するまで、AI.GENERATE_TEXT・AI.GENERATE_EMBEDDING はエラーになります。「接続を作った」と「権限を付与した」は別の手順です。
入力テーブルの必須列名を忘れてエラーになる: AI.GENERATE_TEXT の入力テーブル・サブクエリには prompt、AI.GENERATE_EMBEDDING には content という名前の STRING 列が必須です。別名の列しかない場合は SELECT ... AS prompt/AS content で明示的にエイリアスしてください。
ベクトルインデックスを作ったのに速くならないと誤解する: 対象テーブルが 10 MB 未満だと、CREATE VECTOR INDEX は成功してもインデックスは populate されず、VECTOR_SEARCH は総当たりのまま動き続けます。「インデックスを作ったのに効果が無い」と感じたら、まず INFORMATION_SCHEMA.VECTOR_INDEXES の coverage_percentage を確認してください。

理解度チェック

Q1. AI.GENERATE と AI.GENERATE_TEXT の違いを、認証方式と対応モデルの観点から説明してください。

AI.GENERATE はスカラー関数で Gemini モデルのみに対応し、接続やリモートモデルを作らずエンドユーザー本人の IAM 権限(roles/aiplatform.user)だけで呼び出せます。AI.GENERATE_TEXT はテーブル値関数で、Gemini に加え Anthropic Claude・Llama・Mistral 等のパートナーモデルにも対応しますが、CLOUD_RESOURCE 接続を経由したリモートモデルの作成が必須です。

Q2. AI.GENERATE_EMBEDDING と ML.GENERATE_EMBEDDING はどういう関係にありますか?

ML.GENERATE_EMBEDDING は現在も動作しますが、公式ドキュメント自身が「AI.GENERATE_EMBEDDING は同じ機能を出力列名を簡略化して提供する、新規クエリでは AI.GENERATE_EMBEDDING を推奨する」と明記しています。旧関数が廃止されたわけではありませんが、現行の推奨は AI.GENERATE_EMBEDDING です。

Q3. ベクトルインデックスが無い場合とある場合で、VECTOR_SEARCH の検索方式はどう変わりますか?また 10 MB 未満のテーブルではどうなりますか?

インデックスが無ければ全行との距離を計算する総当たり(brute force)検索になります。インデックスがあれば、IVF(k-means クラスタリング)や TreeAH(木構造 + Asymmetric Hashing)でベクトル空間を絞り込んでから距離計算する近似最近傍探索(ANN)に切り替わります。ただし対象テーブルが 10 MB 未満の場合は、インデックスを作成しても populate されず(coverage_percentage が 0)、VECTOR_SEARCH は総当たりへ自動的にフォールバックします。

Q4.(シナリオ)自社の問い合わせ文をバッチで要約したい。Gemini だけでなく Anthropic Claude の出力も比較評価したいと考えています。使うべき関数はどれですか?
A. AI.GENERATE B. AI.GENERATE_TEXT C. AI.EMBED D. AI.CLASSIFY

B。 AI.GENERATE_TEXT はテーブル値関数としてテーブル全体をバッチ処理でき、Gemini に加えて Anthropic Claude・Llama・Mistral 等のパートナーモデルにも対応します。A は Gemini 専用のスカラー関数、C はエンベディング生成、D は分類に特化した管理型関数で、いずれも複数モデルの比較には向きません。

Q5.(シナリオ)50 GB あるカタログ商品テーブルに CREATE VECTOR INDEX を実行した直後に VECTOR_SEARCH を試したところ、期待したほど速くなりませんでした。何を確認すべきですか?
A. distance_type の指定漏れ B. top_k の値が大きすぎないか C. INFORMATION_SCHEMA.VECTOR_INDEXES の coverage_percentage D. 接続のサービスアカウント権限

C。 CREATE VECTOR INDEX の完了は DDL の成功を意味するだけで、インデックスの populate(refresh)には別途時間がかかります。coverage_percentage が低い間は VECTOR_SEARCH が総当たりのまま動き続けるため、まず populate の進み具合を確認するのが筋です。A・B は速度に影響し得ますが、今回のように「作った直後」という状況からはインデックス未反映がまず疑われます。D は接続を伴わない VECTOR_SEARCH 自体の速度には無関係です。

まとめと次章へ