3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「あの深夜に書いた最高の一節なんだっけ?」を二度と言わないために、己の記憶をゼロトラストしてAlloyDB ✕ pgvectorの曖昧検索要塞を作った

3
Posted at
AIベクトル読書メモ UI画面

皆さんも、よなよなスマホに自分のお気に入りのポエムをしたためて、にやにやしていますよね?ただ、あまりにも多くため過ぎたポエムの中で、きらりと光る逸品のうち、自己最高得点をつけた逸品の内容を忘れてしまうことってけっこうあるじゃないですか?お気に入りポエムの数が膨大になりすぎて、中身は覚えていないにもかかわらず、「出来が良かったあれ、どんな内容だっけ?」という事実のみ覚えている状態に陥ってしまうことありますよね?。
そのうち、内容を思い出そうと悩んでいる事実すら忘れてしまい、うとうと眠ってしまい、翌朝を迎えて、悩んでいたことすら忘れてしまうことが星の数ほどあるかと思います。
しかし、私はこの、夜も眠れないと思っていたけど意外と寝れている事実は横に置いておいて、何とか自分のお気に入りポエムを忘れないようにしたいです。
そのため、私は決断しました!
「だったら、今はやりのゼロトラストすればいいじゃん、忘れたなら忘れたで、AIに思い出させればいいんだから!!」
「生成AI統合の高性能データベース『AlloyDB』で、日記帳をつくればいいじゃない!!」 と。

AlloyDBの高性能なベクトル処理エンジン(ScaNN / pgvector統合)を活用することで、膨大な読書メモを「エモーショナルなニュアンス」で曖昧検索(セマンティック検索)し、目的の一節をスムーズに引き当てることができます。これでもう、お気に入りの一節を見失う心配はありません。

本記事は二部構成となっております。

  • 前半(ローカル検証編): Google公式のローカル版エンジン「AlloyDB Omni」と、軽量ローカルAI(Ollama)を組み合わせ、クラウド利用料ゼロ(無料)のローカルDocker環境で「AIベクトル読書メモ環境」を構築・検証します。
  • 後半(Google Cloud本番展開編): 同じアプリケーション思想を、Managed AlloyDB + Gemini Enterprise Agent Platform (旧称 Vertex AI) Embeddings + Cloud Run + Direct VPC Egress の Google Cloud 本番構成へ展開する設計指針を解説します。

💡 この記事の3行まとめ(忙しい人向け)

  1. 解決する課題: 個人用の数万件におよぶ読書メモから、自然言語の曖昧なニュアンスに基づいて目的のメモを高速かつ完全にオフラインで検索したいという課題。
  2. 採用したアーキテクチャ: AlloyDB Omni(pgvector搭載)とローカルで動作する Ollama (nomic-embed-text などのローカル埋め込みモデル) による完全オフラインのセマンティック検索システム。
  3. 実証成果: 本ハンズオンのサンプル100件ではベクトル検索の基本動作を確認。Google 独自の高速インメモリ・ベクトル・インデックス(ScaNN)の基本適用と、ローカル/本番の段階的二部構成を実証(※データ件数が少ない場合は全件スキャンが選択されるため、実行計画は EXPLAIN コマンドで確認を推奨)。

本記事の完全版(コードの折りたたみ表示やより詳細なトラブルシューティングが含まれるバージョン)は、筆者の個人技術ブログ kuis.win でも公開しています。



🏗️ アーキテクチャ概要

AlloyDB Omniによる高性能セマンティック検索の実現

膨大な「読書メモ」などのテキストデータを管理する際、従来の単純なキーワードマッチ(LIKE検索)や形態素解析による検索では、「文脈」や「意味の類似度」に基づいたセマンティック検索を実現できません。セマンティック検索にはテキストをベクトル化し、ベクトルデータベースで類似度クエリを実行する必要があります。

本記事では、Google Cloudが提供する高性能なPostgreSQL互換データベースのローカル実行版であるAlloyDB Omniと、ローカル環境で動作する軽量LLMエンジンであるOllamaを組み合わせたシステムを構築します。これにより、外部APIの課金や認証情報の管理を行うことなく、ローカル環境でミリ秒単位の類似度検索(pgvectorによるコサイン類似度クエリなど)を実証する実装方法について解説します。

