## Embeddingi i wyszukiwanie wektorowe

*Kontekst i wiedza*

*Ostatnia zmiana: 28 września 2026*

Model embeddingowy zamienia cały fragment tekstu w jeden wektor tak, żeby teksty o podobnym znaczeniu dostawały bliskie wektory. Na tym stoi wyszukiwanie wektorowe w RAG: pytanie też staje się wektorem, a baza zwraca fragmenty o najbliższych wektorach, nawet bez wspólnych słów.

**Po ludzku:** Mapa, na której każdy fragment tekstu ma swoją pinezkę, a teksty o podobnej treści leżą blisko siebie. Szukanie to wbicie pinezki pytania i zebranie najbliższych sąsiadów. Numer umowy prawie nie przesuwa pinezki, więc umowy 48213 i 48231 leżą niemal w tym samym miejscu.

*Interaktywny widżet na stronie: Wybierz pytanie albo wpisz własne i zobacz, który fragment każda metoda stawia na górze.*

### Jeden wektor na cały fragment

- Model embeddingowy, zwykle transformer, liczy wektor dla każdego tokena i łączy je w jeden: uśredniając (mean pooling) albo, w embedderach zbudowanych na LLM-ie dekoderowym, jak Qwen3-Embedding (2025), biorąc wektor ostatniego tokena. Wynik ma stałą długość, zwykle od kilkuset do kilku tysięcy liczb, niezależnie od długości tekstu. To co innego niż wektory wewnątrz LLM-a z tematu „Wektory i macierze”: tam każdy token ma własny wektor, a model uczy się przewidywać następny token, nie porównywać tekstów.
- Model uczy się kontrastowo, na parach: pytanie i fragment z odpowiedzią mają dostać bliskie wektory, a pytanie i inne fragmenty z tego samego batcha odległe. „Blisko” znaczy więc „mówi o tym samym albo na to odpowiada”, w granicach tego, co było w danych treningowych.
- Bliskość mierzy się cosinusem kąta między wektorami. Jeśli wektory mają długość 1 (część modeli tak je zwraca, w pozostałych normalizujesz sam), cosinus to zwykły iloczyn skalarny, a ranking po odległości euklidesowej wychodzi identyczny.

### Szukanie w milionach wektorów

- Wyszukiwanie dokładne (brute force) porównuje pytanie z każdym wektorem. Rozmiar indeksu to liczba wektorów × wymiary × bajty, np. 10 mln fragmentów × 1024 wymiary × 4 B (float32) ≈ 41 GB, i każde zapytanie czyta indeks w całości. Przy dziesiątkach tysięcy fragmentów to milisekundy i zero zgubionych wyników, przy milionach i dużym ruchu za wolno i za drogo.
- ANN (approximate nearest neighbours) sprawdza tylko ułamek wektorów, za cenę czasem zgubionego sąsiada. HNSW buduje wielowarstwowy graf „kto jest blisko kogo” i schodzi po nim zachłannie w stronę pytania: szybki i dokładny, ale dla niskiego opóźnienia wektory i graf siedzą w RAM. DiskANN (2019) trzyma w RAM tylko skompresowane wektory, a graf z pełnymi wektorami na SSD: w artykule to miliard wektorów na jednej maszynie z 64 GB RAM, poniżej 3 ms na zapytanie. IVF dzieli wektory na klastry (k-means) i przeszukuje tylko kilka najbliższych: mniej pamięci, zwłaszcza z kompresją, zwykle niższy recall przy tej samej szybkości. Kompromis stroisz parametrem, `efSearch` w HNSW albo `nprobe` w IVF, i mierzysz recall względem brute force na próbce zapytań.
- Pamięć zmniejsza kwantyzacja wektorów: int8 daje 4 razy mniej, binarna (1 bit na wymiar) 32 razy mniej, czyli zamiast 41 GB ok. 1,3 GB. W teście Hugging Face same wektory binarne zachowały ok. 92,5% jakości wyszukiwania, a przeliczenie czołówki pełnym wektorem pytania względem tych samych wektorów binarnych (rescoring) podniosło to do ok. 96%, bez dodatkowej pamięci. Liczby zależą od modelu.
- Drugi sposób to mniej wymiarów. Modele trenowane metodą Matryoshka trzymają najważniejszą informację w pierwszych wymiarach, więc zapisany wektor można przyciąć i znormalizować ponownie, bez przeliczania korpusu. OpenAI podaje, że text-embedding-3-large przycięty z 3072 do 256 wymiarów wypada w benchmarku MTEB lepiej niż starszy ada-002 z 1536.

