Když máš texty převedené na embeddingy, potřebuješ je někam uložit a umět v nich rychle najít nejbližší sousedy dotazu. To dělá vektorová databáze. Dobrá zpráva: pro většinu projektů nepotřebuješ nic nového instalovat, zvládne to Postgres, který nejspíš už máš.
Co taková databáze dělá
Uloží vektor + text + metadata a na dotaz „tady je vektor, dej mi 5 nejbližších“ odpoví v řádu milisekund, i když má milion záznamů.
dotaz → embedding → [0.02, -0.18, ...] → DB → 5 nejpodobnějších chunků + metadata
Naivně by se muselo porovnat se všemi vektory (to je přesné hledání). Databáze místo toho staví index (HNSW, IVF), který hledá přibližně, zato mnohonásobně rychleji. Tomu se říká ANN: approximate nearest neighbor.
💡 „Přibližně“ znamená, že občas nevrátí úplně nejbližšího souseda, ale téměř vždy někoho velmi blízkého. Za tu rychlost to stojí; přesné hledání dává smysl jen u malých kolekcí.
Co si vybrat
| Řešení | Kdy sáhnout |
|---|---|
| pgvector (rozšíření Postgresu) | Výchozí volba. Data i vektory na jednom místě, jedna transakce, znáš SQL. Zvládne miliony záznamů. |
| SQLite + vektorové rozšíření | Malé projekty, desktop, prototypy. |
| Specializované (Qdrant, Weaviate, Milvus…) | Desítky milionů vektorů, potřeba pokročilých filtrů a škálování. |
| Hostované (Pinecone…) | Nechceš nic provozovat a nevadí ti závislost a cena. |
Nezačínej specializovanou databází. Přidává provoz, zálohování a další místo, kde se data
rozejdou. pgvector je pro devět z deseti projektů správná odpověď.
CREATE EXTENSION vector;
CREATE TABLE chunky (
id bigserial PRIMARY KEY,
doc_id text NOT NULL,
nadpis text,
text text NOT NULL,
platny boolean DEFAULT true,
embedding vector(1536)
);
-- index pro rychlé hledání
CREATE INDEX ON chunky USING hnsw (embedding vector_cosine_ops);
-- dotaz: 5 nejbližších, jen platné dokumenty
SELECT text, nadpis, 1 - (embedding <=> $1) AS podobnost
FROM chunky
WHERE platny
ORDER BY embedding <=> $1
LIMIT 5;
Filtry jsou důležitější, než se zdá
Skoro vždycky chceš hledat jen v části dat: jen v platné verzi dokumentů, jen v jazyce uživatele, jen v tom, na co má daný zákazník právo.
⚠️ Filtrování podle práv nikdy nenechávej na modelu. Když do kontextu vložíš dokument, na který uživatel nemá nárok, model ho použije a máš únik dat. Filtr patří do dotazu do databáze.
Hybridní hledání
Samotné vektory selhávají na přesných řetězcích: číslo objednávky, název dílu X-42B, konkrétní
paragraf. Tam vyhrává klasický fulltext. Řešením je hybrid:
dotaz ─┬─ vektorové hledání → kandidáti A
└─ fulltext (BM25) → kandidáti B
↓
sloučit a přeřadit (rerank) → top 5
Rerank je druhý průchod, který kandidáty seřadí přesněji (buď specializovaným modelem, nebo LLM). Vyplatí se: často zvedne kvalitu RAG víc než výměna modelu.
Co hlídat v provozu
- Verze embeddingů. Ke každému záznamu ulož, jakým modelem vznikl. Bez toho nezvládneš migraci.
- Aktualizace a mazání. Když se dokument změní, staré chunky musí zmizet, jinak model odpovídá podle neplatné verze.
- Velikost. Milion vektorů po 1536 dimenzích je zhruba 6 GB jen na embeddingy. Menší dimenze (nebo komprimované) šetří výrazně.
- Kvalita hledání. Měř, jak často je správná odpověď mezi vrácenými chunky, bez toho ladíš poslepu (evaluace).
Cvičení
- Máš 200 000 chunků. Odhadni, kolik zabere index, když má embedding 1024 rozměrů ve float32. Co s tím uděláš, když je to moc?
- Přibližné hledání vrátí u jednoho dotazu jiné výsledky než přesné porovnání se všemi vektory. Je to chyba?
Náčrt řešení: rozbal, až si cvičení zkusíš sám
- Zhruba 820 MB.
200 000 × 1024 × 4 B = 819 200 000 B. Když je to moc, máš tři páky: uložit vektory ve float16 (polovina), zmenšit rozměr embeddingu, nebo použít kvantizovaný index, který drží vektory komprimované a přesnost dohání přepočtem u finalistů. Pořadí podle poměru užitku k práci je obvykle float16, pak menší dimenze, pak kvantizace. - Není, je to celý smysl přibližného hledání. Přesné porovnání se všemi vektory je lineární a u statisíců záznamů příliš pomalé, takže indexy typu HNSW nebo IVF prohledávají jen část prostoru a výměnou za rychlost připouštějí, že občas o skutečného nejbližšího souseda přijdou. Důležité je, aby ztráta byla malá a měřitelná: proto se u indexu ladí parametry podle recall proti přesnému hledání na vzorku.
Shrnutí
- Vektorová DB hledá nejbližší sousedy pomocí přibližného indexu (HNSW/IVF).
- Začni s
pgvectorv Postgresu; specializovanou databázi nasazuj, až když ti přeroste. - Filtry (práva, platnost, jazyk) patří do dotazu, ne do promptu.
- Kombinuj vektory s fulltextem a přidej rerank, bývá to největší skok v kvalitě.
Zákazník A dostal v odpovědi údaje zákazníka B. Kde je nejpravděpodobnější chyba?
Ve filtrování při vyhledávání. Do kontextu se dostal chunk, na který uživatel nemá právo, a model ho použil. Oprávnění se musí filtrovat už v dotazu do vektorové databáze, v promptu se to zaručit nedá.
Uživatelé hledají podle čísel dílů (např. „X-42B“) a sémantické hledání selhává. Proč a co s tím?
Embedding zachytí téma, ne přesný řetězec, takže konkrétní kód dílu nenajde spolehlivě. Řešením je hybridní hledání: vektory pro významové dotazy plus fulltext pro přesné identifikátory, výsledky sloučit a přeřadit.
Kdy má smysl nasadit specializovanou vektorovou databázi místo pgvectoru?
Když ti přeroste rozsah (desítky milionů vektorů), potřebuješ pokročilé filtry, škálování napříč uzly nebo funkce, které Postgres nenabízí. Do té doby je pgvector jednodušší: jedna databáze, jedna transakce, jedna záloha.
