100 万個のベクトル、ループなし: AlloyDB を使用してエンベディングを大規模に生成する

1. 概要

この Codelab では、スケーラブルなナレッジベース検索アプリケーションを構築します。Python スクリプトとループを使用して複雑な ETL パイプラインを管理し、ベクトル エンベディングを生成する代わりに、AlloyDB AI を使用して、単一の SQL コマンドを使用してデータベース内でエンベディング生成をネイティブに処理します。

d4324260c68d4a70.png

作成するアプリの概要

高パフォーマンスの「検索可能」なナレッジベース データベース アプリケーション。

学習内容

ここでは以下について学びます。

  • AlloyDB クラスタをプロビジョニングし、AI 拡張機能を有効にします。
  • SQL を使用して合成データ(50,000 行以上)を生成します。
  • バッチ処理を使用して、データセット全体のベクトル エンベディングをバックフィルします。
  • リアルタイムの増分トリガーを設定して、新しいデータを自動的に埋め込みます。
  • 「Flexing Context」のハイブリッド検索(ベクトル + SQL フィルタ)を実行します。

要件

  • ブラウザ(ChromeFirefox など)
  • 課金を有効にした Google Cloud プロジェクト
  • SQL に関する基本的な知識。

2. 始める前に

プロジェクトを作成する

  1. Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。
  2. Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください
  1. Google Cloud 上で動作するコマンドライン環境の Cloud Shell を使用します。Google Cloud コンソールの上部にある [Cloud Shell をアクティブにする] をクリックします。

[Cloud Shell をアクティブにする] ボタンの画像

  1. Cloud Shell に接続したら、次のコマンドを使用して、すでに認証済みであることと、プロジェクトがプロジェクト ID に設定されていることを確認します。
gcloud auth list
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project
  1. プロジェクトが設定されていない場合は、次のコマンドを使用して設定します。
gcloud config set project <YOUR_PROJECT_ID>
  1. 必要な API を有効にする: リンクにアクセスして、API を有効にします。

または、この操作に gcloud コマンドを使用することもできます。gcloud コマンドとその使用方法については、ドキュメントをご覧ください。

gcloud services enable \
  alloydb.googleapis.com \
  compute.googleapis.com \
  cloudresourcemanager.googleapis.com \
  servicenetworking.googleapis.com \
  aiplatform.googleapis.com

注意点とトラブルシューティング

「ゴースト プロジェクト」症候群

gcloud config set project を実行したが、実際にはコンソール UI で別のプロジェクトが表示されている。左上のプルダウンでプロジェクト ID を確認してください。

請求のバリケード

プロジェクトを有効にしたが、請求先アカウントを忘れた。AlloyDB は高性能エンジンです。ガソリン タンク(課金)が空の場合、起動しません。

API 伝播の遅延

[API を有効にする] をクリックしたのに、コマンドラインに Service Not Enabled と表示される。60 秒ほど待ちます。クラウドがニューロンを起動するまでしばらくお待ちください。

割り当て Quags

新しいトライアル アカウントを使用している場合は、AlloyDB インスタンスのリージョン割り当てに達する可能性があります。us-central1 が失敗した場合は、us-east1 を試してください。

「非表示」のサービス エージェント

AlloyDB サービス エージェントに aiplatform.user ロールが自動的に付与されないことがあります。SQL クエリが後で Gemini と通信できない場合、通常はこのことが原因です。

3. データベースの設定

このラボでは、テストデータのデータベースとして AlloyDB を使用します。クラスタを使用して、データベースやログなどのすべてのリソースを保持します。各クラスタには、データへのアクセス ポイントを提供するプライマリ インスタンスがあります。テーブルには実際のデータが格納されます。

テスト データセットを読み込む AlloyDB クラスタ、インスタンス、テーブルを作成しましょう。

  1. ボタンをクリックするか、以下のリンクを Google Cloud コンソールのユーザーがログインしているブラウザにコピーします。

  1. この手順が完了すると、リポジトリがローカルの Cloud Shell エディタにクローンされ、プロジェクト フォルダから次のコマンドを実行できるようになります(プロジェクト ディレクトリにいることを確認することが重要です)。