### Słabe punkty, wyszukiwanie hybrydowe i reranking

- Wektor oddaje ogólny sens, a gubi szczegóły. „Umowa 48213” i „umowa 48231” to dla niego prawie to samo: umowa z jakimś numerem. Podobnie kody, liczby, nazwy produktów, których model nie widział w treningu, i firmowy żargon. „Praca zdalna wymaga zgody” i „nie wymaga zgody” też dostają prawie ten sam wektor: w benchmarku NevIR (2023) większość modeli wyszukiwania, także najnowocześniejszych, szeregowała dokumenty różniące się tylko przeczeniem nie lepiej niż losowo, a najlepiej wypadły cross-encodery. Powtórzenie badania z 2025 r. pokazało, że jeszcze lepiej radzą sobie rerankery LLM oceniające całą listę naraz, choć wciąż gorzej niż ludzie.
- Wyszukiwanie wektorowe zawsze zwraca k wyników, także gdy w bazie nie ma odpowiedzi. Stały próg podobieństwa przeniesiony z innego modelu nie zadziała, bo skala zależy od modelu: w multilingual-e5 wyniki skupiają się między 0,7 a 1,0. Próg dobierasz na własnych danych, a pewniejszym sygnałem jest wynik rerankera.
- Dlatego standardem jest hybryda: BM25, czyli klasyczny ranking po wspólnych słowach ważonych ich rzadkością, plus wektory. Listy scala się np. przez RRF (reciprocal rank fusion): dokument dostaje sumę 1/(60 + miejsce) z każdej listy. RRF patrzy tylko na miejsca, więc nie trzeba godzić skal BM25 i cosinusa. Po polsku BM25 potrzebuje lematyzacji albo stemmera, inaczej „umowa” i „umowy” to różne słowa.
- Na końcu reranker, zwykle cross-encoder: czyta pytanie i fragment razem, więc widzi negację i szczegóły, których nie widać w dwóch osobno policzonych wektorach. Jest za wolny na cały korpus, więc porządkuje tylko czołówkę, np. 50–100 kandydatów. W teście Anthropic (contextual retrieval, 2024) dodanie kontekstu do fragmentów razem z BM25 obniżyło odsetek trafnych fragmentów brakujących w top 20 (1 − recall@20) z 5,7% do 2,9%, a reranking do 1,9%. Szerzej w temacie „RAG”.
- Techniki, które omijają słabości jednego wektora na fragment (HyDE, pytania generowane dla fragmentów, multi-query, small-to-big, late interaction w stylu ColBERT), opisuje sekcja „Sztuczki po stronie zapytania i indeksu” w temacie „RAG”.

### Decyzje przy budowie indeksu

- Chunking decyduje, co znaczy wektor. Długi fragment uśrednia kilka tematów i nie pasuje dobrze do żadnego pytania. Krótki gubi kontekst: „w tym okresie przysługuje…”, ale w jakim? Pomaga dopisanie tytułu dokumentu i sekcji przed embeddingiem. Tekst dłuższy niż limit modelu jest obcinany: w multilingual-e5 po 512 tokenach, w Qwen3-Embedding po 32 tys.
- Pytanie i dokument to różne teksty: kilka słów kontra akapit. Część modeli oczekuje prefiksów, np. `query: ` i `passage: ` w e5, albo parametru typu wejścia w API. Pominięty albo zamieniony prefiks nie rzuca błędu, tylko po cichu psuje ranking.
- Dla polskiego wybierasz model wielojęzyczny albo polski i sprawdzasz go na polskich pytaniach, nie tylko w rankingu MTEB. Benchmark PIRB (41 zadań wyszukiwania po polsku) porównuje ponad 20 modeli i pokazuje, że hybryda z wyszukiwaniem po słowach poprawia nawet najlepsze modele wektorowe.
- Zmiana modelu embeddingowego to przeliczenie całego korpusu, bo wektory z dwóch modeli są nieporównywalne, nawet przy tej samej liczbie wymiarów. Przy każdym wektorze trzymasz tekst źródłowy i wersję modelu, nowy indeks budujesz obok starego i przełączasz, gdy wygra na twoim zestawie.
- Wyszukiwanie oceniasz osobno od odpowiedzi, na zestawie pytań z oznaczonymi trafnymi fragmentami. Recall@k: jaka część trafnych fragmentów jest w pierwszych k wynikach. MRR: średnia z 1/miejsce pierwszego trafnego, więc 1. miejsce daje 1, a 3. daje 0,33. Wartość k ustawiasz na tyle fragmentów, ile naprawdę wkładasz do promptu. Zobacz „Ewaluacje”.

