Списък в паметта не стига
В 11. клас пазехме векторите в списък. Това работи до няколкостотин парчета и до първото рестартиране. Реалната система иска векторите да са в базата — редом с данните, които описват.
Векторна база
Хранилище, което пази вектори и умее бързо да намира най-близките до зададен вектор.
Двата подхода
| Разширение на релационна база | Специализирана услуга | |
|---|---|---|
| Пример | pgvector в PostgreSQL | отделна векторна база |
| Предимство | едно място за всичко; JOIN между вектори и таблици | по-бърза при десетки милиони вектори |
| Недостатък | по-бавна при много голям обем | второ хранилище за поддръжка и синхронизиране |
| За училищен проект | почти винаги правилният избор | излишно усложнение |
Правилото за избора
Започвайте от базата, която вече имате. Отделна векторна услуга се въвежда, когато измерите, че съществуващата не се справя — не предварително.
Схемата
Векторът никога не стои сам. Около него трябват поне: текстът на парчето, връзка към документа-родител, позиция и момент на изчисляване.
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE parcheta (
id SERIAL PRIMARY KEY,
dokument_id INT NOT NULL REFERENCES dokumenti(id),
poredno INT NOT NULL,
tekst TEXT NOT NULL,
model TEXT NOT NULL, -- кой модел е правил вектора
vektor VECTOR(384), -- броят измерения е фиксиран
izchisleno_na TIMESTAMP DEFAULT NOW()
);- Броят измерения е част от типа —
VECTOR(384). Друг модел с друга дължина изисква друга колона или друга таблица. - Името на модела се пази изрично, за да се знае кои вектори са остарели след смяна.
- Текстът се пази до вектора — при търсене се връща веднага, без второ обръщение.
Заявката за близост
-- <=> е разстояние по косинус в pgvector (по-малко = по-близко)
SELECT p.id, d.zaglavie, p.tekst,
1 - (p.vektor <=> $1) AS blizost
FROM parcheta p
JOIN dokumenti d ON d.id = p.dokument_id
WHERE p.model = 'multilingual-MiniLM'
ORDER BY p.vektor <=> $1
LIMIT 5;Филтриране заедно с близост
Тук е основното предимство на подхода „вектори в релационната база“: смисловото търсене се комбинира с обикновени условия в една заявка.
SELECT p.id, p.tekst, 1 - (p.vektor <=> $1) AS blizost
FROM parcheta p
JOIN dokumenti d ON d.id = p.dokument_id
WHERE d.v_sila = TRUE
AND d.data_na_sila <= CURRENT_DATE
ORDER BY p.vektor <=> $1
LIMIT 5;С отделна векторна услуга същото изисква две заявки и обединяване в кода — и лесно връща по-малко резултати от очакваното.
Вмъкване
- Изчисляваме вектора в приложението (или в задача по разписание).
- Вмъкваме заедно с текста, родителя и името на модела.
- При обновяване на текста преизчисляваме и обновяваме реда.
- При изтриване на документа изтриваме и парчетата — оттам е външният ключ.
Пакетно, не по едно
Изчисляването на вектори е бавно. За хиляда парчета се прави на партиди от по 32–64 и се вмъква с една заявка на партида, а не с хиляда отделни.