sh run.sh
  1. UI を使用します(ターミナルのリンクをクリックするか、ターミナルの [ウェブでプレビュー] リンクをクリックします)。
  2. プロジェクト ID、クラスタ名、インスタンス名の詳細を入力して、開始します。
  3. ログがスクロールしている間にコーヒーを飲んで、裏側で何が行われているかについてはこちらをご覧ください。10 ~ 15 分ほどかかることがあります。

注意点とトラブルシューティング

「忍耐」の問題

データベース クラスタは重いインフラストラクチャです。ページを更新したり、「フリーズした」ように見える Cloud Shell セッションを強制終了したりすると、部分的にプロビジョニングされた「ゴースト」インスタンスが作成され、手動で介入しないと削除できなくなる可能性があります。

リージョンが一致しない

us-central1 で API を有効にしたが、asia-south1 でクラスタをプロビジョニングしようとすると、割り当ての問題やサービス アカウントの権限の遅延が発生する可能性があります。ラボ全体で 1 つのリージョンを使用してください。

ゾンビ クラスタ

以前にクラスタに同じ名前を使用し、削除していない場合、スクリプトでクラスタ名がすでに存在すると表示されることがあります。クラスタ名はプロジェクト内で一意にする必要があります。

Cloud Shell のタイムアウト

コーヒー ブレイクに 30 分かかると、Cloud Shell がスリープ状態になり、sh run.sh プロセスが切断されることがあります。タブをアクティブな状態に保つ

4. スキーマのプロビジョニング

このステップでは、次の内容について説明します。

879263c907f3cac6.png

AlloyDB クラスタとインスタンスが実行されたら、AlloyDB Studio の SQL エディタに移動して、AI 拡張機能を有効にしてスキーマをプロビジョニングします。

1e3ac974b18a8113.png

インスタンスの作成が完了するまで待つ必要がある場合があります。完了したら、クラスタの作成時に作成した認証情報を使用して AlloyDB にログインします。PostgreSQL の認証には次のデータを使用します。

  • ユーザー名: 「postgres
  • データベース: 「postgres
  • パスワード: 「alloydb」(作成時に設定したパスワード)

AlloyDB Studio への認証が成功すると、エディタに SQL コマンドが入力されます。最後のウィンドウの右にあるプラス記号を使用して、複数のエディタ ウィンドウを追加できます。

28cb9a8b6aa0789f.png

必要に応じて [実行]、[形式]、[クリア] オプションを使用して、エディタ ウィンドウに AlloyDB のコマンドを入力します。

拡張機能を有効にする

このアプリのビルドには、拡張機能 pgvectorgoogle_ml_integration を使用します。pgvector 拡張機能を使用すると、ベクトル エンベディングを保存して検索できます。google_ml_integration 拡張機能は、Vertex AI 予測エンドポイントにアクセスして SQL で予測を取得するために使用する関数を提供します。次の DDL を実行して、これらの拡張機能を有効にします。

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

テーブルを作成する

スケールを実証するにはデータセットが必要です。CSV をインポートする代わりに、SQL を使用して 50,000 行の合成「ヘルプ記事」を即座に生成します。

AlloyDB Studio で次の DDL ステートメントを使用してテーブルを作成できます。

-- 1. Create the table
CREATE TABLE help_articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    category TEXT,
    product_version TEXT,
    content_body TEXT,
    embedding vector(768) -- Dimension for text-embedding-005
);

-- 2. Generate 50,000 rows of synthetic data
INSERT INTO help_articles (title, category, product_version, content_body)
SELECT
    'Help Article ' || i,
    CASE 
        WHEN i % 3 = 0 THEN 'Billing' 
        WHEN i % 3 = 1 THEN 'Technical' 
        ELSE 'General' 
    END,
    CASE 
        WHEN i % 2 = 0 THEN '2.0' 
        ELSE '1.0' 
    END,
    'This article covers common issues regarding ' || 
    CASE 
        WHEN i % 3 = 0 THEN 'payment failures, invoice disputes, and credit card updates.'
        WHEN i % 3 = 1 THEN 'connection timeouts, latency issues, and API errors.'
        ELSE 'account profile settings, password resets, and user roles.' 
    END
