Supabase Vectorとは
Supabase Vectorは、PostgreSQLの拡張機能pgvectorを活用したベクトル検索機能です。AI/MLアプリケーションで必要なセマンティック検索やRAG(検索拡張生成)を、既存のPostgreSQLデータベースで実現できます。
セットアップ
pgvectorの有効化
-- pgvector拡張を有効化
CREATE EXTENSION IF NOT EXISTS vector;
-- ベクトルカラムを持つテーブルを作成
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(1536), -- OpenAI ada-002の次元数
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
インデックスの作成
-- IVFFlat インデックス(高速、近似検索)
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- HNSW インデックス(より高精度)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
ベクトルの生成と保存
OpenAI Embeddingsを使用
import { createClient } from '@supabase/supabase-js';
import OpenAI from 'openai';
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const openai = new OpenAI();
async function addDocument(content: string, metadata: object) {
// Embeddingを生成
const embeddingResponse = await openai.embeddings.create({
model: 'text-embedding-ada-002',
input: content
});
const embedding = embeddingResponse.data[0].embedding;
// Supabaseに保存
const { error } = await supabase
.from('documents')
.insert({
content,
embedding,
metadata
});
if (error) throw error;
}
ベクトル検索
類似度検索
async function searchSimilar(query: string, limit = 5) {
// クエリのEmbeddingを生成
const embeddingResponse = await openai.embeddings.create({
model: 'text-embedding-ada-002',
input: query
});
const queryEmbedding = embeddingResponse.data[0].embedding;
// ベクトル検索
const { data, error } = await supabase.rpc('match_documents', {
query_embedding: queryEmbedding,
match_threshold: 0.7,
match_count: limit
});
return data;
}
検索用SQL関数
CREATE OR REPLACE FUNCTION match_documents(
query_embedding VECTOR(1536),
match_threshold FLOAT,
match_count INT
)
RETURNS TABLE (
id BIGINT,
content TEXT,
metadata JSONB,
similarity FLOAT
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT
documents.id,
documents.content,
documents.metadata,
1 - (documents.embedding <=> query_embedding) AS similarity
FROM documents
WHERE 1 - (documents.embedding <=> query_embedding) > match_threshold
ORDER BY documents.embedding <=> query_embedding
LIMIT match_count;
END;
$$;
RAGアプリケーションの構築
async function askQuestion(question: string) {
// 1. 関連ドキュメントを検索
const relevantDocs = await searchSimilar(question, 3);
// 2. コンテキストを構築
const context = relevantDocs
.map(doc => doc.content)
.join('\n\n');
// 3. LLMで回答を生成
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: `以下のコンテキストを使って質問に回答してください。
コンテキストに情報がない場合は「わかりません」と答えてください。
コンテキスト:
${context}`
},
{ role: 'user', content: question }
]
});
return {
answer: response.choices[0].message.content,
sources: relevantDocs
};
}
ハイブリッド検索
ベクトル検索とフルテキスト検索を組み合わせます。
CREATE OR REPLACE FUNCTION hybrid_search(
query_text TEXT,
query_embedding VECTOR(1536),
match_count INT
)
RETURNS TABLE (
id BIGINT,
content TEXT,
similarity FLOAT
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT
d.id,
d.content,
(
0.5 * (1 - (d.embedding <=> query_embedding)) +
0.5 * ts_rank(to_tsvector('japanese', d.content), plainto_tsquery('japanese', query_text))
) AS similarity
FROM documents d
WHERE to_tsvector('japanese', d.content) @@ plainto_tsquery('japanese', query_text)
ORDER BY similarity DESC
LIMIT match_count;
END;
$$;
Edge Functionsとの統合
// supabase/functions/embed-and-search/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
serve(async (req) => {
const { query } = await req.json();
// OpenAI APIでEmbedding生成
const embeddingRes = await fetch('https://api.openai.com/v1/embeddings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'text-embedding-ada-002',
input: query
})
});
const { data } = await embeddingRes.json();
const embedding = data[0].embedding;
// Supabaseで検索
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
const { data: results } = await supabase.rpc('match_documents', {
query_embedding: embedding,
match_threshold: 0.7,
match_count: 5
});
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
});
});
パフォーマンスのヒント
| 設定 | 推奨値 |
|---|---|
| IVFFlat lists | sqrt(行数) |
| HNSW m | 16-32 |
| HNSW ef_construction | 64-128 |
まとめ
Supabase Vectorは、PostgreSQLの強みを活かしながらベクトル検索を実現します。既存のデータベースにAI機能を追加でき、RAGアプリケーションの構築が容易になります。
← 一覧に戻る