bigquery で埋め込みベクトルを自動生成させる


| 6 min read | engineer bigquery vector-search

bigquery と vertex ai を統合してテーブルにあるカラムから埋め込みベクトル(embedding)が生成できる。最近発表された機能にてテーブルの更新などにも追従して自律的に生成できるようになったので試してみる。2026/2/20 に公式ブログの記事になっているので、そのあたりに追加された機能。

embedding の自律生成

公式ブログでは、自律型埋め込み生成機能とかいてある。以前は embedding の生成と管理のためのパイプラインを組む必要があったが、bigquery 上で自動的にコンテンツの更新、追加を検知して embedding を生成する。前の記事(https://blog.uni-3.app/dbt-manage-bq-embedding/)では dbt(の incremental 機能)を使うなどして管理していたが、それが不要になる。

また、従来は embedding 作成用のモデル定義には create model などの DDL を実行していたが、それらの工程が簡略化されている(現時点でプレビュー機能)。

embedding カラム作成

前提として、GCP の設定は済んでいるものとする。この記事では embedding を生成するテーブルを blog_content、対象カラム名を content、としている。

カラム名は任意。ここでは embedding としている。model 引数は特定のモデル embeddinggemma-300m のみ対応している。その他のモデルを使用する場合は endpoint と、connection_id を指定する(従来のモデル参照方法を用いる)

ALTER TABLE `project_id`.`dataset`.`blog_content`
ADD COLUMN IF NOT EXISTS embedding
  STRUCT<result ARRAY<FLOAT64>, status STRING>
  GENERATED ALWAYS AS (
    AI.EMBED(
      content,
      model => 'embeddinggemma-300m'
    )
) STORED OPTIONS (asynchronous = TRUE);

他にも気になる構文はあるが、とくに公式ドキュメントに記載はなかった。

今回は既存テーブルに作成したが、テーブル作成時でも指定できる。やるとしたらこんな感じか。

CREATE TABLE dataset.blog_content (
  slug STRING,
  content STRING,
  embedding STRUCT<result ARRAY<FLOAT64>, status STRING>
    GENERATED ALWAYS AS (AI.EMBED(
      model => 'embeddinggemma-300m'
    ))
    STORED OPTIONS( asynchronous = TRUE )
);
  • 生成後のスキーマ確認

カラム定義と、自律生成関連のメタデータが追加されているのがわかる

bq show --schema --format=prettyjson "dataset.blog_content"
[
  {
    "name": "content",
    "type": "STRING"
  },
  {
    "name": "slug",
    "type": "STRING"
  },
  {
    "fields": [
      {
        "mode": "REPEATED",
        "name": "result",
        "type": "FLOAT"
      },
      {
        "name": "status",
        "type": "STRING"
      }
    ],
    "generatedColumn": {
      "generatedExpressionInfo": {
        "asynchronous": true,
        "generationExpression": "AI.EMBED(content, model => \"embeddinggemma-300m\")",
        "stored": true
      },
      "generatedMode": "GENERATED_ALWAYS"
    },
    "name": "embedding",
    "type": "RECORD"
  }
]

5 分くらい経ったら、embedding が生成されていた。ベクトルの次元数はモデルカードにある通り 768 次元。既存テーブルに embedding カラムが追加されて困ることといえば、console 画面からデータのプレビューが見づらくなることだろうか。

検索

AI.SEARCH 関数を使う。現時点で Pre-GA 機能。

パイソン、で検索してみる。モデルの定義は参照元のテーブルを用いるため、各種パラメータ(次元数やモデル指定など)の指定は省略できる。

base.以下に参照元テーブルのカラムが入っている。distance も取得できる。

SELECT
  base.slug,
  base.title,
  distance
FROM AI.SEARCH(
  TABLE `project_id.dataset.blog_content`,
  "content",
  "パイソン",
  top_k=>3
);

結果

slug	title	distance
dlt-load-github-content	dlt(data load tool)でgithub上のファイルをbigqueryに転送する	1.1508814329244923
dagster-pyairbyte-dbt-init	ローカルでdagsterを使ってデータパイプラインの一連の処理を試した	1.1746919840524137
chatgpt-gradio 	ChatGPT with gradio	1.1749497690166033

embedding を作成していないカラム、"content"->"title" にしてみたら No generation expression found for the column_to_search: title. というエラーが表示されたので、embedding の設定を見て判断してそう。

感想

AI.EMBED 関数を用いることで、既存テーブル(embedding の元になるカラムが存在するテーブルに対して)自律的に embedding の生成が可能になった。

作成と検索には bigquery のクエリ権限があればいけた。AI.EMBED の関数のリファレンスによると、 embeddinggemma-300m は bigquery 内部の組み込みモデルらしい(vertex ai を通さない)という旨の記述があったので、内部でいい感じに機能するものなのだろうか。簡潔にかけるし今後は model 引数を使って定義するのだろう、と期待できる

embedding の更新タイミングやエラー状況を把握したい場合は INFORMATION_SCHEMA などからジョブの実行履歴を取得することに注意。ジョブの実行履歴は以下のようなクエリで取得可能。

-- US region
SELECT * FROM `region-us.INFORMATION_SCHEMA.JOBS` j
WHERE EXISTS (
  SELECT 1
  FROM unnest(j.referenced_tables) t
  WHERE
  t.table_id = 'blog_content'
)
 
AND starts_with(job_id, 'gc')
-- only errors
--AND error_result IS NOT NULL
ORDER BY j.creation_time DESC;

参考