FROM generate_series(1, 50000) AS i;

item_vector 列には、テキストのベクトル値を格納できます。

データを確認します。

SELECT count(*) FROM help_articles;
-- Output: 50000

データベース フラグを有効にする

インスタンス構成コンソールに移動し、[プライマリを編集] をクリックして、[高度な構成] に移動し、[データベース フラグを追加] をクリックします。

  1. google_ml_integration.enable_model_support フラグがオンに設定されていることを確認します:

ない場合は、フラグのプルダウンに入力して [オン] に設定し、インスタンスを更新します。

  1. google_ml_integration.enable_faster_embedding_generation フラグがオンに設定されていることを確認します。

ない場合は、フラグのプルダウンに入力して [オン] に設定し、インスタンスを更新します。

データベース フラグを構成する手順:

  1. Google Cloud コンソールで、[クラスタ] ページに移動します。

[クラスタ] に移動

  1. [リソース名] 列でクラスタをクリックします。
  2. [概要] ページで、クラスタ内の [インスタンス] に移動し、インスタンスを選択して [編集] をクリックします。
  3. インスタンスに対してデータベース フラグの追加、変更、または削除を行います。

フラグを追加する

  1. インスタンスにデータベース フラグを追加するには、[フラグを追加] をクリックします。
  2. [新しいデータベース フラグ] リストからフラグを選択します。
  3. フラグの値を指定します。
  4. [完了] をクリックします。
  5. [インスタンスを更新] をクリックします。
  6. google_ml_integration 拡張機能のバージョンが 1.5.2 以降であることを確認します。

拡張機能のバージョンを確認するには、次のコマンドを使用します。

SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';

拡張機能を上位に更新する必要がある場合は、次のコマンドを使用します。

ALTER EXTENSION google_ml_integration UPDATE;

権限を付与

  1. ユーザーが自動エンベディング生成を管理できるようにするには、google_ml.embed_gen_progress テーブルと google_ml.embed_gen_settings テーブルに対する INSERT、UPDATE、DELETE の各権限を付与します。
GRANT INSERT, UPDATE, DELETE ON google_ml.embed_gen_progress TO postgres;

「postgres」は、権限が付与される USER_NAME です。

  1. 次のステートメントを実行して、「embedding」関数に対する実行権限を付与します。
GRANT EXECUTE ON FUNCTION embedding TO postgres;

AlloyDB サービス アカウントに Vertex AI ユーザーロールを付与する

Google Cloud IAM コンソールで、AlloyDB サービス アカウント(service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com のような形式)に「Vertex AI ユーザー」ロールへのアクセス権を付与します。PROJECT_NUMBER にはプロジェクト番号が設定されます。

または、Cloud Shell ターミナルから次のコマンドを実行することもできます。

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

注意点とトラブルシューティング

「パスワード忘れ」ループ

「ワンクリック」設定を使用していて、パスワードを忘れた場合は、コンソールのインスタンスの基本情報ページに移動し、[編集] をクリックして postgres パスワードをリセットします。

「拡張機能が見つかりません」というエラー

CREATE EXTENSION が失敗するのは、インスタンスが初期プロビジョニングの「メンテナンス」状態または「更新中」状態のままになっていることが原因であることがよくあります。インスタンスの作成ステップが完了しているかどうかを確認し、必要に応じて数秒待ちます。

5. 「ワンショット」ベクトル生成

これがラボの核心です。これらの 50,000 行を処理するために Python ループを記述する代わりに、ai.initialize_embeddings 関数を使用します。

この単一のコマンドは、次の 2 つの処理を行います。

  1. 既存のすべての行をバックフィルします。
  2. トリガーを作成して、今後の行を自動的に埋め込みます。

AlloyDB クエリエディタから次の SQL ステートメントを実行します。