システム構成図(データフロー)


💫 本構成のインフラ特徴と設計思想

  • 3種の検索アルゴリズム比較: LIKE部分一致、全文検索(pg_trgm / Bi-gram)、AIベクトル検索(pgvector/ScaNN)の3方式を横並びで同時実行し、その精度差を視覚的に検証します。
  • 完全ローカルでのベクトル埋め込み(Embeddings): 外部APIを介さず、Ollamaコンテナでローカルに768次元ベクトル(nomic-embed-text)を自動抽出(本番運用時は Gemini Enterprise Agent Platform (旧称 Vertex AI) の text-multilingual-embedding-002 等への切り替えも可能。※モデルを差し替える場合は出力次元数に合わせてテーブル側の vector(N) の定義も一致させる必要があります)。
  • Google 独自 ScaNN インデックス: 大規模データに対しても高速近傍点探索を実行するGoogle ScaNN アルゴリズムを alloydb_scann 拡張機能にて直接利用(USING scann (embedding cosine))。

💡 発展コラム:google_ml_integration 拡張機能による Zero-ETL データベース内推論
本ハンズオンコードでは初学者が理解しやすいよう「Go API → Ollama → DB」の流れを採用していますが、Google Cloud の AlloyDB 本番環境では google_ml_integration 拡張機能を有効化することで、SQL関数 google_ml.embedding() から直接 Gemini Enterprise Agent Platform (旧称 Vertex AI) モデルを即座にインデータベース呼び出しできる Zero-ETL 構成へのアップグレードが可能です。

🔄 ローカル vs Google Cloud 本番のデータ処理フロー

「AIでテキストをベクトル(数値)化して、データベースで意味検索する」という処理の流れは、ローカル環境でも Google Cloud 本番環境でも全く同一です:

💻 ローカルの場合(Ollama ✕ AlloyDB Omni)
【ユーザー】
   │ 「泣けるポエム」と入力
   ▼
【Web アプリ (Docker)】
   │
   ├─① Ollama (ローカルAI) へ「泣けるポエム」を渡す
   │  └─▶ Ollama が [0.12, -0.84, 0.55...] の「768次元の数値」に変換して返す
   │
   └─② AlloyDB Omni (ローカルDB) へ「この数値と一番近いポエムを探して!」と SQL 投げる
      └─▶ AlloyDB Omni が pgvector で高速計算し、マッチした過去のメモを返す!
☁️ Google Cloud 本番の場合(Vertex AI ✕ Managed AlloyDB)
【ユーザー】
   │ 「泣けるポエム」と入力
   ▼
【Cloud Run (Webアプリ)】
   │
   ├─① Vertex AI (GCPのAI) へ「泣けるポエム」を渡す
   │  └─▶ Vertex AI が [0.08, -0.71, 0.62...] の「768次元の数値」に変換して返す
   │
   └─② Managed AlloyDB (GCPのDB) へ「この数値と一番近いポエムを探して!」と SQL 投げる
      └─▶ AlloyDB が ScaNN インデックスで高速計算し、マッチした過去のメモを返す!

データベース選定とアーキテクチャの比較検証

AIベクトル検索(セマンティック検索)を安全かつ高速に稼働させるための、主なベクトルストア手法の適合度を以下に示します。

評価軸 PostgreSQL (pgvector HNSW) Cloud SQL (PostgreSQL) AlloyDB Omni (ScaNNインデックス)
基本データモデル リレーショナル(一般PostgreSQLエンジン) フルマネージドRDB(外部独立インスタンス) 高速PostgreSQL互換(インメモリ最適化)
大規模ベクトル検索 中(データ量増大に伴い遅延増加) 中(標準pgvector HNSWによるインデックス) 高(Google ScaNNによる大規模近似最近傍探索)
スケーラビリティ 低(単一マシンのメモリ/CPU性能に依存) 中(インスタンス性能に応じた垂直スケール) 高(ハイブリッドカラムナエンジンで分析処理を分離)
ランニングコスト 中(VMの定額月額料金:約1,500円〜) 高(データベース常時起動の定額料金:約2,000円〜) 極小(ローカル開発環境ならクラウド費用0円)
安全対策 / 運用負荷 低(OSアップデートやDB障害復旧の手動保守) 高(Googleによるフルマネージドバックアップ) 高(Google Cloud AlloyDBへの互換デプロイパス)

