~/tools / 嵌入與搜尋(RAG) / pgvector SQL Builder

pgvector SQL Builder — 寫出 pgvector SQL:資料表、HNSW/IVFFlat 索引與最近鄰查詢

寫出用 pgvector 在 PostgreSQL 中儲存與搜尋 embedding 的 SQL:含有 vector(n) 或 halfvec(n) 欄位的資料表、使用與你的距離相符之運算子類別的 HNSW 或 IVFFlat 索引,以及會回傳易讀分數的最近鄰查詢。它會檢查 pgvector 文件中給出的限制,並估算向量佔用的空間。資料不會離開你的瀏覽器。

每個向量佔用 1,544 位元組,所以 10,000 列光是向量就約佔 15.4 MB(不含索引與其他欄位)。

1. 擴充功能與資料表

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text NOT NULL,
  embedding vector(384) NOT NULL
);

2. 索引

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

3. 搜尋

PREPARE 會把來自 /api/embed 的問題向量當成 $1。請以文字傳入,例如 '[0.01,-0.02,…]'。

PREPARE search_documents(vector(384)) AS
  SELECT id, content, 1 - (embedding <=> $1) AS cosine_similarity
  FROM documents
  ORDER BY embedding <=> $1
  LIMIT 5;

-- EXECUTE search_documents('[0.0123, -0.0456, ...]');

限制與預設值取自 pgvector 0.8.7 的 README 與原始碼,於 2026-11-04 確認。 pgvector README

一切都在你的瀏覽器中完成。這個工具只會寫出 SQL,不會連線到任何地方。

pgvector 是為 PostgreSQL 加上向量相似度搜尋的開源擴充功能,因此你用 Ollama 產生的 embedding 可以和其他資料存放在同一個資料庫中。有幾個細節很容易出錯。欄位的維度數必須和你的 embedding 模型相同。每種距離都有自己的運算子:<=> 是餘弦距離,<-> 是 L2 距離,<#> 是內積的負值,<+> 是 L1 距離。只有當近似索引是用相同距離的運算子類別建立(例如 <=> 對應 vector_cosine_ops),而且查詢以該運算子本身升冪排序並加上 LIMIT 時,才會使用索引。這個工具用同一組選擇寫出三個部分:CREATE EXTENSION 與 CREATE TABLE、CREATE INDEX,以及一個 PREPARE 陳述式,它會回傳最接近的列並附上易讀的分數(餘弦相似度是 1 減去餘弦距離,內積則是 <#> 乘以 -1)。它套用 pgvector 0.8.7 版的 README 與原始碼中的限制:一個欄位最多 16,000 個維度,索引對 vector 最多 2,000 維、對 halfvec 最多 4,000 維,IVFFlat 沒有 L1 運算子類別,HNSW 的 m 為 2 到 100,ef_construction 為 4 到 1,000 且至少為 2 × m。使用 HNSW 時,一次搜尋最多回傳 hnsw.ef_search 列(預設為 40),因此當 LIMIT 較大時,工具會發出警告。對 IVFFlat,它會依 README 的起點填入 lists 與 probes:100 萬列以內用 rows / 1000,超過則用 sqrt(rows),probes 則用 sqrt(lists)。產生的 SQL 已用 Ollama 的真實 384 維向量,在 PostgreSQL 16 上的 pgvector 0.8.7 執行,且 EXPLAIN 顯示索引確實被使用。

這個工具的核心邏輯以 MIT 授權開放原始碼。 github.com/seigo5555/toolkitbox-lib/src/pgvector-sql.ts

使用情境

  • 在把 Ollama 的 embedding 載入 PostgreSQL 之前,先建立存放文件區塊的資料表
  • 確認餘弦、L2、內積或 L1 距離各自對應哪個運算子與運算子類別,讓索引真的被用到
  • 估算小型 VPS 上向量佔用的空間,並比較 vector 與 halfvec

使用方法

  1. 輸入資料表名稱,以及你的 embedding 模型的維度數。這是 /api/embed 回傳的單一向量長度;本站的 Embedding Similarity Calculator 會顯示它。
  2. 選擇型別、距離與索引。Ollama 的文件建議大多數語意搜尋使用餘弦相似度,而 /api/embed 回傳的向量長度為 1。
  3. 複製這三個區塊,在 psql 或其他用戶端中執行。IVFFlat 索引要在資料載入之後再建立;HNSW 索引可以建立在空的資料表上。

FAQ

我該選哪種距離?

Ollama 的 embedding 文件建議大多數語意搜尋使用餘弦相似度。在 pgvector 中,<=> 回傳餘弦距離,工具會用 1 減去該距離,轉成 cosine_similarity。對於長度為 1 的向量(例如 /api/embed 回傳的向量),餘弦距離、L2 距離與內積會把各列排成相同的順序,因為這類向量的 L2 距離平方等於 2 減去 2 倍的餘弦相似度。

HNSW 還是 IVFFlat?

pgvector 的 README 說,HNSW 的查詢效能(就速度與召回率的取捨而言)優於 IVFFlat,但建立較慢、使用較多記憶體;而且因為沒有訓練步驟,可以建立在空的資料表上。IVFFlat 建立較快、使用較少記憶體,應在資料表已有一些資料之後再建立。沒有索引時,pgvector 會進行精確搜尋,能提供完美的召回率。

為什麼我的查詢沒有使用索引?

README 說,查詢需要 ORDER BY 與 LIMIT,而且 ORDER BY 必須是以升冪排列的距離運算子本身,而不是像 1 - (embedding <=> …) DESC 這樣的運算式。運算子也必須與索引的運算子類別相符:<=> 配 vector_cosine_ops,<-> 配 vector_l2_ops,<#> 配 vector_ip_ops,<+> 配 vector_l1_ops。工具會根據同一個選擇寫出兩者。README 也提到,資料表很小時,全表掃描可能比較快。

為什麼我得到的列比 LIMIT 少?

使用 HNSW 時,結果數量受 hnsw.ef_search 限制,預設為 40;我們在一個 200 列的資料表上試了 LIMIT 100,在把 hnsw.ef_search 提高到 100 之前,只得到 40 列。README 補充,用 WHERE 篩選是在掃描索引之後才套用,所以某個條件可能讓列數變得更少。自 0.8.0 版起,迭代式索引掃描(SET hnsw.iterative_scan = strict_order 或 relaxed_order)會在需要時掃描更多索引。

halfvec 是什麼?

一種半精度的向量型別。每個向量佔用 2 × 維度數 + 8 位元組,而不是 4 × 維度數 + 8 位元組;halfvec 上的 HNSW 與 IVFFlat 索引最多支援 4,000 維,而不是 2,000 維。每個元素都是半精度浮點數,所以值的儲存精度較低。

我的資料會被傳送到別處嗎?

不會。這個工具只在你的瀏覽器中寫出 SQL,從不連線到資料庫。

相關文章

相關工具