CALL ai.initialize_embeddings(
  model_id => 'text-embedding-005',
  table_name => 'help_articles',
  content_column => 'content_body',
  embedding_column => 'embedding',
  incremental_refresh_mode => 'transactional'
);

エンベディングを確認する

embedding 列にデータが入力されていることを確認します。

SELECT id, left(content_body, 30), substring(embedding::text, 1, 30) as vector_partial 
FROM help_articles;

次のような結果が表示されます。

a872b8926a164275.png

何が起こったのか。

  1. 大規模なバックフィル: 既存の 50,000 行を自動的にスキャンし、Vertex AI を介してエンベディングを生成します。
  2. 自動化: incremental_refresh_mode => ‘transactional' を設定すると、AlloyDB は内部トリガーを自動的に設定します。help_articles に挿入された新しい行は、すぐにエンベディングが生成されます。
  3. 必要に応じて、incremental_refresh_mode => ‘None' を設定して、一括更新を行うステートメントのみを取得し、すべての行のエンベディングを更新するために ai.refresh_embeddings() を手動で呼び出すことができます。

Kafka キュー、Python ワーカー、移行スクリプトを 6 行の SQL に置き換えました。すべての属性の詳細な公式ドキュメントをご覧ください。

リアルタイム トリガーのテスト

「Zero Loop」自動化が新しいデータで機能することを確認しましょう。

  1. 新しい行を挿入する:
INSERT INTO help_articles (title, category, product_version, content_body)
VALUES ('New Scaling Guide', 'Technical', '2.0', 'How to scale AlloyDB to millions of transactions.');
  1. すぐに確認する:
SELECT embedding FROM help_articles WHERE title = 'New Scaling Guide';

結果:

外部スクリプトを実行しなくても、ベクトルがすぐに生成されます。

バッチサイズのチューニング

現在、AlloyDB のデフォルトのバッチサイズは 50 です。デフォルトはすぐに使用できますが、AlloyDB では、ユーザーが独自のモデルとデータセットに最適な構成を調整することもできます。

CALL ai.initialize_embeddings(
  model_id => 'text-embedding-005',
  table_name => 'help_articles',
  content_column => 'content_body',
  embedding_column => 'embedding',
  incremental_refresh_mode => 'transactional',
  batch_size => 20
);

ただし、パフォーマンスを制限する可能性のある割り当て上限に注意する必要があります。推奨される AlloyDB 割り当てを確認するには、ドキュメントの「始める前に」セクションをご覧ください。

注意点とトラブルシューティング

IAM 伝播のギャップ

gcloud IAM コマンドを実行しましたが、SQL CALL が権限エラーで失敗します。IAM の変更が Google バックボーンに反映されるまでに時間がかかることがあります。深呼吸しましょう。

ベクトル ディメンションの不一致

help_articles テーブルの content_body 列は VECTOR(768) に設定されています。後で別のモデル(1, 536 次元モデルなど)を使用しようとすると、挿入が爆発します。text-embedding-005 に準拠します。

6. コンテキスト検索の柔軟性

次に、ハイブリッド検索を実行します。セマンティック理解(ベクトル)とビジネス ロジック(SQL フィルタ)を組み合わせます。

次のクエリを実行して、プロダクト バージョン 2.0 の請求に関する問題を特定します。

SELECT
  title,
  left(content_body, 100) as content_snippet,
  1 - (embedding <=> embedding('text-embedding-005', 'Invoice did not go through')::vector) as relevance
FROM help_articles
WHERE category = 'Billing'  -- Hard SQL Filter
  AND product_version = '2.0' -- Hard SQL Filter
ORDER BY relevance DESC
LIMIT 5;

これが Flexing Context です。検索は、厳格なビジネス制約(バージョン 2.0)を尊重しながら、ユーザーの意図(「請求に関する問題」)を理解するように「柔軟に対応」します。

f0fdb50d6195c462.png

