~/tools / Embedding và tìm kiếm (RAG) / pgvector SQL Builder

pgvector SQL Builder — Viết SQL pgvector: bảng, chỉ mục HNSW/IVFFlat và truy vấn láng giềng gần nhất

Viết SQL để lưu và tìm kiếm embedding trong PostgreSQL bằng pgvector: một bảng có cột vector(n) hoặc halfvec(n), một chỉ mục HNSW hoặc IVFFlat với lớp toán tử khớp với khoảng cách của bạn, và một truy vấn láng giềng gần nhất với điểm số dễ đọc. Công cụ kiểm tra các giới hạn nêu trong tài liệu pgvector và ước tính dung lượng các vector chiếm. Không có gì rời khỏi trình duyệt của bạn.

Mỗi vector chiếm 1,544 byte, nên 10,000 hàng chiếm khoảng 15.4 MB chỉ riêng phần vector (chưa tính chỉ mục và các cột khác).

1. Phần mở rộng và bảng

CREATE EXTENSION IF NOT EXISTS vector;

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

2. Chỉ mục

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

3. Tìm kiếm

PREPARE nhận vector của câu hỏi từ /api/embed làm $1. Hãy truyền nó dưới dạng văn bản, chẳng hạn '[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, ...]');

Các giới hạn và giá trị mặc định lấy từ README và mã nguồn của pgvector 0.8.7, kiểm tra vào ngày 2026-11-04. pgvector README

Mọi thứ diễn ra trong trình duyệt của bạn. Công cụ chỉ viết SQL và không kết nối tới đâu cả.

pgvector là một phần mở rộng mã nguồn mở thêm khả năng tìm kiếm tương đồng vector vào PostgreSQL, nên các embedding bạn tạo bằng Ollama có thể được lưu cùng cơ sở dữ liệu với phần còn lại của dữ liệu. Có vài chi tiết dễ làm sai. Cột phải có cùng số chiều với mô hình embedding của bạn. Mỗi khoảng cách có toán tử riêng: <=> cho khoảng cách cosine, <-> cho khoảng cách L2, <#> cho tích vô hướng âm và <+> cho khoảng cách L1. Một chỉ mục xấp xỉ chỉ được dùng khi nó được xây bằng lớp toán tử của cùng khoảng cách đó, chẳng hạn vector_cosine_ops cho <=>, và khi truy vấn sắp xếp theo chính toán tử đó theo thứ tự tăng dần kèm LIMIT. Công cụ này viết ba phần từ cùng một bộ lựa chọn: CREATE EXTENSION và CREATE TABLE, CREATE INDEX, và một câu lệnh PREPARE trả về các hàng gần nhất với điểm số dễ đọc (độ tương đồng cosine là 1 trừ khoảng cách cosine, và tích vô hướng là <#> nhân với -1). Nó áp dụng các giới hạn trong README và mã nguồn của pgvector phiên bản 0.8.7: tối đa 16,000 chiều trong một cột, chỉ mục tối đa 2,000 chiều với vector và 4,000 với halfvec, không có lớp toán tử L1 cho IVFFlat, m của HNSW từ 2 đến 100, và ef_construction từ 4 đến 1,000 và ít nhất là 2 × m. Với HNSW, một lần tìm kiếm trả về tối đa hnsw.ef_search hàng (mặc định 40), nên công cụ cảnh báo khi LIMIT lớn hơn. Với IVFFlat, nó điền lists và probes từ các điểm khởi đầu của README: rows / 1000 cho đến 1M hàng, sqrt(rows) khi nhiều hơn, và sqrt(lists) cho probes. SQL được tạo ra đã được chạy trên pgvector 0.8.7 với PostgreSQL 16 bằng các vector 384 chiều thật từ Ollama, và EXPLAIN cho thấy chỉ mục được sử dụng.

Phần xử lý chính của công cụ này là mã nguồn mở theo giấy phép MIT. github.com/seigo5555/toolkitbox-lib/src/pgvector-sql.ts

Trường hợp dùng

  • Tạo bảng cho các chunk tài liệu của bạn trước khi nạp embedding từ Ollama vào PostgreSQL
  • Kiểm tra toán tử nào đi với lớp toán tử nào cho khoảng cách cosine, L2, tích vô hướng hay L1, để chỉ mục thật sự được dùng
  • Ước tính dung lượng các vector chiếm trên một VPS nhỏ, và so sánh vector với halfvec