標準のPostgreSQLやCloud SQLでもpgvectorを使用できますが、データ数が数十万〜数百万件に達した際、クエリの検索性能が低下します。また、常時起動のデータベースサービスは固定のインフラコストが負担になります。
したがって、ローカル環境で無償検証でき、本番 Managed AlloyDB へのシームレスな移行パスを持つ 「AlloyDB Omni」 は、ベクトル検索のプロトタイピングにおいて有力な選択肢の一つとなります。

⚠️ 補足:ライセンス区分と埋め込みモデルの互換性について

  • ライセンスについて: AlloyDB Omni Developer Edition は、開発・テスト・評価・デモ目的で無償利用できます。商用プロダクションデータ処理等にはご利用いただけませんので、本番運用時は Managed AlloyDB をご検討ください。
  • 埋め込みモデルの互換性: ベクトルはモデル固有のベクトル空間に属します。ローカル(Ollama / nomic-embed-text)で生成・保存したベクトルと、本番(Gemini Enterprise Agent Platform (旧称 Vertex AI) / text-multilingual-embedding-002)の検索ベクトルを直接混在させることはできません。本番へ移行する際は、本番モデルですべて再ベクトル化を行う必要があります。

📂 すべての設定ファイルと完全なコードはこちらのGitHubリポジトリで公開しています

👉 github.com/kuiswin/174-alloydb-reading
(※本リポジトリのソースコードおよびスクリプトは個人・学習・検証目的でご活用いただけます。商用本番環境での利用は自己責任でお願いします。)

※本リポジトリのコードおよび記事のロジック構築には生成AIを活用しています。動作確認は行っておりますが、AI特有の誤ったコード生成(ハルシネーション)や仕様変更、意図しない不具合等が含まれる可能性があります。本番環境へ適用される場合は、セキュリティやクラウド費用(FinOps)をご自身でご確認の上、自己責任にてご運用ください。



💻 ローカル環境での検証 & 稼働手順

ローカルPCの環境に、以下の構成ファイルを配置して起動確認を行います。
検証に必要なソースコードはすべてGitHub上に公開されています。

  • 📄 docker-compose.yml (AlloyDB Omni・Ollama・Go APIのサービス連携定義)
  • 📄 Dockerfile (Go APIバックエンドコンテナのビルド定義)
  • 📄 go.mod (Goモジュール定義)
  • 📄 main.go (Ollama API連携、pgvector / pg_bigm 比較検索クエリ、文学データ自動作成機能)
  • 📄 index.html (3大検索方式を並行実行し可視化するWeb UI)
  • 📄 style.css (グラスモーフィズムデザイン装飾CSS)

📁 フォルダ構成

sandbox_174/
├── docker-compose.yml
├── Dockerfile
├── go.mod
├── main.go
└── public/
    ├── index.html
    └── style.css

ローカルでの動作確認について

本システムでは、Google Cloudの実機環境にデプロイする前に、マネージドAlloyDBと共通するPostgreSQL互換のスキーマ、SQL、ベクトル型の基本動作をローカル環境で事前に検証できるよう、Docker Composeを用いたコンテナ(AlloyDB OmniやOllamaなど)を組み合わせて検証を行います。これにより、クラウド料金を発生させることなく、Go API経由でのAlloyDB接続、書籍読書メモの保存、AIによるベクトルセマンティック検索機能、およびScaNNインデックスの基本動作などの主要機能を安全にテストできます。

GitHub上に公開されているソースコードを一つずつコピーしてフォルダに配置し、Docker環境を手動で立ち上げることで動作確認自体は可能ですが、ファイル構成の作成や依存関係のインストール、環境変数のマッピング設定を手動で行うのは少々手間(不便)がかかります。

より簡単かつスピーディーにローカルでの動作確認を行いたい方は、記事の後半(個人ブログパート)で紹介しているセットアップ手順をご参照ください。



