Supabase + pgvector 向量資料庫配置與 RAG 系統完整教學
Supabase 結合 pgvector 擴充功能,讓您可以直接在 PostgreSQL 中儲存與搜尋向量資料,實現高效能的 RAG(檢索增強生成)系統。本文將逐步帶您完成向量資料庫配置,並展示如何運用語意搜尋提升 AI 應用的精準度。
什麼是 pgvector 與為何選擇 Supabase
pgvector 是 PostgreSQL 的向量搜尋擴充功能,支援向量嵌入(embedding)的儲存、索引建立與相似性搜尋。Supabase 作為開源的 Firebase 替代方案,內建 PostgreSQL 完整功能,可直接啟用 pgvector 擴充,無需額外部署向量資料庫。
使用 Supabase 的優勢包括:統一的資料庫架構、RESTful API 自動生成、Row Level Security 安全性控制,以及與 Edge Functions 的無縫整合。這讓 RAG 系統的開發流程更加簡潔。
步驟一:啟用 pgvector 擴充功能
在 Supabase 控制台或透過 SQL 編輯器執行以下指令啟用 pgvector:
-- 建立 pgvector 擴充功能
CREATE EXTENSION IF NOT EXISTS vector;
-- 驗證版本
SELECT extversion FROM pg_extension WHERE extname = 'vector';
接著建立儲存向量資料的資料表,並指定向量維度(這裡以 OpenAI text-embedding-3-small 的 1536 維為例):
-- 建立文件資料表
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(1536),
metadata JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 建立 HNSW 索引以加速向量搜尋
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
步驟二:產生向量嵌入並儲存
您需要將文字內容轉換為向量嵌入。以下是使用 OpenAI API 產生嵌入的範例程式碼:
import { createClient } from '@supabase/supabase-js';
import OpenAI from 'openai';
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_ANON_KEY
);
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 將文字轉為向量嵌入
async function getEmbedding(text) {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text
});
return response.data[0].embedding;
}
// 儲存文件與向量
async function storeDocument(content, metadata = {}) {
const embedding = await getEmbedding(content);
const { data, error } = await supabase
.from('documents')
.insert({
content,
embedding,
metadata
})
.select();
if (error) throw error;
return data;
}
// 範例:儲存技術文件
await storeDocument(
'Supabase 是開源的 Firebase 替代方案,支援 PostgreSQL 資料庫',
{ source: 'tech-docs', category: 'database' }
);
步驟三:實現語意相似性搜尋
向量搜尋的核心是找出與查詢最相似的文件。pgvector 提供三種距離運算方式:歐氏距離(<#>)、內積(<#>)、餘弦相似度(<=>)。以下範例使用餘弦相似度:
-- 語意搜尋函數
async function semanticSearch(query, topK = 5) {
const queryEmbedding = await getEmbedding(query);
const { data, error } = await supabase
.from('documents')
.select('id, content, metadata')
.select(
'embedding',
{ count: 'exact', head: false }
)
.order(
'embedding',
{ ascending: false, queryEmbedding: queryEmbedding }
)
.limit(topK);
if (error) throw error;
return data;
}
// 執行搜尋
const results = await semanticSearch('什麼是開源的資料庫解決方案?');
Supabase 也支援直接使用 SQL 進行向量搜尋:
-- 直接 SQL 搜尋
SELECT id, content,
(embedding <=> '[查詢向量]'::vector) AS similarity
FROM documents
ORDER BY similarity ASC
LIMIT 5;
步驟四:整合 RAG 系統實作
完整的 RAG 流程是:接收使用者問題 → 向量搜尋相關文件 → 將文件內容作為上下文回饋給 LLM。以下是完整實作:
async function ragQuery(userQuestion) {
// 1. 取得問題的向量表示
const questionEmbedding = await getEmbedding(userQuestion);
// 2. 向量搜尋取得相關文件
const { data: documents } = await supabase.rpc(
'match_documents',
{
query_embedding: questionEmbedding,
match_threshold: 0.7,
match_count: 3
}
);
// 3. 組合上下文
const context = documents
.map(doc => doc.content)
.join('\n\n');
// 4. 呼叫 LLM 生成回答
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: '請根據以下參考資料回答問題。參考資料:\n' + context
},
{
role: 'user',
content: userQuestion
}
]
});
return response.choices[0].message.content;
}
// 使用 RAG 系統
const answer = await ragQuery('Supabase 有哪些主要功能?');
console.log(answer);
其中 match_documents 是透過 Supabase Edge Function 或 PostgreSQL 函數建立的向量搜尋包裝函數,可參考 Supabase 官方提供的 pgvector 教學進行設定。
總結
透過 Supabase 與 pgvector 的整合,您可以在單一資料庫中同時管理結構化資料與向量資料,大幅簡化 RAG 系統的架構。關鍵步驟包含:啟用 pgvector 擴充、建立向量欄位與索引、產生文字嵌入、實作語意搜尋,最後將搜尋結果傳入 LLM 生成回答。此架構具有高度擴展性,適合各種 AI 應用場景。