スタートアップと移行に最適な理由

  1. インフラストラクチャの負債ゼロ: 別のベクトル DB(Pinecone/Milvus)をスピンアップしていません。別の ETL ジョブを作成していない。すべて Postgres にあります。
  2. リアルタイム更新: 「トランザクション」モードを使用すると、検索インデックスが古くなることはありません。データがコミットされると、すぐにベクトル化できます。
  3. スケーラビリティ: AlloyDB は Google のインフラストラクチャ上に構築されています。数百万のベクトルの一括生成を、Python スクリプトよりも高速に処理できます。

注意点とトラブルシューティング

本番環境のパフォーマンスに関する注意点

問題: 50,000 行の処理が高速。カテゴリ フィルタが十分に選択的でない場合、100 万行の処理に非常に時間がかかる。解決策: ベクトル インデックスを追加する。本番環境の規模では、インデックスを作成する必要があります。CREATE INDEX ON help_articles USING hnsw (embedding vector_cosine_ops);インデックスの使用状況を確認する。EXPLAIN ANALYZE SELECT ... を実行して、データベースがインデックスを使用しており、シーケンシャル スキャンを行っていないことを確認します。

「モデルの不一致」という障害

問題: CALL プロシージャで text-embedding-005 を使用して列を初期化しました。SELECT クエリ関数 embedding('model-name', ...) で別のモデル(text-embedding-004 や OSS モデルなど)を誤って使用すると、ディメンションは一致する(768)可能性がありますが、ベクトル空間は完全に異なります。クエリはエラーなしで実行されますが、結果は完全に無関係になります(関連性のないスコア)。トラブルシューティング:ai.initialize_embeddings の model_id が SELECT クエリの model_id と完全に一致していることを確認します。

「Silent Empty」の結果(フィルタリングしすぎ)

問題: ハイブリッド検索は「AND」演算です。セマンティック一致SQL 一致が必要です。ユーザーが「Billing help」を検索しても、product_version 列に ‘2.0' ではなく ‘2.0.1' が含まれている場合、ベクトル一致が 99% であっても、結果は 0 行になります。トラブルシューティング:

  • 最初にベクトル ソートなしでクエリを実行し、SQL フィルタ(WHERE category...)が実際にデータを返しているかどうかを確認します。
  • 大文字と小文字の区別(Billingbilling)を確認します。

4. 権限/割り当てエラー(500 エラー)

問題:SELECT 句の embedding() 関数は、Vertex AI へのリアルタイム ネットワーク呼び出しを行います。データベース サービス アカウントが Vertex AI ユーザーロールを失った場合、または Vertex AI API 割り当て(QPM)に達した場合、SQL クエリ全体が失敗します。トラブルシューティング:

  • Cloud Logging で AlloyDB を確認します。
  • IAM ロールがまだ有効であることを確認します。
  • 高い復元力が必要な場合は、ストアド プロシージャの TRY/CATCH ブロックで関数をラップします。

5. Null エンベディング

問題:モデルが完全に初期化される前にデータを挿入した場合、またはバックグラウンド ワーカーが失敗した場合、一部の行の embedding 列に NULL が含まれることがあります。NULL <=> VectorNULL を返します。これらの行は並べ替え順序から消えます。トラブルシューティング:

  • SELECT count(*) FROM help_articles WHERE embedding IS NULL; を実行して、カバレッジが 100% であることを確認します。

7. クリーンアップ

このラボが完了したら、必ず AlloyDB クラスタとインスタンスを削除してください。

クラスタとそのインスタンスをクリーンアップする必要があります。

8. 完了

スケーラブルなナレッジベース検索アプリケーションを正常に構築しました。Python スクリプトとループを使用して複雑な ETL パイプラインを管理し、ベクトル エンベディングを生成する代わりに、AlloyDB AI を使用して、単一の SQL コマンドを使用してデータベース内でエンベディング生成をネイティブに処理しました。

学習した内容

  • データ処理用の「Python For-Loop」を削除しました。
  • 1 つの SQL コマンドで 50,000 個のベクトルを生成しました。
  • トリガーを使用して、今後のベクトルの生成を自動化しました。
  • ハイブリッド検索を実施しました。

次のステップ