開発環境へのアクセス情報

コンテナが起動したら、ブラウザから以下のURLへアクセスします。

  • ローカル環境(ご自身のPCなど)で動かす場合:
    • ベクトル読書メモ検索 UI: http://localhost/
    • ※ブラウザでアクセスすると、3大検索方式を横並びで比較・実行できるダッシュボード画面が表示されます。まず「サンプル100件を登録」をクリックしてデータを初期投入します。


  • 【検証用】筆者の自動公開デモ環境:
    • 筆者のプライベート環境では、コンテナを立ち上げるだけで自動的にドメインとSSLが割り当てられる検証環境を用意しています。以下のURLから動作検証が可能です。
    • ベクトル読書メモ検索 UI: https://book-p80-174.kuis.win/
AIベクトル読書メモ ポータル画面

📦 コンテナ内部の構成

今回の構成(Docker環境)内では、以下の5つのDockerコンテナが協調して動作しています。

  1. alloydb-db 【公式イメージ / 無改造】:
    • イメージ: google/alloydbomni:15
    • 役割: Google公式の AlloyDB Omni データベース本体。
  2. alloydb-init 【初期化用 (transient) / 完了後自動終了】:
    • イメージ: postgres:15-alpine
    • 役割: AlloyDB の起動を検知した後に pgvector 拡張 (vector)、ScaNNベクトルインデックス拡張 (alloydb_scann)、および pg_bigm 拡張を有効化し、reading_memos テーブルと各種インデックスを全自動構築します。
  3. ollama 【公式イメージ / 無改造】:
    • イメージ: ollama/ollama:latest
    • 役割: 完全ローカルの Ollama Embeddings エンジン本体。
  4. ollama-init 【初期化用 (transient) / 完了後自動終了】:
    • イメージ: curlimages/curl:latest
    • 役割: ローカルAIモデルのセットアップコンテナ。ollama 起動を待ってから nomic-embed-text モデルをプルします。
  5. alloydb-go-api 【独自開発 / アプリ・Web UI本体】:
    • イメージ: Dockerfile よりローカルビルド
    • 役割: Go言語で書かれたバックエンドアプリケーション。フロントの UI 配信(ポート 80)と、メモのベクトル登録および3方式の類似度検索APIの実行を担当します。

📂 ローカル検証環境の一括セットアップ

ここまででまだ手元でローカル検証環境を動作させていない方は、以下のコマンドを実行することで、依存ツールのインストールから検証に必要な設定ファイル群のGit取得、Docker起動まで一括でローカルに構築できます!

# 依存ツールのインストール (未導入の場合)
sudo apt-get update && sudo apt-get install -y docker.io docker-compose-v2 git

# リポジトリのクローンと作業ディレクトリへの移動
git clone https://github.com/kuiswin/174-alloydb-reading.git sandbox_174
cd sandbox_174

# コンテナのビルドとバックグラウンド起動
docker compose up -d --build
Docker Compose イメージダウンロード・起動ターミナル画面

☕ 補足:初回起動時はコーヒーでも飲んで気長にお待ちください(笑)
ワンライナーコマンドを実行すると、AlloyDB Omni データベース本体(約1.2GB)や Ollama ローカルAIエンジン(約2.5GB)など、合計4GB超の巨大コンテナイメージが一斉に自動ダウンロード&ビルドされます!
ネットワーク回線速度やPCスペックによっては少々時間がかかります(上のターミナル画面のようにプログレスバーがズラリと進みます)。あせらず温かいコーヒーでも飲みながら、環境が整うのを気長にお待ちください☕

コンテナの起動完了後、ブラウザから http://localhost/ (または検証用ドメイン https://book-p80-174.kuis.win/)を開くと、以下のように AlloyDB Omni 駆動の「AIベクトル読書メモ」のポータル画面が立ち上がります:

AIベクトル読書メモ ポータル画面

🎯 ワンクリックで体感!3大検索エンジンの精度差を比較テスト

画面右上の 「サンプル100件を登録」 をクリックして読書メモデータを投入したら、検索窓の下にあるおすすめタグ(サジェストボタン)をポチッと押して、3つの検索アルゴリズムの挙動を比べてみましょう:

  • 🏃 「友を信じて走り抜く強い絆」
  • 🌟 「目に見えない本当に大切なもの」
  • 🔥 「成果を出す起業家の純粋な情熱」

