Obsah
1. SQLite-vec: vektorové rozšíření databáze SQLite
2. Koncept virtuálních tabulek v SQLite
3. Instalace rozšíření sqlite-vec
4. Základy práce s rozšířením sqlite-vec
5. Nové možnosti dotazu SELECT
6. Vizualizace výpočtů vzdáleností pro 2D vektory
7. Vytvoření a naplnění tabulky pro uložení 2D vektorů
8. Výpis obsahu tabulky se sloupcem s vektory
9. Vyhledávání na základě vzdálenosti vektorů
10. Tabulky obsahující jak sloupec s vektory, tak i sloupce s dalšími daty
11. Výpis hodnot podle vzdálenosti
12. Využití databáze SQLite s rozšířením SQLite-vec z Pythonu
13. Instalace balíčku sqllite-vec
14. Zjištění verze rozšíření SQLite-vec
15. Vytvoření virtuální tabulky s vektory
16. Naplnění tabulek s vektory
17. Dotazy: zjištění nejbližších vektorů
18. Repositář s demonstračními příklady
19. Předchozí články věnující se problematice vyhledávání podobných vektorů v databázích
1. SQLite-vec: vektorové rozšíření databáze SQLite
V posledních několika letech můžeme sledovat poměrně výrazné a stále častější nasazování takzvaných vektorových databází. Jedná se (alespoň většinou) o specializované databáze, které umožňují ukládat vektory numerických hodnot (pevné délky) a především efektivně vyhledávat vektory podle jejich podobnosti (například se zadaným vzorem), přičemž podobností může být myšlena například vzdálenost koncových bodů vektorů v Eukleidovském prostoru, kosinus úhlu mezi vektory, výsledek skalárního součinu atd. Tyto operace se dnes používají v mnoha oblastech. Například se s nimi můžeme setkat při rozpoznávání a zpracování přirozeného jazyka (NLP – Native Language Processing), při rozpoznávání obrázků, rozpoznávání hlasů, detekci anomálií, ale i (a to zejména) v souvislosti s velkými jazykovými modely (LLM). K tomuto stále ještě aktuálnímu tématu se ještě vrátíme v samostatném článku.
Ovšem namísto specializovaných databází je možné si zvolit nějakou existující technologii, která se v praxi osvědčuje už desítky let. Na stránkách Roota jsme se již setkali s relační databází PostgreSQL, pro kterou vzniklo rozšíření nazvané pgvector. Toto rozšíření umožňuje efektivní ukládání vektorů do tabulek, ale zejména podporuje i vyhledávání vektorů na základě jejich podobnosti (navíc lze využít různé varianty indexů atd.). Ovšem nejedná se o jedinou klasickou relační databázi, která byla rozšířena i do oblasti vyhledávání podobných vektorů. Podobné rozšíření existuje i pro databázi SQLite; samotné rozšíření se jmenuje jednoduše SQLite-vec.
Dnes se seznámíme se základními vlastnostmi tohoto rozšíření a ve druhé části článku i s příklady použití SQLite-vec v aplikacích vytvářených v programovacím jazyku Python.
2. Koncept virtuálních tabulek v SQLite
V popisu vektorového rozšíření databáze SQLite se setkáme s termínem virtuální tabulka. Jedná se o potenciálně velmi užitečnou vlastnost databáze SQLite, na kterou se můžeme dívat ze dvou stran. Častější je pohled vývojáře, který SQLite používá jako běžnou SQL databázi. Z pohledu tohoto vývojáře se virtuální tabulka do značné míry podobá běžné tabulce (až na poněkud odlišný způsob její konstrukce). Typicky jsou podporovány všechny základní operace pro manipulaci s daty (insert, update, delete) i operace pro vyhledávání (query). Odlišný je však pohled vývojáře, který vytváří rozšíření pro databázi SQLite. Virtuální tabulka je totiž objektem s callback metodami, které se volají pro všechny databázové operace nad touto tabulkou. Jinými slovy to znamená, že například příkaz INSERT INTO virtuální_tabulka … bude interně převeden na volání metody příslušného (zaregistrovaného) objektu.
To, jak konkrétně bude virtuální tabulka uložena, je zcela v kompetenci příslušného objektu, tj. záleží na implementaci příslušného rozšíření. Konkrétně v případě vektorového rozšíření je každá virtuální tabulka interně uložena jako několik (konkrétně pět) běžných tabulek, což je ovšem do značné míry implementační detail.
3. Instalace rozšíření sqlite-vec
Rozšíření sqlite-vec není standardní součástí databáze SQLlite, takže je nutné ho nainstalovat zvlášť. V této kapitole si ukážeme, jakým způsobem je možné toto rozšíření přeložit do sdílené knihovny, která se poté načte do spuštěné konzole SQLite. K překladu postačují jen základní nástroje GCC nebo Clang a taktéž nástroj make.
Nejdříve je nutné nainstalovat balíček sqlite-devel (pozor, v názvu chybí trojka). Tento balíček obsahuje hlavičkové soubory použité při překladu sqlite-vec a taktéž sdílenou knihovnu libsqlite3.so:
/usr/include/sqlite3.h /usr/include/sqlite3ext.h /usr/lib/libsqlite3.so /usr/lib/pkgconfig/sqlite3.pc /usr/lib64/libsqlite3.so /usr/lib64/pkgconfig/sqlite3.pc
Instalace tohoto balíčku vypadá na distribucích Linux s RPM následovně:
$ sudo dnf install sqlite-devel Updating and loading repositories: Repositories loaded. Package Arch Version Repository Size Installing: sqlite-devel x86_64 3.47.2-5.fc42 updates 673.4 KiB Transaction Summary: Installing: 1 package Total size of inbound packages is 146 KiB. Need to download 146 KiB. After this operation, 673 KiB extra will be used (install 673 KiB, remove 0 B). Is this ok [y/N]: y [1/1] sqlite-devel-0:3.47.2-5.fc42.x86_64 100% | 617.3 KiB/s | 146.3 KiB | 00m00s ---------------------------------------------------------------------------------------------------------------------------------------- [1/1] Total 100% | 143.4 KiB/s | 146.3 KiB | 00m01s Running transaction [1/3] Verify package files 100% | 166.0 B/s | 1.0 B | 00m00s [2/3] Prepare transaction 100% | 4.0 B/s | 1.0 B | 00m00s [3/3] Installing sqlite-devel-0:3.47.2-5.fc42.x86_64 100% | 1.4 MiB/s | 674.1 KiB | 00m00s Complete!
Ve druhém kroku se provede naklonování repositáře se zdrojovými kódy rozšíření sqlite-vec:
$ git clone git@github.com:asg017/sqlite-vec.git Cloning into 'sqlite-vec'... remote: Enumerating objects: 2475, done. remote: Counting objects: 100% (1033/1033), done. remote: Compressing objects: 100% (234/234), done. remote: Total 2475 (delta 873), reused 799 (delta 799), pack-reused 1442 (from 1) Receiving objects: 100% (2475/2475), 955.37 KiB | 640.00 KiB/s, done. Resolving deltas: 100% (1379/1379), done.
Přejdeme do adresáře s projektem:
$ cd sqlite-vec
Překlad se provede tímto příkazem:
$ make loadable static
mkdir -p dist
cc \
-fPIC -shared \
-Wall -Wextra \
-Ivendor/ \
-O3 \
-lm \
sqlite-vec.c -o dist/vec0.so
...
...
...
Po (doufejme že úspěšném) překladu by se v podadresáři dist měla nacházet jak sdílená knihovna vec0.so, tak i libsqlite_vec0.a (archiv s objektovým souborem vec0.o, ten ovšem nepoužijeme):
$ ls -l dist total 360 -rw-r--r--. 1 ptisnovs ptisnovs 210222 Jan 8 16:50 libsqlite_vec0.a -rwxr-xr-x. 1 ptisnovs ptisnovs 153120 Jan 8 16:50 vec0.so
4. Základy práce s rozšířením sqlite-vec
Ve chvíli, kdy máme k dispozici sdílenou nativní knihovnu s vektorovým rozšířením, si toto rozšíření můžeme otestovat. V této kapitole si ukážeme základy práce s vektorovým rozšířením; podrobnější popis bude uveden později.
Spustíme REPL databáze SQLite s předáním jména souboru, do kterého má být uloženo schéma i vlastní tabulky. Tento soubor je v případě potřeby vytvořen:
$ sqlite3 test1.db SQLite version 3.47.2 2024-12-07 20:39:59 Enter ".help" for usage hints.
Příkazem .load se pokusíme o načtení rozšíření:
sqlite> .load ./vec0
Vypíšeme si existující tabulky, přičemž by se měl vrátit prázdný seznam:
sqlite> .tables
Nyní vytvoříme novou virtuální tabulku nazvanou vec_examples, která obsahuje sloupec sample_embedding s osmiprvkovými vektory:
sqlite> create virtual table vec_examples using vec0( sample_embedding float[8] );
Znovu se pokusíme o výpis existujících tabulek:
sqlite> .tables vec_examples vec_examples_rowids vec_examples_chunks vec_examples_vector_chunks00 vec_examples_info
Z výpisu je patrné, že virtuální tabulka vec_examples je ve skutečnosti tvořena pěti dalšími tabulkami.
Jediná nová tabulka obsahující nějaké údaje se jmenuje vec_examples_info. Ta obsahuje metadata o verzi vektorového rozšíření:
sqlite> select * from vec_examples_info; CREATE_VERSION|v0.1.7-alpha.2 CREATE_VERSION_MAJOR|0 CREATE_VERSION_MINOR|1 CREATE_VERSION_PATCH|7
5. Nové možnosti dotazu SELECT
Vektorové rozšíření nejenom že umožňuje do databáze ukládat vektory, ale navíc byl upraven i příkaz SELECT, který dokáže získat seznam nejbližších vektorů, tj. provádět klasickou operaci nazývanou vector similarity search. Jedná se o velmi užitečnou vlastnost, kterou si nyní ukážeme.
Nejdříve do virtuální tabulky vec_examples vložíme čtveřici osmiprvkových vektorů:
sqlite> insert into vec_examples(rowid, sample_embedding) values (1, '[1, 0, 0, 0, 0, 0, 0, 0]'); sqlite> insert into vec_examples(rowid, sample_embedding) values (2, '[0, 1, 0, 0, 0, 0, 0, 0]'); sqlite> insert into vec_examples(rowid, sample_embedding) values (3, '[0, 0, 1, 0, 0, 0, 0, 0]'); sqlite> insert into vec_examples(rowid, sample_embedding) values (4, '[1, 0, 1, 0, 0, 0, 0, 0]');
Nově je možné v příkazu SELECT použít pseudosloupec distance, například následujícím způsobem:
SELECT rowid, distance FROM vec_examples WHERE sample_embedding MATCH '[0, 0.5, 0.5, 0., 0., 0., 0., 0.]' ORDER BY distance LIMIT 4;
Tento příkaz by měl vrátit čtveřici hodnot – vzdáleností od vektoru, který byl zadán v příkazu SELECT v klauzuli WHERE MATCH:
3|0.70710676908493 2|0.70710676908493 4|1.22474491596222 1|1.22474491596222
Podobný příkaz, ovšem pro vektor, který je v databázi uložen:
SELECT rowid, distance FROM vec_examples WHERE sample_embedding MATCH '[1, 0., 0., 0., 0., 0., 0., 0.]' ORDER BY distance LIMIT 4;
Asi nebude velkým překvapením, že první nalezený vektor bude mít nulovou vzdálenost:
1|0.0 4|1.0 3|1.41421353816986 2|1.41421353816986
Pro čitelnější výsledky je vhodné zadat příkaz:
sqlite> .mode qbox
Zkusme si výše uvedené příkazy zadat znovu:
sqlite> select rowid, distance from vec_examples where sample_embedding match '[0, 0.5, 0.5, 0., 0., 0., 0., 0.]' order by distance limit 4;
Výsledky:
┌───────┬──────────────────┐ │ rowid │ distance │ ├───────┼──────────────────┤ │ 3 │ 0.70710676908493 │ │ 2 │ 0.70710676908493 │ │ 4 │ 1.22474491596222 │ │ 1 │ 1.22474491596222 │ └───────┴──────────────────┘
A:
sqlite> select rowid, distance from vec_examples where sample_embedding match '[1, 0., 0., 0., 0., 0., 0., 0.]' order by distance limit 4;
S výsledky:
┌───────┬──────────────────┐ │ rowid │ distance │ ├───────┼──────────────────┤ │ 1 │ 0.0 │ │ 4 │ 1.0 │ │ 3 │ 1.41421353816986 │ │ 2 │ 1.41421353816986 │ └───────┴──────────────────┘
6. Vizualizace výpočtů vzdáleností pro 2D vektory
Na 2D vektory se můžeme (i když to není zcela přesné) dívat jako na koncové souřadnice orientovaných šipek vycházejících z počátku kartézské soustavy souřadnic. V případě, že si tyto souřadnice vykreslíme do plochy, získáme například následující graf (do kterého jsem doplnil numerické souřadnice u vybraných koncových bodů):
│ y
│
│
│
│ [5,5]
o o │ o o o
│ o o o
o │ o o o
[-4, 3] │
│
─────────────────────────────────────[0,0]──────────────────────────────────────
│ x
│ o
│
│ o o
│ [5,-5]
│
│
│
│
Tento diagram obsahuje následující koncové souřadnice vektorů (použijeme je později v reálné databázi):
| Souřadnice |
|---|
| [-5, 5] |
| [-4, 3] |
| [-3, 5] |
| [ 3, –5] |
| [ 4, –3] |
| [ 5, –5] |
| [ 3, 3] |
| [ 3, 4] |
| [ 3, 5] |
| [ 4, 3] |
| [ 4, 4] |
| [ 4, 5] |
| [ 5, 3] |
| [ 5, 4] |
| [ 5, 5] |
7. Vytvoření a naplnění tabulky pro uložení 2D vektorů
Podívejme se nyní podrobněji na způsob využití rozšíření sqlite-vec. Nejdříve vytvoříme virtuální tabulku nazvanou v2, která bude obsahovat jediný explicitně zadaný sloupec s názvem embedding. Do tohoto sloupce se budou ukládat dvouprvkové vektory, přičemž každý prvek bude typu float (numerická hodnota s plovoucí řádovou čárkou):
CREATE virtual TABLE v2 USING vec0( embedding float[2] );
Následně tabulku naplníme patnácti záznamy (vektory). Souřadnice těchto vektorů odpovídají diagramu, který byl uveden v předchozí kapitole:
INSERT INTO v2(rowid, embedding) VALUES(1, '[-5, 5]'); INSERT INTO v2(rowid, embedding) VALUES(2, '[-4, 3]'); INSERT INTO v2(rowid, embedding) VALUES(3, '[-3, 5]'); INSERT INTO v2(rowid, embedding) VALUES(4, '[ 3, -5]'); INSERT INTO v2(rowid, embedding) VALUES(5, '[ 4, -3]'); INSERT INTO v2(rowid, embedding) VALUES(6, '[ 5, -5]'); INSERT INTO v2(rowid, embedding) VALUES(7, '[ 3, 3]'); INSERT INTO v2(rowid, embedding) VALUES(8, '[ 3, 4]'); INSERT INTO v2(rowid, embedding) VALUES(9, '[ 3, 5]'); INSERT INTO v2(rowid, embedding) VALUES(10, '[ 4, 3]'); INSERT INTO v2(rowid, embedding) VALUES(11, '[ 4, 4]'); INSERT INTO v2(rowid, embedding) VALUES(12, '[ 4, 5]'); INSERT INTO v2(rowid, embedding) VALUES(13, '[ 5, 3]'); INSERT INTO v2(rowid, embedding) VALUES(14, '[ 5, 4]'); INSERT INTO v2(rowid, embedding) VALUES(15, '[ 5, 5]');
Pro jistotu si ověříme, že tabulka v2 skutečně obsahuje patnáct záznamů:
sqlite> select count(*) from v2; 15
8. Výpis obsahu tabulky se sloupcem s vektory
Pokusme se vypsat všech patnáct záznamů, které jsou v tabulce v2 uloženy. Nastavíme hezčí (nebo „hezčí“) způsob výpisu příkazem .mode a poté spustíme standardní dotaz select:
sqlite> .mode qbox sqlite> select * from v2;
Z výpisu je patrné, že tabulka ve skutečnosti obsahuje ještě sloupec rowid, který jsme sice při definici tabulky nepožadovali, ale který byl doplněn automaticky. A taktéž je patrné, že se obsah vektorů zobrazil v hexadecimální (interní) podobě; každý vektor jako osmice bajtů:
┌───────┬─────────────────────┐ │ rowid │ embedding │ ├───────┼─────────────────────┤ │ 1 │ x'0000a0c00000a040' │ │ 2 │ x'000080c000004040' │ │ 3 │ x'000040c00000a040' │ │ 4 │ x'000040400000a0c0' │ │ 5 │ x'00008040000040c0' │ │ 6 │ x'0000a0400000a0c0' │ │ 7 │ x'0000404000004040' │ │ 8 │ x'0000404000008040' │ │ 9 │ x'000040400000a040' │ │ 10 │ x'0000804000004040' │ │ 11 │ x'0000804000008040' │ │ 12 │ x'000080400000a040' │ │ 13 │ x'0000a04000004040' │ │ 14 │ x'0000a04000008040' │ │ 15 │ x'0000a0400000a040' │ └───────┴─────────────────────┘
Abychom získali vektory v čitelné podobě, musíme použít malý trik – necháme si obsah vektorů převést na JSON, tedy v tomto případě konkrétně na seznam s dvojicí numerických hodnot:
sqlite> select rowid, vec_to_json(embedding) from v2;
Nyní bude výpis snadno čitelný:
┌───────┬────────────────────────┐ │ rowid │ vec_to_json(embedding) │ ├───────┼────────────────────────┤ │ 1 │ '[-5.000000,5.000000]' │ │ 2 │ '[-4.000000,3.000000]' │ │ 3 │ '[-3.000000,5.000000]' │ │ 4 │ '[3.000000,-5.000000]' │ │ 5 │ '[4.000000,-3.000000]' │ │ 6 │ '[5.000000,-5.000000]' │ │ 7 │ '[3.000000,3.000000]' │ │ 8 │ '[3.000000,4.000000]' │ │ 9 │ '[3.000000,5.000000]' │ │ 10 │ '[4.000000,3.000000]' │ │ 11 │ '[4.000000,4.000000]' │ │ 12 │ '[4.000000,5.000000]' │ │ 13 │ '[5.000000,3.000000]' │ │ 14 │ '[5.000000,4.000000]' │ │ 15 │ '[5.000000,5.000000]' │ └───────┴────────────────────────┘
9. Vyhledávání na základě vzdálenosti vektorů
Nyní se pokusme vyhledat tři nejbližší vektory k vektoru [-4, 4]:
SELECT rowid, distance FROM v2 WHERE embedding MATCH '[-4, 4]' ORDER BY distance LIMIT 3;
Výsledky by měly vypadat následovně (druhé dvě hodnoty odpovídají mocnině dvou, tedy vzdálenosti bodů po úhlopříčce):
┌───────┬──────────────────┐ │ rowid │ distance │ ├───────┼──────────────────┤ │ 2 │ 1.0 │ │ 3 │ 1.41421353816986 │ │ 1 │ 1.41421353816986 │ └───────┴──────────────────┘
Což si pochopitelně můžeme vizualizovat v rovině (hvězdičkou je naznačen vektor z podmínky, kolečkem vektory vrácené jako výsledek dotazu a tečkou ostatní vektory, které neodpovídají dotazu):
│ y
│
│
│
│
o o │ . . .
* │ . . .
o │ . . .
│
│
─────────────────────────────────────[0,0]──────────────────────────────────────
│ x
│
│ .
│
│ . .
│
│
│
│
Podobně můžeme získat pět vektorů, které jsou nejpodobnější vektoru [4, 4]:
SELECT rowid, distance FROM v2 WHERE embedding MATCH '[4, 4]' ORDER BY distance LIMIT 5;
Získané výsledky:
┌───────┬──────────┐ │ rowid │ distance │ ├───────┼──────────┤ │ 11 │ 0.0 │ │ 14 │ 1.0 │ │ 12 │ 1.0 │ │ 10 │ 1.0 │ │ 8 │ 1.0 │ └───────┴──────────┘
Výsledných pět vrácených vektorů si opět zobrazíme v 2D rovině (první bod/vektor je totožný se zadaným vektorem):
│ y
│
│
│
│
. . │ . o .
. │ o * o
. │ . o .
│
│
─────────────────────────────────────[0,0]──────────────────────────────────────
│ x
│
│ .
│
│ . .
│
│
│
│
Tři vektory nejpodobnější vektoru [3, 3]:
SELECT rowid, distance FROM v2 WHERE embedding MATCH '[3, 3]' ORDER BY distance LIMIT 3;
Výsledky:
┌───────┬──────────┐ │ rowid │ distance │ ├───────┼──────────┤ │ 7 │ 0.0 │ │ 10 │ 1.0 │ │ 8 │ 1.0 │ └───────┴──────────┘
V 2D rovině budou výsledky vypadat takto:
│ y
│
│
│
│
. . │ . . .
. │ o . .
. │ * o .
│
│
─────────────────────────────────────[0,0]──────────────────────────────────────
│ x
│
│ .
│
│ . .
│
│
│
│
A konečně vzdálenosti nejbližších deseti vektorů:
sqlite> SELECT rowid, distance FROM v2 WHERE embedding MATCH '[3, 3]' and k=10;
S výsledky:
┌───────┬──────────────────┐ │ rowid │ distance │ ├───────┼──────────────────┤ │ 7 │ 0.0 │ │ 10 │ 1.0 │ │ 8 │ 1.0 │ │ 11 │ 1.41421353816986 │ │ 13 │ 2.0 │ │ 9 │ 2.0 │ │ 14 │ 2.2360680103302 │ │ 12 │ 2.2360680103302 │ │ 15 │ 2.82842707633972 │ │ 5 │ 6.08276271820068 │ └───────┴──────────────────┘
10. Tabulky obsahující jak sloupec s vektory, tak i sloupce s dalšími daty
Samozřejmě je možné, aby virtuální tabulka obsahující sloupec s vektory obsahovala i další sloupce, například s původními texty (před embeddingem) atd. Ukažme si tuto vlastnost na příkladu tabulky pojmenované v3, která kromě sloupce embedding s vektory obsahuje i sloupec name, jenž může obsahovat běžný text:
CREATE virtual TABLE v3 USING vec0( embedding float[2], name text );
Tabulku naplníme našimi patnácti vektory a navíc ještě doplníme nějaké texty:
INSERT INTO v3(rowid, embedding, name) VALUES(1, '[-5, 5]', 'one'); INSERT INTO v3(rowid, embedding, name) VALUES(2, '[-4, 3]', 'two'); INSERT INTO v3(rowid, embedding, name) VALUES(3, '[-3, 5]', 'three'); INSERT INTO v3(rowid, embedding, name) VALUES(4, '[ 3, -5]', 'four'); INSERT INTO v3(rowid, embedding, name) VALUES(5, '[ 4, -3]', 'five'); INSERT INTO v3(rowid, embedding, name) VALUES(6, '[ 5, -5]', 'six'); INSERT INTO v3(rowid, embedding, name) VALUES(7, '[ 3, 3]', 'seven'); INSERT INTO v3(rowid, embedding, name) VALUES(8, '[ 3, 4]', 'eight'); INSERT INTO v3(rowid, embedding, name) VALUES(9, '[ 3, 5]', 'nine'); INSERT INTO v3(rowid, embedding, name) VALUES(10, '[ 4, 3]', 'ten'); INSERT INTO v3(rowid, embedding, name) VALUES(11, '[ 4, 4]', 'evelen'); INSERT INTO v3(rowid, embedding, name) VALUES(12, '[ 4, 5]', 'twelve'); INSERT INTO v3(rowid, embedding, name) VALUES(13, '[ 5, 3]', 'thirteen'); INSERT INTO v3(rowid, embedding, name) VALUES(14, '[ 5, 4]', 'fourteen'); INSERT INTO v3(rowid, embedding, name) VALUES(15, '[ 5, 5]', 'fifteen');
11. Výpis hodnot podle vzdálenosti
V tomto okamžiku může dotaz (podle očekávání) obsahovat i pokyn k výpisu jak vzdálenosti, tak i obsahu získaného ze sloupce s textem:
sqlite> .mode qbox sqlite> SELECT rowid, distance, name FROM v3 WHERE embedding MATCH '[-4, 4]' ORDER BY distance LIMIT 10;
Ze zobrazených výsledků je patrné, že vše pracuje podle očekávání, tj. vypíšou se jak vypočtené vzdálenosti, tak i obsahy sloupce name:
┌───────┬──────────────────┬────────────┐ │ rowid │ distance │ name │ ├───────┼──────────────────┼────────────┤ │ 2 │ 1.0 │ 'two' │ │ 3 │ 1.41421353816986 │ 'three' │ │ 1 │ 1.41421353816986 │ 'one' │ │ 8 │ 7.0 │ 'eight' │ │ 9 │ 7.07106781005859 │ 'nine' │ │ 7 │ 7.07106781005859 │ 'seven' │ │ 11 │ 8.0 │ 'evelen' │ │ 12 │ 8.06225776672363 │ 'twelve' │ │ 10 │ 8.06225776672363 │ 'ten' │ │ 14 │ 9.0 │ 'fourteen' │ └───────┴──────────────────┴────────────┘
12. Využití databáze SQLite s rozšířením SQLite-vec z Pythonu
Ve druhé části dnešního článku si ukážeme základní způsoby využití rozšíření SQLite-vec z programovacího jazyka Python. Demonstrační příklady budou postaveny nad balíčkem sqllite-vec, který však (což je logické) ke své činnosti vyžaduje i balíček sqlite3, který je v současnosti součástí standardní knihovny Pythonu (viz sqlite3 – DB-API 2.0 interface for SQLite databases. Oba balíčky zajišťují jednoduché načtení rozšíření sqlite-vec, takže se programátor nemusí příliš starat o to, kde je vlastně umístěna příslušná sdílená nativní knihovna. A současně jsou k dispozici i pomocné funkce zajišťující převod seznamů a popř. i n-dimenzionálních polí (ndarray) na vektory. Tyto konverzní funkce se používají jak při manipulaci s daty uloženými v databázi (insert, update, delete), tak i při provádění dotazů (query).
13. Instalace balíčku sqllite-vec
Druhá část tohoto článku je zaměřena na praktické ukázky použití knihovny s vektorovým rozšířením SQLlite. Musíme si tedy připravit projekt v Pythonu a následně do něj knihovnu sqlite-vec přidat formou přímé závislosti (dependency). Pro vytvoření projektu použijeme buď nástroj PDM – viz též PDM: moderní správce balíčků a virtuálních prostředí Pythonu nebo (což je v současnosti výhodnější) nástroj uv:
$ uv init test-sqlite-vec Initialized project `test-sqlite-vec`
Následně do projektu přidáme nový balíček sqlite-vec, který bude nainstalován do virtuálního prostředí Pythonu:
$ uv add sqlite-vec Using CPython 3.13.9 interpreter at: /usr/bin/python3.13 Creating virtual environment at: .venv Resolved 2 packages in 258ms Prepared 1 package in 156ms Installed 1 package in 3ms + sqlite-vec==0.1.6
Výsledný projektový soubor bude vypadat takto:
[project]
name = "test-sqlite-vec"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"sqlite-vec>=0.1.6",
]
Od této chvíle je nutné všechny dále uvedené příklady spouštět takto:
$ uv run jméno_skriptu
Dále si můžete doinstalovat – i když to dnes není zapotřebí – i užitečný balíček sqlite-utils, který vlastně slouží jako manažer celého ekosystému postaveného nad SQLite:
$ uv add sqlite-utils Resolved 12 packages in 363ms Prepared 9 packages in 428ms Installed 9 packages in 22ms + click==8.3.1 + click-default-group==1.2.4 + pip==25.3 + pluggy==1.6.0 + python-dateutil==2.9.0.post0 + six==1.17.0 + sqlite-fts4==1.0.3 + sqlite-utils==3.39 + tabulate==0.9.0
Jak se tento balíček používá? Jedná se o nový příkaz, kterým lze například doinstalovat další rozšíření knihovny SQLite atd. Pro zajímavost si uveďme příklad jeho použití:
$ uv run sqlite-utils install sqlite-utils-sqlite-vec Collecting sqlite-utils-sqlite-vec Downloading sqlite_utils_sqlite_vec-0.1.6-py3-none-any.whl.metadata (275 bytes) Requirement already satisfied: sqlite-utils in ./.venv/lib64/python3.13/site-packages (from sqlite-utils-sqlite-vec) (3.39) Requirement already satisfied: sqlite-vec==0.1.6 in ./.venv/lib64/python3.13/site-packages (from sqlite-utils-sqlite-vec) (0.1.6) Requirement already satisfied: sqlite-fts4 in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (1.0.3) Requirement already satisfied: click>=8.3.1 in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (8.3.1) Requirement already satisfied: click-default-group>=1.2.3 in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (1.2.4) Requirement already satisfied: tabulate in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (0.9.0) Requirement already satisfied: python-dateutil in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (2.9.0.post0) Requirement already satisfied: pluggy in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (1.6.0) Requirement already satisfied: pip in ./.venv/lib64/python3.13/site-packages (from sqlite-utils->sqlite-utils-sqlite-vec) (25.3) Requirement already satisfied: six>=1.5 in ./.venv/lib64/python3.13/site-packages (from python-dateutil->sqlite-utils->sqlite-utils-sqlite-vec) (1.17.0) Downloading sqlite_utils_sqlite_vec-0.1.6-py3-none-any.whl (2.3 kB) Installing collected packages: sqlite-utils-sqlite-vec Successfully installed sqlite-utils-sqlite-vec-0.1.6
Výsledný projektový soubor bude vypadat takto:
[project]
name = "test-sqlite-vec"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"sqlite-utils>=3.39",
"sqlite-vec>=0.1.6",
]
14. Zjištění verze rozšíření SQLite-vec
Nyní, když již máme nainstalovány všechny potřebné balíčky Pythonu, si můžeme vyzkoušet, jakým způsobem se s balíčkem sqlite-vecpracuje. V dnešním prvním Pythonovském demonstračním příkladu je nejdříve provedeno připojení k databázi uložené v souboru v0.db. Pokud tato databáze a/nebo soubor neexistuje, dojde k jejich vytvoření:
db = sqlite3.connect("v0.db")
Následně načteme vektorové rozšíření. Tato operace by měla proběhnout korektně, protože nativní sdílená knihovna je součástí virtuálního prostředí Pythonu:
db.enable_load_extension(True) sqlite_vec.load(db) db.enable_load_extension(False)
V posledním kroku si necháme vypsat verzi rozšíření:
vec_version, = db.execute("select vec_version()").fetchone()
print(f"vec_version={vec_version}")
Pokud byly dodrženy všechny kroky uvedené v předchozí kapitole, měl by tento příklad vypsat:
vec_version=v0.1.6
Následuje výpis celého zdrojového kódu tohoto demonstračního příkladu:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v souboru
# - databáze je v případě potřeby vytvořena (nový soubor)
# - načtení rozšíření SQLite-vec
# - tisk verze rozšíření SQLite-vec
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
db = sqlite3.connect("v0.db")
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
# tisk verze rozšíření SQLite-vec
vec_version, = db.execute("select vec_version()").fetchone()
print(f"vec_version={vec_version}")
SQLite navíc umožňuje vytvoření databáze pouze v operační paměti, tj. bez toho, aby se databáze ukládala do reálného souboru. Pro tento účel je nutné při připojování k databází použít „magické“ jméno :memory::
db = sqlite3.connect(":memory:")
Skript po této úpravě bude vypadat následovně:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v paměti
# - databáze je (nutně) vždy vytvořena
# - načtení rozšíření SQLite-vec
# - tisk verze rozšíření SQLite-vec
import sqlite3
import sqlite_vec
# připojení k databázi uložené v paměti
# databáze je vždy vytvořena
db = sqlite3.connect(":memory:")
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
# tisk verze rozšíření SQLite-vec
vec_version, = db.execute("select vec_version()").fetchone()
print(f"vec_version={vec_version}")
Výsledkem však bude naprosto stejná zpráva s informací o verzi vektorového rozšíření:
vec_version=v0.1.6
15. Vytvoření virtuální tabulky s vektory
V dalším kroku pochopitelně budeme muset vytvořit tabulku, která obsahuje sloupec nebo sloupce určené pro uložení vektorů (pochopitelně s předem známým počtem prvků). Je přitom nutné, aby již bylo načteno vektorové rozšíření, což jsme si ukázali v předchozí kapitole.
Při práci s relačními databázemi v Pythonu se typicky postupuje takovým způsobem, že se nejdříve získá kurzor a další příkazy pro manipulaci s daty nebo pro dotazy se provádí přes tento objekt. Ovšem standardní balíček sqlite3 nabízí v tomto případě „zkratku“, protože příkazy pro manipulaci s daty (ale i například pro vytvoření tabulky atd.) lze provádět přímo nad objektem, který získáme po připojení k databázi.
Celý postup, při kterém vytvoříme virtuální tabulku, tedy může vypadat následovně:
db = sqlite3.connect("v1.db")
TABLE_STATEMENT = """
CREATE virtual TABLE v1 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
db.commit()
db.close()
Následuje úplný zdrojový kód dnešního třetího demonstračního příkladu napsaného v Pythonu:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v souboru
# - databáze je v případě potřeby vytvořena
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky s jedním sloupcem
# - nepoužívá se kurzor (zkrácený zápis)
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
db = sqlite3.connect("v1.db")
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v1 USING vec0(
embedding float[2]
);
"""
# vytvoření virtuální tabulky
result = db.execute(TABLE_STATEMENT)
print(result)
db.commit()
db.close()
V praxi je však výhodnější se explicitnímu odpojení od databáze vyhnout (kvůli nutnosti zachytávání výjimek atd.) a použít namísto toho context manager. Zápis je čitelnější, kratší a navíc i bezpečnější (databázi zavře i při vzniku výjimky):
with sqlite3.connect("v1.db") as db:
TABLE_STATEMENT = """
CREATE virtual TABLE v1 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
db.commit()
Výsledná podoba demonstračního příkladu:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v souboru
# - databáze je v případě potřeby vytvořena
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky s jedním sloupcem
# - využití kontext manažeru
# - nepoužívá se kurzor (zkrácený zápis)
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
with sqlite3.connect("v1.db") as db:
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v1 USING vec0(
embedding float[2]
);
"""
# vytvoření virtuální tabulky
result = db.execute(TABLE_STATEMENT)
print(result)
db.commit()
16. Naplnění tabulek s vektory
Tabulku, resp. přesněji řečeno virtuální tabulku, která byla vytvořena skripty uvedenými v předchozí kapitole, je pochopitelně možné naplnit záznamy, které obsahují dvouprvkové vektory. V knihovně sqlite_vec je mj. dostupná i funkce nazvaná serialize_float32, která dokáže převést obsah běžného Pythonovského seznamu na vektor, jenž je možné uložit do databáze. Můžeme tedy provést například tuto operaci:
INSERT_STATEMENT = "INSERT INTO v2(embedding) VALUES(?)" db.execute(INSERT_STATEMENT, [sqlite_vec.serialize_float32([1, 2])])
V tomto demonstračním příkladu opět používáme „zkratku“, protože voláme přímo metodu execute nad objektem získaným po připojení do databáze. V případě, že se použije odlišná databáze (což zde nemá smysl) by se musel vytvořit kurzor:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v souboru
# - databáze je v případě potřeby vytvořena
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky
# - naplnění tabulky několika vektory
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
with sqlite3.connect("v2.db") as db:
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v2 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
INSERT_STATEMENT = "INSERT INTO v2(embedding) VALUES(?)"
db.execute(INSERT_STATEMENT, [sqlite_vec.serialize_float32([1, 2])])
db.execute(INSERT_STATEMENT, [sqlite_vec.serialize_float32([1, 2])])
db.execute(INSERT_STATEMENT, [sqlite_vec.serialize_float32([1, 2])])
db.commit()
Výše uvedený demonstrační příklad nyní nepatrně upravíme, a to tak, abychom do (nové) virtuální tabulky uložili všech patnáct vektorů v 2D, které byly použity v úvodní části dnešního článku. Vektory jsou původně reprezentovány seznamem seznamů a do tabulky jsou vloženy ve smyčce (což ovšem není nejrychlejší způsob):
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v souboru
# - databáze je v případě potřeby vytvořena
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky
# - naplnění tabulky patnácti vektory
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
with sqlite3.connect("v2.db") as db:
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v2 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
INSERT_STATEMENT = "INSERT INTO v2(rowid, embedding) VALUES(?, ?)"
data_to_insert = [
[1, [-5, 5]],
[2, [-4, 3]],
[3, [-3, 5]],
[4, [ 3, -5]],
[5, [ 4, -3]],
[6, [ 5, -5]],
[7, [ 3, 3]],
[8, [ 3, 4]],
[9, [ 3, 5]],
[10, [ 4, 3]],
[11, [ 4, 4]],
[12, [ 4, 5]],
[13, [ 5, 3]],
[14, [ 5, 4]],
[15, [ 5, 5]],
]
for data in data_to_insert:
print(f"Inserting {data[1]}")
db.execute(INSERT_STATEMENT, [data[0], sqlite_vec.serialize_float32(data[1])])
db.commit()
17. Dotazy: zjištění nejbližších vektorů
V poslední dvojici demonstračních příkladů nejenže vytvoříme virtuální tabulku a naplníme ji daty (vektory), ale taktéž provedeme dotaz, který zjistí tři nejbližší vektory k vektoru zadanému. Se znalostmi z předchozích kapitol by měl být tento příklad snadno pochopitelný:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v paměti
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky
# - naplnění tabulky patnácti vektory
# - nalezení tří vektorů nejbližích k [-4, 4]
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
with sqlite3.connect(":memory:") as db:
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v2 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
INSERT_STATEMENT = "INSERT INTO v2(rowid, embedding) VALUES(?, ?)"
data_to_insert = [
[1, [-5, 5]],
[2, [-4, 3]],
[3, [-3, 5]],
[4, [ 3, -5]],
[5, [ 4, -3]],
[6, [ 5, -5]],
[7, [ 3, 3]],
[8, [ 3, 4]],
[9, [ 3, 5]],
[10, [ 4, 3]],
[11, [ 4, 4]],
[12, [ 4, 5]],
[13, [ 5, 3]],
[14, [ 5, 4]],
[15, [ 5, 5]],
]
for data in data_to_insert:
print(f"Inserting {data[1]}")
db.execute(INSERT_STATEMENT, [data[0], sqlite_vec.serialize_float32(data[1])])
db.commit()
SELECT_STATEMENT = """
SELECT
rowid,
distance
FROM v2
WHERE embedding MATCH ?
ORDER BY distance
LIMIT 3
"""
print()
print("Finding vectors close to [-4, 4]")
results = db.execute(SELECT_STATEMENT, [sqlite_vec.serialize_float32([-4, 4])]).fetchall()
for result in results:
print(result)
V dnešním posledním demonstračním příkladu je naznačeno, že i hodnota předaná v klauzuli LIMIT je volitelná, resp. že se jedná o parametr, který může být databázi předán při pokládání dotazu:
# Knihovna SQLite s rozšířením SQLite-vec
#
# - připojení k databázi uložené v paměti
# - načtení rozšíření SQLite-vec
# - vytvoření virtuální tabulky
# - naplnění tabulky patnácti vektory
# - nalezení pěti vektorů nejbližích k [4, 4]
import sqlite3
import sqlite_vec
# připojení k databázi uložené v souboru
# v případě potřeby je databáze vytvořena
with sqlite3.connect(":memory:") as db:
# načtení rozšíření SQLite-vec
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
TABLE_STATEMENT = """
CREATE virtual TABLE v2 USING vec0(
embedding float[2]
);
"""
db.execute(TABLE_STATEMENT)
INSERT_STATEMENT = "INSERT INTO v2(rowid, embedding) VALUES(?, ?)"
data_to_insert = [
[1, [-5, 5]],
[2, [-4, 3]],
[3, [-3, 5]],
[4, [ 3, -5]],
[5, [ 4, -3]],
[6, [ 5, -5]],
[7, [ 3, 3]],
[8, [ 3, 4]],
[9, [ 3, 5]],
[10, [ 4, 3]],
[11, [ 4, 4]],
[12, [ 4, 5]],
[13, [ 5, 3]],
[14, [ 5, 4]],
[15, [ 5, 5]],
]
for data in data_to_insert:
print(f"Inserting {data[1]}")
db.execute(INSERT_STATEMENT, [data[0], sqlite_vec.serialize_float32(data[1])])
db.commit()
SELECT_STATEMENT = """
SELECT
rowid,
distance
FROM v2
WHERE embedding MATCH ?
ORDER BY distance
LIMIT ?
"""
print()
print("Finding vectors close to [4, 4]")
results = db.execute(SELECT_STATEMENT, [sqlite_vec.serialize_float32([4, 4]), 5]).fetchall()
for result in results:
print(result)
18. Repositář s demonstračními příklady
Demonstrační příklady, s nimiž jsme se dnes seznámili a které jsou určeny pro Python 3.11 (a libovolnou vyšší verzi Pythonu) a knihovnu SQLite s rozšíření SQLite-vec, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy i na definice formulářů:
19. Předchozí články věnující se problematice vyhledávání podobných vektorů v databázích
Na stránkách Roota jsme se již několikrát setkali s problematikou vyhledávání vektorů v databázích (ať již se jedná o mainstreamové databáze, nebo o databáze specializované). Následují odkazy na tyto články:
- pgvector: vektorová databáze postavená na Postgresu
https://www.root.cz/clanky/pgvector-vektorova-databaze-postavena-na-postgresu/ - Rozšíření PostgreSQL jménem pgvector, embedding a sémantické vyhledávání (1. část)
https://www.root.cz/clanky/rozsireni-postgresql-jmenem-pgvector-embedding-a-semanticke-vyhledavani-1-cast/ - FAISS: knihovna pro rychlé a efektivní vyhledávání podobných vektorů
https://www.root.cz/clanky/faiss-knihovna-pro-rychle-a-efektivni-vyhledavani-podobnych-vektoru/ - FAISS: knihovna pro rychlé a efektivní vyhledávání podobných vektorů (2. část)
https://www.root.cz/clanky/faiss-knihovna-pro-rychle-a-efektivni-vyhledavani-podobnych-vektoru-2-cast/ - Knihovna FAISS a embedding: základ jazykových modelů
https://www.root.cz/clanky/knihovna-faiss-a-embedding-zaklad-jazykovych-modelu/
20. Odkazy na Internetu
- sqlite-vec repository
https://github.com/asg017/sqlite-vec - SQlite-vec
https://builders.mozilla.org/project/sqlite-vec/ - How to use sqlite-vec to store and query vector embeddings
https://dev.to/stephenc222/how-to-use-sqlite-vec-to-store-and-query-vector-embeddings-58mf - SQLite-Vector
https://www.sqlite.ai/sqlite-vector - Vector database
https://en.wikipedia.org/wiki/Vector_database - Nearest neighbor search
https://en.wikipedia.org/wiki/Nearest_neighbor_search#Approximation_methods - RAG – Retrieval-augmented generation
https://en.wikipedia.org/wiki/Retrieval-augmented_generation - pgvector na GitHubu
https://github.com/pgvector/pgvector - Why we replaced Pinecone with PGVector
https://www.confident-ai.com/blog/why-we-replaced-pinecone-with-pgvector - PostgreSQL as VectorDB – Beginner Tutorial
https://www.youtube.com/watch?v=Ff3tJ4pJEa4 - What is a Vector Database? (neobsahuje odpověď na otázku v titulku :-)
https://www.youtube.com/watch?v=t9IDoenf-lo - PGVector: Turn PostgreSQL Into A Vector Database
https://www.youtube.com/watch?v=j1QcPSLj7u0 - Milvus
https://milvus.io/ - Vector Databases simply explained! (Embeddings & Indexes)
https://www.youtube.com/watch?v=dN0lsF2cvm4 - Vector databases are so hot right now. WTF are they?
https://www.youtube.com/watch?v=klTvEwg3oJ4 - Step-by-Step Guide to Installing “pgvector” and Loading Data in PostgreSQL
https://medium.com/@besttechreads/step-by-step-guide-to-installing-pgvector-and-loading-data-in-postgresql-f2cffb5dec43 - Best 17 Vector Databases for 2025
https://lakefs.io/blog/12-vector-databases-2023/ - Top 15 Vector Databases that You Must Try in 2025
https://www.geeksforgeeks.org/top-vector-databases/ - Picking a vector database: a comparison and guide for 2023
https://benchmark.vectorview.ai/vectordbs.html - Top 9 Vector Databases as of Feburary 2025
https://www.shakudo.io/blog/top-9-vector-databases - What is a vector database?
https://www.ibm.com/think/topics/vector-database - SQL injection
https://en.wikipedia.org/wiki/SQL_injection - Cosine similarity
https://en.wikipedia.org/wiki/Cosine_similarity - Hammingova vzdálenost
https://cs.wikipedia.org/wiki/Hammingova_vzd%C3%A1lenost - Jaccard index
https://en.wikipedia.org/wiki/Jaccard_index - Manhattanská metrika
https://cs.wikipedia.org/wiki/Manhattansk%C3%A1_metrika - FAISS (Facebook AI Similarity Search)
https://en.wikipedia.org/wiki/FAISS - FAISS documentation
https://faiss.ai/ - Introduction to Facebook AI Similarity Search (Faiss)
https://www.pinecone.io/learn/series/faiss/faiss-tutorial/ - Faiss: Efficient Similarity Search and Clustering of Dense Vectors
https://medium.com/@pankaj_pandey/faiss-efficient-similarity-search-and-clustering-of-dense-vectors-dace1df1e235 - Cosine Distance vs Dot Product vs Euclidean in vector similarity search
https://medium.com/data-science-collective/cosine-distance-vs-dot-product-vs-euclidean-in-vector-similarity-search-227a6db32edb - F16C
https://en.wikipedia.org/wiki/F16C - FP16 (AVX-512)
https://en.wikipedia.org/wiki/AVX-512#FP16 - Top 8 Vector Databases in 2025: Features, Use Cases, and Comparisons
https://synapsefabric.com/top-8-vector-databases-in-2025-features-use-cases-and-comparisons/ - Is FAISS a Vector Database? Complete Guide
https://mljourney.com/is-faiss-a-vector-database-complete-guide/ - FAISS and sentence-transformers in 5 Minutes
https://www.stephendiehl.com/posts/faiss/ - Sentence Transformer: Quickstart
https://sbert.net/docs/quickstart.html#sentence-transformer - Sentence Transformers: Embeddings, Retrieval, and Reranking
https://pypi.org/project/sentence-transformers/ - uv
https://docs.astral.sh/uv/ - A Gentle Introduction to Retrieval Augmented Generation (RAG)
https://wandb.ai/cosmo3769/RAG/reports/A-Gentle-Introduction-to-Retrieval-Augmented-Generation-RAG---Vmlldzo1MjM4Mjk1 - The Beginner’s Guide to Text Embeddings
https://www.deepset.ai/blog/the-beginners-guide-to-text-embeddings - SQLite: Run-Time Loadable Extensions
https://sqlite.org/loadext.html - SQLite: Load An Extension
https://sqlite.org/c3ref/load_extension.html - The Virtual Table Mechanism Of SQLite
https://sqlite.org/vtab.html