Cách dùng

  1. Nhập tên bảng và số chiều của mô hình embedding. Đây là độ dài của một vector từ /api/embed; Embedding Similarity Calculator trên trang này sẽ hiển thị nó.
  2. Chọn kiểu, khoảng cách và chỉ mục. Tài liệu của Ollama khuyên dùng độ tương đồng cosine cho hầu hết tìm kiếm ngữ nghĩa, và /api/embed trả về các vector có độ dài 1.
  3. Sao chép ba khối và chạy chúng trong psql hoặc một ứng dụng khách khác. Hãy tạo chỉ mục IVFFlat sau khi dữ liệu đã được nạp; chỉ mục HNSW có thể được tạo trên bảng trống.

Hỏi đáp

Tôi nên chọn khoảng cách nào?

Tài liệu embedding của Ollama khuyên dùng độ tương đồng cosine cho hầu hết các trường hợp tìm kiếm ngữ nghĩa. Trong pgvector, <=> trả về khoảng cách cosine, và công cụ chuyển nó thành cosine_similarity bằng 1 trừ khoảng cách. Với các vector có độ dài 1, như những vector từ /api/embed, khoảng cách cosine, khoảng cách L2 và tích vô hướng sắp xếp các hàng theo cùng một thứ tự, vì với những vector như vậy bình phương khoảng cách L2 bằng 2 trừ đi 2 lần độ tương đồng cosine.

HNSW hay IVFFlat?

README của pgvector nói HNSW có hiệu năng truy vấn tốt hơn IVFFlat (xét về đánh đổi giữa tốc độ và recall) nhưng xây chậm hơn và dùng nhiều bộ nhớ hơn, và có thể được tạo trên bảng trống vì không có bước huấn luyện. IVFFlat xây nhanh hơn và dùng ít bộ nhớ hơn, và nên được tạo sau khi bảng đã có một ít dữ liệu. Khi không có chỉ mục, pgvector thực hiện tìm kiếm chính xác, cho recall hoàn hảo.

Vì sao truy vấn của tôi không dùng chỉ mục?

README nói truy vấn cần có ORDER BY và LIMIT, và ORDER BY phải là chính toán tử khoảng cách theo thứ tự tăng dần, không phải một biểu thức như 1 - (embedding <=> …) DESC. Toán tử cũng phải khớp với lớp toán tử của chỉ mục: <=> với vector_cosine_ops, <-> với vector_l2_ops, <#> với vector_ip_ops và <+> với vector_l1_ops. Công cụ viết cả hai từ cùng một lựa chọn. README cũng lưu ý rằng quét toàn bảng có thể nhanh hơn khi bảng nhỏ.

Vì sao tôi nhận được ít hàng hơn LIMIT?

Với HNSW, kết quả bị giới hạn bởi hnsw.ef_search, mặc định là 40; khi chúng tôi thử LIMIT 100 trên một bảng 200 hàng, chúng tôi nhận được 40 hàng cho đến khi nâng hnsw.ef_search lên 100. README bổ sung rằng việc lọc bằng WHERE được áp dụng sau khi quét chỉ mục, nên một điều kiện có thể làm số hàng còn ít hơn nữa. Từ phiên bản 0.8.0, quét chỉ mục lặp (SET hnsw.iterative_scan = strict_order hoặc relaxed_order) quét thêm chỉ mục khi cần.

halfvec là gì?

Một kiểu vector nửa độ chính xác. Mỗi vector chiếm 2 × số chiều + 8 byte thay vì 4 × số chiều + 8, và các chỉ mục HNSW và IVFFlat trên halfvec hỗ trợ đến 4,000 chiều thay vì 2,000. Mỗi phần tử là một số dấu phẩy động nửa độ chính xác, nên các giá trị được lưu kém chính xác hơn.

Dữ liệu của tôi có bị gửi đi đâu không?

Không. Công cụ chỉ viết SQL trong trình duyệt của bạn và không bao giờ kết nối tới cơ sở dữ liệu.

Bài viết liên quan

Liên quan