例えば 「成果を出す起業家の純粋な情熱」 をクリックすると、以下のように3つの検索方式が同時に走り、その違いが一目瞭然で分かります:

3大検索エンジンの比較結果画面
  • LIKE検索(単純パターン): 「起業家」「情熱」という単語がそのまま含まれる文章はないため 該当なし(0件)
  • ⚠️ 部分一致全文検索(FTS TRI-GRAM): 「純粋」などの文字の重なり部分に引っ張られ、文脈と無関係なメモが低スコアでヒット。
  • 🧠 AIベクトル検索(PGVECTOR): 文章全体の「意味・文脈・情熱」を捉え、意味類似度 60%超で的確に抽出

このように、単語を正確に思い出せなくても「ニュアンスや情景」だけで過去のメモが引き当てられる セマンティック検索の有用性 をローカル環境で手軽に体感できます!

💻 ターミナルから CLI(curl & SQL)で直接検証してみる

ブラウザだけでなく、ターミナルから直接 API や AlloyDB の生データを叩いて検証することも可能です:

1. curl コマンドで 3大検索 API を叩いて JSON 出力を確認
# 作業ディレクトリへの移動(未移動時のみ自動移動)
[ -d "sandbox_174" ] && cd sandbox_174

# 「成果を出す起業家の純粋な情熱」で3大検索APIを一括実行
curl -s "http://localhost/api/search?q=成果を出す起業家の純粋な情熱" | jq .

【実行結果例(JSON)】

{
  "like": [],
  "fts": [
    {
      "id": "6cfd712c...",
      "title": "中島敦 『悟浄歎異』",
      "content": "孫悟空の底知れない強さは、自らを疑わないその純粋さにある。",
      "score": 0.07
    }
  ],
  "vector": [
    {
      "id": "0078bfab...",
      "title": "江戸川乱歩 『心理試験』",
      "content": "犯人は緻密な計画を立てて心理試験に臨んだが、明智小五郎の洞察力には勝てなかった。",
      "score": 0.64
    },
    {
      "id": "39ac0052...",
      "title": "中島敦 『山月記』",
      "content": "その時、藪の中から一匹の獰猛な虎が躍り出た。",
      "score": 0.63
    }
  ],
  "like_ms": 4.0,
  "fts_ms": 2.4,
  "vector_ms": 187.0
}
2. AlloyDB Omni の pgvector テーブルを生 SQL で直接覗く
# AlloyDB Omni コンテナに psql で接続し、直近登録された読書メモと768次元ベクトルを確認
docker compose exec alloydb-db psql -U postgres -d reading_db -c \
  "SELECT title, left(embedding::text, 40) || '...' AS vector_preview, memo FROM reading_memos LIMIT 3;"

【出力結果例】

       title        |              vector_preview              |                        memo                        
--------------------+------------------------------------------+----------------------------------------------------
 夏目漱石 『こころ』 | [-0.012451172,0.034820557,-0.008911133...] | 精神的に向上心のないものは馬鹿だ。
 夏目漱石 『こころ』 | [-0.045196533,0.021057129,-0.015625000...] | 私は冷淡な人間です。しかし温かい人間になるように...
 太宰治 『人間失格』 | [-0.028900146,0.041259766,-0.003417969...] | 恥の多い生涯を送って来ました。
(3 rows)

🚀 次のステップ:本番(クラウド)展開へ

ローカル環境でのAIベクトル検索動作テストが完璧に成功したら、次はいよいよ本番環境へのデプロイメントです!

本番環境(Google Cloud)に持っていくためには、アクセス急増時にクラウド利用料金が跳ね上がるのを防ぐためのセキュリティ・コスト制御が不可欠です。

本番展開の設計思想や、一発自動デプロイスクリプトの実装方法については、以下の個人技術ブログで詳細に公開しています!

👉 【後編:Google Cloud本番デプロイ編】本番一発自動デプロイとFinOpsコスト防衛手順はこちら(ブログ記事リンク)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?