AI Engineering Academy · レッスン

pgvector:PostgreSQLでEmbeddingを扱う

PostgreSQLでpgvector拡張を有効にし、vector列を持つテーブルを作成してembeddingを挿入し、 cosine distance演算子を使って最近傍検索を実行します。

レッスン 3/413 ステップ

「pgvector:PostgreSQLでEmbeddingを扱う」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。

pgvector:PostgreSQLでのベクトル検索

pgvectorは、vectorデータ型と類似度検索演算子を追加するオープンソースのPostgreSQL拡張です。すでに運用しているデータベース内で、通常のアプリケーションデータと埋め込みを同じ場所に保存できるため、別のベクトルストアが不要になります。

すでにPostgreSQLを使用していて、新しいインフラ依存関係を導入せずに意味検索を追加したいチームに最適です。

pgvector拡張の有効化

公式リポジトリから pgvector をインストールするか、あらかじめインストールされているマネージド PostgreSQL プロバイダー(Supabase、Neon、AWS RDS、Render)を利用します。その後、単一の SQL コマンドでデータベース上で有効化できます。再起動は必要ありません。

-- Run this once per database to enable the extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Verify it's installed
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'vector';
-- Returns: vector | 0.7.0 (or similar)

ベクトル列を持つテーブルの作成

vector(n)を指定してベクトル列を追加します。n は埋め込みの次元数です。メタデータ用に通常の PostgreSQL 列と組み合わせることもできます。これにより、標準 SQL の WHERE 句を使って user_id、created_at、その他のフィールドでフィルタリングできます。

CREATE TABLE documents (
    id          BIGSERIAL PRIMARY KEY,
    content     TEXT NOT NULL,
    category    TEXT,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    embedding   vector(1536)   -- must match your model dimension
);

-- Index on category for fast metadata filtering
CREATE INDEX ON documents (category);

SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'documents';

Python からの埋め込みの挿入

psycopg2またはasyncpgドライバーを使用して、埋め込みを含む行を挿入します。Python の浮動小数点数リストを、pgvector が正しく解析できる'[0.1, 0.2, ...]'形式の文字列に変換します。Python パッケージのpgvectorには、これを自動的に処理するregister_vectorヘルパーが用意されています。

import psycopg2
from pgvector.psycopg2 import register_vector
from openai import OpenAI

conn = psycopg2.connect('postgresql://user:pass@localhost/mydb')
register_vector(conn)
cur = conn.cursor()

client = OpenAI()

document = 'pgvector adds vector search to PostgreSQL.'
resp = client.embeddings.create(model='text-embedding-3-small', input=document)
embedding = resp.data[0].embedding

cur.execute(
    'INSERT INTO documents (content, category, embedding) VALUES (%s, %s, %s)',
    (document, 'database', embedding)
)
conn.commit()
print('Inserted document with embedding')

<=>演算子によるコサイン類似度

pgvector には、3 種類の距離演算子があります。

  • <=> — コサイン距離(1 - cosine_similarity)
  • <-> — ユークリッド距離(L2 距離)
  • <#> — 負の内積(ドット積による類似度に使用)

テキスト埋め込みには<=>(コサイン距離)を使用します。ORDER BY distance ASC を指定すると、最も類似したドキュメントから順に返されます。

-- Find top 5 documents most similar to a query embedding
-- Replace '[0.1, 0.2, ...]' with the actual query vector
SELECT
    id,
    content,
    category,
    1 - (embedding <=> '[0.1, 0.2, 0.3]'::vector) AS cosine_similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, 0.3]'::vector
LIMIT 5;

ベクトル検索と SQL フィルターの組み合わせ

pgvector の大きな利点の一つは、ANN 検索と標準 SQL の WHERE 句を組み合わせられることです。特別な構文を使わずにメタデータによるフィルタリングをネイティブに実行できます。通常どおり SQL を記述するだけです。

-- Semantic search filtered to the 'database' category
-- and only recent documents
SELECT
    id,
    content,
    1 - (embedding <=> %s::vector) AS similarity
FROM documents
WHERE
    category = 'database'
    AND created_at > NOW() - INTERVAL '30 days'
ORDER BY embedding <=> %s::vector
LIMIT 5;

-- In Python with psycopg2:
# cur.execute(sql, (query_embedding, query_embedding))

HNSW インデックスによる高速化

インデックスがない場合、pgvector はすべての行を正確に走査します。10,000 行未満であれば問題ありませんが、規模が大きくなると遅くなります。近似最近傍検索にはHNSW インデックスを作成します。m(ノードあたりの接続数)とef_construction(構築時の探索幅)パラメーターでは、インデックスのサイズと構築時間を増減させることで、再現率とのバランスを調整します。

-- Create an HNSW index for cosine distance queries
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- At query time, control recall vs speed:
SET hnsw.ef_search = 40;  -- higher = better recall, slower