### Sprawdź się

**Pytanie:** Jak działa wyszukiwanie wektorowe i czemu samo nie wystarcza?

**Krótka odpowiedź:** Model embeddingowy, wytrenowany tak, by teksty o podobnym znaczeniu leżały blisko siebie, zamienia cały fragment w jeden wektor. Wektor pytania liczy się tym samym modelem, a wyszukiwanie zwraca najbliższe wektory po cosinusie. Przy milionach fragmentów indeks przybliżony, np. HNSW, odpowiada w milisekundach kosztem odrobiny recall, a zajmowaną przez niego pamięć zmniejsza kwantyzacja. Wektory łapią parafrazy, ale gubią identyfikatory, liczby, rzadkie nazwy i negację, a do tego zawsze coś zwracają. Dlatego łącz je z BM25 przez RRF, czołówkę porządkuj cross-encoderem, a recall@k mierz osobno od jakości odpowiedzi.

### Pytania pogłębiające

- **Zmieniasz model embeddingowy. Co z indeksem?** Przeliczasz cały korpus, bo wektory z różnych modeli są nieporównywalne, nawet przy tej samej liczbie wymiarów. Nowy indeks budujesz obok starego, porównujesz recall@k na tym samym zestawie pytań i dopiero wtedy przełączasz ruch. Dlatego przy wektorach trzymasz tekst źródłowy i wersję modelu.
- **HNSW czy IVF?** HNSW daje wysoki recall przy niskim opóźnieniu, o ile wektory i graf mieszczą się w RAM. IVF dzieli wektory na klastry i przeszukuje kilka z nich. Z kompresją PQ mieści dużo więcej wektorów w tej samej pamięci, kosztem recall, który odzyskujesz większym nprobe albo przeliczeniem czołówki na pełnych wektorach.
- **Jak odrzucić fragmenty, gdy w bazie nie ma odpowiedzi?** Próg cosinusa dobierasz na własnym zestawie, bo skala zależy od modelu. Do zestawu włączasz też pytania bez odpowiedzi w bazie. Pewniejszym sygnałem jest wynik rerankera. Model w prompcie dostaje wprost zgodę na „nie ma tego w źródłach”.
- **Jak łączysz wyszukiwanie wektorowe z filtrami, np. uprawnieniami albo datą?** Filtr nałożony po wyszukiwaniu wycina wyniki z top-k, więc przy wąskim filtrze zostaje ich za mało albo zero. Lepiej filtrować w trakcie przeszukiwania indeksu, co obsługuje wiele baz wektorowych, albo trzymać osobne indeksy, np. per klient. Uprawnienia egzekwujesz przy wyszukiwaniu, nie w prompcie.
- **Recall@10 jest wysoki, a odpowiedzi i tak słabe. Gdzie szukasz?** Najpierw, czy trafny fragment nie ląduje na 8.–10. miejscu, gdy do promptu idą tylko trzy pierwsze: wtedy pomaga reranker. Potem, czy fragment po wycięciu z dokumentu w ogóle zawiera odpowiedź (chunking). Na końcu, czy model jej nie przekręca, co mierzy już ewaluacja generowania.

### Źródła

- [Malkov, Yashunin: Efficient and robust approximate nearest neighbor search using HNSW graphs](https://arxiv.org/abs/1603.09320)
- [Cormack, Clarke, Büttcher: Reciprocal Rank Fusion (SIGIR 2009)](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf)
- [Anthropic: Introducing Contextual Retrieval (2024)](https://www.anthropic.com/news/contextual-retrieval)
- [Hugging Face: Binary and Scalar Embedding Quantization](https://huggingface.co/blog/embedding-quantization)

Strona interaktywna: https://howaiworks.dev/pl/embeddingi/