-- Verify the index is being used:
EXPLAIN SELECT * FROM documents
ORDER BY embedding <=> '[0.1]'::vector(1)
LIMIT 5;

IVFFlat インデックス:HNSW の代替

IVFFlatインデックスタイプは、ベクトルをlists個のバケットにクラスタリングし、クエリ時には最も近いprobes個のバケットだけを検索します。HNSW より高速に構築でき、使用メモリも少なくて済みますが、同じ速度では再現率がやや低くなります。

目安として、頻繁な再インデックスでより高速なインデックス構築が必要なら IVFFlat を使用します。安定したコーパスでクエリ速度を最大化したい場合は HNSW を使用します。

-- IVFFlat index: good for large, infrequently updated corpora
-- lists ≈ sqrt(n) where n is the number of vectors
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

-- At query time, probes controls recall vs speed:
SET ivfflat.probes = 10;  -- check 10 of 100 clusters

Python によるクエリの完全な例

ユーザーのクエリを受け取り、埋め込みを生成し、メタデータフィルタリング付きの pgvector 類似度検索を実行して、上位 k 件の結果を辞書のリストとして返す、完全な Python 関数を示します。

import psycopg2
from pgvector.psycopg2 import register_vector
from openai import OpenAI

conn = psycopg2.connect('postgresql://user:pass@localhost/mydb')
register_vector(conn)
client = OpenAI()

def semantic_search(query, category=None, top_k=5):
    resp = client.embeddings.create(model='text-embedding-3-small', input=query)
    q_vec = resp.data[0].embedding

    sql = '''
        SELECT content,
               1 - (embedding <=> %s::vector) AS similarity
        FROM documents
        {where}
        ORDER BY embedding <=> %s::vector
        LIMIT %s
    '''
    where = 'WHERE category = %s' if category else ''
    params = [q_vec, q_vec, top_k] if not category else [q_vec, category, q_vec, top_k]
    cur = conn.cursor()
    cur.execute(sql.format(where=where), params)
    return [{'text': r[0], 'score': r[1]} for r in cur.fetchall()]

pgvector と Pinecone のトレードオフ

pgvector と Pinecone を比較します。

  • pgvector:セルフホスト型で追加コストがなく、SQL と完全に統合できます。約 500 万ベクトルまで適切にスケールしますが、チューニングには DBA の知識が必要です
  • Pinecone:完全マネージド型で、数十億ベクトル規模に対応し、専用のフィルタリング機能を備えています。ただし、より高価で、運用するサービスが別に必要です

すでに PostgreSQL を運用しており、コーパスが数百万ドキュメント未満なら pgvector を選びます。大規模なスケールが必要な場合や、コストを払って運用負担を避けたい場合は Pinecone を選びます。

Supabase で pgvector を使用する

Supabaseは、pgvector があらかじめインストールされたホスト型 PostgreSQL サービスです。Python クライアント(supabase-py)とベクトルクエリ用の REST API を提供しているため、自分でデータベースサーバーを管理せずに、本番用 pgvector 環境を最も簡単に構築できます。

from supabase import create_client
import os

url = os.environ['SUPABASE_URL']
key = os.environ['SUPABASE_SERVICE_KEY']
supabase = create_client(url, key)

# Insert a document with embedding
supabase.table('documents').insert({
    'content': 'Supabase hosts PostgreSQL with pgvector.',
    'embedding': embedding   # list of 1536 floats
}).execute()

# Vector similarity search via RPC (Supabase edge function)
# result = supabase.rpc('match_documents', {'query_embedding': q_vec, 'match_count': 5}).execute()

理解度チェック

このレッスンで学んだ AI Engineering の概念について、理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、pgvector によって PostgreSQL にベクトル型の列とコサイン距離・ユークリッド距離の演算子を追加できること、HNSW インデックスによって大規模環境でも高速な近似最近傍検索を実現できること、そして標準 SQL の WHERE 句によって特別な構文なしでメタデータをフィルタリングできることを学びました。次は、主要なベクトルストアの選択肢を比較し、プロジェクトに適したものを選べるようにします。

無料で開始

AI チューターと学ぶ Python — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
30
レッスン
120

よくある質問

「pgvector:PostgreSQLでEmbeddingを扱う」レッスンは無料ですか?

はい。「pgvector:PostgreSQLでEmbeddingを扱う」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。

「pgvector:PostgreSQLでEmbeddingを扱う」で何を学びますか?

PostgreSQLでpgvector拡張を有効にし、vector列を持つテーブルを作成してembeddingを挿入し、 cosine distance演算子を使って最近傍検索を実行します。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Engineering Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「pgvector:PostgreSQLでEmbeddingを扱う」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Engineering Academyレッスンでコードを書いて実行できますか?

はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Vector Databaseが必要な理由
  2. Pineconeを始める
  3. pgvector:PostgreSQLでEmbeddingを扱う
  4. Vector Storeの選定とベンチマーク
← AI Engineering Academyに戻る