Benchmarki mierzą dokładnie to, co rzadko zawodzi
Publiczne benchmarki ANN przepuszczają stały zbiór jednorodnych wektorów przez zapytania top-k bez filtrów i raportują recall wobec liczby zapytań na sekundę. To naprawdę przydatne, jeśli piszesz kod indeksu. Do wyboru silnika prawie bezużyteczne, bo recall bez filtrów przy 500 tys. rekordów to akurat ta część, która działa. Popularne silniki mieszczą się tam w kilku punktach od siebie, a reranker przed modelem zaciera resztę różnicy.
Widać za to wszystko, czego benchmark nie mierzył. Filtr trafiający dwa procent zbioru wraca z czterema wynikami zamiast dwudziestu. Numer części znika, bo wektory gęste nie radzą sobie z dokładnymi tokenami. p99 skacze do kilkukrotności mediany w momencie, gdy trzy osoby pytają naraz, a potem w któryś wtorek przeliczenie embeddingów zdejmuje indeks na noc, której nikt nie wpisał do kalendarza.
Jedno rozgraniczenie warto zrobić od razu. Jakość odpowiedzi rozstrzyga się głównie wyżej niż baza, w chunkingu, rankingu i osadzaniu w źródłach, i to rozłożyliśmy osobno w tekście o tym, co pęka w firmowej bazie wiedzy przy 500 tys. rekordów. Ten artykuł jest o warstwie pod spodem: w czym trzymasz wektory i co ta decyzja robi ci w szóstym miesiącu.
Wyszukiwanie z filtrem to miejsce, w którym po cichu giną wyniki
Każde realne zapytanie ma filtr. Spółka, dział, typ dokumentu, data obowiązywania, język, zakres uprawnień. Na demie filtr jest dodatkiem, a na produkcji największym pojedynczym źródłem dwóch skarg, które brzmią na niepowiązane, a nie są: nic nie zwrócił oraz zwrócił dokument innego działu.
Obie naiwne implementacje zawodzą, tylko w przeciwne strony. Post-filtrowanie najpierw robi wyszukiwanie wektorowe, potem odrzuca to, co nie pasuje, i dlatego dokumentacja Weaviate o filtrowaniu zwraca uwagę, że nie da się przewidzieć, ile elementów znajdzie się w wyniku, a restrykcyjny filtr potrafi zostawić pustkę. Pre-filtrowanie wygląda na oczywistą poprawkę, dopóki nie zobaczysz, co robi z grafem: przewodnik Qdrant po filtrowaniu mówi wprost, że nie należy go stosować nad dużymi zbiorami, bo zrywa zbyt wiele połączeń w grafie HNSW i dokładność spada.
Każdy silnik zbudował więc coś pomiędzy i właśnie w tym „pomiędzy” siedzą różnice, które widać dopiero na produkcji.
| Silnik | Jak działa wyszukiwanie z filtrem | Co to znaczy w praktyce |
|---|---|---|
| Qdrant | Filtrowalny indeks dokłada dodatkowe połączenia między punktami spełniającymi filtr; poniżej progu pełnego skanu, domyślnie 10 KB, przechodzi na wyszukiwanie po indeksie payloadu | Wąskie filtry zostają dokładne, ale pola, po których filtrujesz, potrzebują indeksów payloadu, najlepiej założonych przed ingestem |
| Weaviate | Indeks odwrócony buduje listę dozwolonych kandydatów dla HNSW; od v1.34 domyślną strategią jest ACORN, a próg przejścia na skan liniowy stoi przy mniej więcej 15% zbioru | Sensowne domyślne ustawienia, a próg to pokrętło, do którego prędzej czy później nabierzesz zdania |
| Pinecone | Filtrowanie dzieje się wewnątrz usługi; 40 KB filtrowalnych metadanych na rekord, $in i $nin ograniczone do 10 000 wartości | Wystarczy, dopóki filtr uprawnień nie potrzebuje więcej niż 10 000 identyfikatorów; wtedy przeprojektowujesz schemat wokół namespace'ów |
| pgvector | Przy indeksie przybliżonym filtr nakładany jest po przeskanowaniu indeksu; iteracyjne skany indeksu od wersji 0.8.0 skanują dalej, aż znajdzie się dość pasujących wierszy | Filtry, złączenia i wektory w jednym zapytaniu SQL nad realnymi danymi, w zamian za samodzielne strojenie limitów skanu |
| Chroma | where na metadanych plus where_document z $contains na treści | Wystarczy do prostych filtrów; wcześniej i tak uderzy sufit pamięci opisany niżej |
Niezależnie od tego, co silnik robi w środku, najtańsza dokładność bierze się ze zawężenia problemu przed wyszukiwaniem. W bazie z 500 tys.+ rekordów, którą prowadzimy, retrieval najpierw rozpoznaje encję z pytania w realnych tabelach, a dopiero potem szuka semantycznie, z tymi kanonicznymi ID jako ograniczeniem w metadanych. Indeks dostaje dużo mniejszy problem, a filtr jest dokładny, bo pochodzi z zapytania do bazy, nie z miary podobieństwa. Ten ruch działa na każdym silniku z tabeli i dlatego cenimy go wyżej niż sam wybór silnika.
Wektory gęste nie znajdą numeru części
Zapytaj czysty indeks embeddingów o umowę SLA-2024-118, a dostaniesz trzy umowy, które brzmią podobnie. To awaria strukturalna, nie kwestia strojenia: identyfikator niesie znaczenie w znakach, nie w semantyce, i od tego jest wyszukiwanie leksykalne. Każda baza, w której ludzie szukają po indeksie magazynowym, kodzie błędu, numerze faktury czy sygnaturze procedury, potrzebuje BM25 albo wektorów rzadkich obok embeddingów. Silniki nie są tu równe.
Weaviate traktuje to jako pełnoprawny typ zapytania. Wyszukiwanie hybrydowe wystawia parametr alpha, gdzie 0 to czyste słowa kluczowe, a 1 czysty wektor, z Relative Score Fusion jako domyślną metodą łączenia od v1.24. Jedno wywołanie i jedna liczba do strojenia, czyli tyle prostoty, ile w tym temacie da się dostać.
Qdrant składa to z elementów i te elementy są dobre. Zapytania hybrydowe łączą wektory rzadkie i gęste przez etap prefetch, a wyniki scala Reciprocal Rank Fusion albo, od v1.11, Distribution-Based Score Fusion, które normalizuje rozkłady wyników zamiast patrzeć na pozycje. Prefetche można zagnieżdżać, więc wieloetapowy pipeline „pobierz i przerankuj” mieści się w jednym żądaniu.
Pinecone też obsługuje wektory rzadkie, z udokumentowanym limitem 2048 niezerowych wartości na wektor w limitach bazy. pgvector nie ma własnego wyszukiwania leksykalnego i nie potrzebuje, bo siedzi w Postgresie: pełnotekstowe tsvector jest w tej samej bazie, a oba rankingi scalasz w SQL. To więcej kodu niż alpha w Weaviate, ale też wersja, którą zdebugujesz przez EXPLAIN. Chromowe where_document jest filtrem po fragmencie tekstu, nie rankingiem, więc zamiennikiem nie jest.
Dla porządku: baza, którą prowadzimy, łączy dense i BM25, a potem rerankuje scalony zbiór. Reranker jest najdroższym słowem w tym zdaniu i wciąż najtańszą znaną nam poprawką na wyniki trafione tematycznie i bezużyteczne.
Rachunek to w większości rachunek za pamięć
HNSW to graf, po którym się chodzi, a chodzenie po nim z dysku nie jest czymś, czego chcesz. Ekonomia jest więc wszędzie ta sama, a najdosadniej opisuje ją Chroma. Przewodnik po wydajności pojedynczego węzła mówi, że indeks musi siedzieć w pamięci systemowej, że poniżej 2 GB RAM wdrożenia się nie zaleca i że gdy kolekcja przekroczy dostępną pamięć, system zaczyna swapować i szybko staje się bezużyteczny. Jest tam nawet reguła doboru rozmiaru: maksymalna kolekcja w milionach to dostępny RAM w gigabajtach razy 0,245. Zapytania zrównoleglają się do liczby vCPU, a potem ustawiają się w kolejkę i to właśnie tam ucieka p99.
Weaviate zapisuje tę samą zależność jako regułę planowania w dokumentacji o zasobach: zużycie pamięci to mniej więcej dwukrotność objętości wszystkich wektorów. O tym podwojeniu zapomina się dokładnie wtedy, gdy dobiera się maszynę po surowej objętości embeddingów.
Kompresja jest tu główną dźwignią, a Qdrant opisuje jej cenę uczciwie w przewodniku po kwantyzacji: kwantyzacja skalarna daje 4x przy błędzie zwykle poniżej 1%, binarna sięga 32x i nawet 40x przyspieszenia, ale chce danych wysokowymiarowych i wycentrowanych plus przebiegu rescoringu, a produktowa dochodzi do 64x kosztem szybkości, bo liczenie odległości traci ścieżkę SIMD. Weaviate wskazuje na kwantyzację produktową z tego samego powodu. Żadna z nich nie jest darmowa pod względem dokładności i każda jest tańsza niż kolejne 32 GB RAM.
Usługa zarządzana zmienia kształt kosztu, nie jego istnienie. W cenniku Pinecone plan Standard startuje od 50 dolarów minimum miesięcznie, magazyn kosztuje 0,33 dolara za GB na miesiąc, zapisy 4 do 4,50 dolara za milion jednostek, odczyty 16 do 18 dolarów za milion, zależnie od chmury i regionu, a transfer wychodzący 0,10 dolara za GB. Pamięci nie kupujesz w ogóle. Dostajesz za to rachunek rosnący z liczbą zapytań, czyli odwrotną ekspozycję niż przy własnej maszynie: jedno skaluje się z tym, ile trzymasz, drugie z tym, ile pytasz. Asystent obsługi odpowiadający na 50 tys. pytań miesięcznie i baza wewnętrzna z 500 pytaniami mają tu zupełnie inne odpowiedzi, a rachunek jest ten sam co przy koszcie zbudowania i utrzymania systemu AI.
Do tego dochodzi rzecz, o którą w polskiej firmie i tak zapyta dział prawny: gdzie fizycznie leżą wektory. Embedding fragmentu umowy albo akt pracowniczych to nadal dane osobowe, więc region hostingu, powierzenie przetwarzania i transfer poza EOG wracają jako pytania z RODO. Qdrant i pgvector na własnej infrastrukturze zamykają ten wątek najprościej, bo dane nie opuszczają maszyny, którą już masz w umowie. Przy usłudze zarządzanej to kwestia wyboru regionu i podpisanego DPA, a nie założenia.
Zaplanuj dzień, w którym zmienisz model embeddingów
Każdy zbiór prędzej czy później zostaje przeliczony od nowa. Wychodzi lepszy model, zmienia się liczba wymiarów, poprawiasz chunking, dochodzi kolejny język. To nie jest migracja, którą robi się jednym ALTER - to pełna przebudowa indeksu, a silniki różnią się tego dnia bardziej niż na jakimkolwiek wykresie recallu.
pgvector jest w tym najbardziej szczery, bo ból należy do ciebie. README projektu mówi wprost, że HNSW daje lepszą wydajność zapytań niż IVFFlat, ale buduje się wolniej i zużywa więcej pamięci, zaleca ustawienie maintenance_work_mem na rząd 8 GB i ostrzega osobnym komunikatem, gdy graf przestaje się mieścić, formułując to jako brak miejsca w maintenance_work_mem po 100 000 krotek. Znaczenie mają też limity wymiarów: typ vector indeksuje do 2000 wymiarów, a halfvec do 4000, więc model o 3072 wymiarach wymusza decyzję o połowicznej precyzji na starcie, nie po fakcie.
Usługi zarządzane przenoszą pracę, ale ją fakturują. Przebudowa w Pinecone to ponowny upsert całego zbioru, czyli jednostki zapisu po 4 do 4,50 dolara za milion w planie Standard, plus 0,25 dolara za GB, jeśli idziesz przez import. Przy jednej przebudowie to tanio, ale warto o tym wiedzieć, zanim zrobisz czwartą.
Wzorzec, który przeżywa zderzenie z produkcją, jest wszędzie ten sam. Zbuduj nowy indeks obok starego, w innej kolekcji albo namespace, pisz przez chwilę do obu, puść zbiór ewaluacyjny na jednym i drugim, potem przełącz alias. Silniki różni to, jak drogi jest ten czas nakładania się: przez kilka godzin płacisz za dwie kopie wszystkiego, czyli dokładnie wtedy arytmetyka pamięci z poprzedniej sekcji przestaje być teoretyczna.
Co uruchomilibyśmy naprawdę: Pinecone, Weaviate, Qdrant, Chroma czy pgvector
Zwycięzcy nie ma i to jest niesatysfakcjonująca, ale poprawna odpowiedź. Są za to obciążenia i każdy z tych silników jest właściwym wyborem przy jednym z nich. W tabeli najważniejsza jest ostatnia kolumna, bo to z tym kompromisem będziesz żyć.
| Obciążenie | Co byśmy uruchomili | Dlaczego | Na co się godzisz |
|---|---|---|---|
| Do kilku milionów fragmentów, dane już w Postgresie | pgvector | Jedna baza, jeden backup, jeden model dostępu; filtry i złączenia w tym samym zapytaniu co wektory | Strojenie limitów skanu i samodzielne złożenie wyszukiwania leksykalnego |
| Dużo filtrowania po metadanych, self-hosting, dane zostają u ciebie | Qdrant | Filtrowalny indeks i opcje kwantyzacji są zrobione pod wąskie filtry nad dużymi zbiorami | Utrzymujesz go sam: indeksy payloadu, snapshoty, aktualizacje |
| Wyszukiwanie hybrydowe bez budowania go od zera | Weaviate | Hybryda z alpha i scalaniem wyników jest pełnoprawnym zapytaniem, a ACORN domyślnie ogarnia filtry | Więcej pojęć do nauczenia, a planowanie pamięci i tak zostaje po twojej stronie |
| Mały zespół, brak budżetu na utrzymanie, nierówny ruch | Pinecone | Nie ma czego provisionować ani trzymać przy życiu; pojemność to problem kogoś innego | Koszty rosnące z liczbą odczytów i limity, których nie zmienisz |
| Prototyp, laptop, jedna maszyna | Chroma | Najkrótsza droga od notebooka do działającego retrievalu | Sufit pamięci, w który wejdziesz, i migracja później |
Nasze nastawienie jest jawne i warto je zważyć: domyślnie sięgamy po pgvector, a system nad 500 tys.+ rekordów działa na PostgreSQL z pgvectorem, obok Pythona, LangChaina i FastAPI. Przy tej skali to nie baza była wąskim gardłem. Za każdym razem była nim jakość retrievalu. To jest ogólny wniosek, a nie zachęta do kopiowania naszego stacku: druga usługa to realny ciężar operacyjny, więc powinna być kupiona konkretnym ograniczeniem, a nie przeczuciem, że poważna opcja musi być tą wyspecjalizowaną. Ten sam spór na poziomie całych systemów, nie indeksów, to pytanie o budowę kontra gotowe narzędzie.
Pięć testów, zanim się na coś zdecydujesz
Żaden z nich nie wymaga rozmowy z dostawcą ani kwartału na proof of concept. Każdy celuje w jeden z trybów awarii opisanych wyżej, więc zły wynik wskazuje poprawkę, a nie nastrój. Puszczaj je na własnym zbiorze, nie na próbce, bo to właśnie próbka sprawia, że wszystkie silniki wyglądają identycznie.
Wgraj dziesięć razy więcej, niż masz. W razie potrzeby zduplikuj zbiór z lekko zaburzonymi wektorami. Nie mierzysz recallu, tylko patrzysz, co robi pamięć i gdzie czas budowy przestaje być liniowy. Jeśli przy dziesięciokrotności indeks już nie mieści się na maszynie, właśnie poznałeś swój realny horyzont planowania.
Zapytaj z najwęższym filtrem, jaki naprawdę stosujesz. Wybierz ten trafiający poniżej procenta zbioru: jedna spółka, jeden typ dokumentu w wąskim zakresie dat. Sprawdź, ile wyników wraca i czy właściwy jest wśród nich. Pusty albo za krótki wynik to najczęstsza produkcyjna niespodzianka w tym temacie.
Poszukaj czegoś po identyfikatorze. Weź numer części, sygnaturę umowy albo ID procedury, który występuje dokładnie raz, i o niego zapytaj. Konfiguracja oparta wyłącznie na wektorach zwróci tematycznych sąsiadów z wysokim wynikiem podobieństwa i dokładnie tak ta awaria ukrywa się miesiącami.
Mierz p95 i p99 pod równoległym obciążeniem, nigdy medianę. Dokumentacja Chromy opisuje zapytania zrównoleglające się do liczby vCPU, a potem wchodzące w kolejkę i liniowy wzrost opóźnień; każdy silnik ma swoją wersję tej krzywej. Mediana przy jednym kliencie nie mówi nic o poniedziałkowym poranku, gdy wszyscy otwierają asystenta naraz.
Przećwicz przebudowę indeksu. Zbuduj go od zera na innym modelu embeddingów i zmierz całość, razem z oknem podwójnego magazynowania. Zrób to, kiedy nic od tego nie zależy, bo alternatywą jest robienie tego pierwszy raz pod presją i z terminem wyłączenia starego modelu.
Dwa popołudnia takiej pracy powiedzą ci więcej niż jakikolwiek tekst porównawczy, ten również, bo lecą na twoich filtrach i twoich identyfikatorach. Jeśli wyniki wskażą na jakość retrievalu, a nie na magazyn, bardziej przyda się tekst o chunkingu, rankingu i osadzaniu w źródłach. A jeśli odpowiedzi wymagają bieżącego stanu systemów, a nie dokumentów, to już inny problem: podłączenie agenta do danych przez narzędzia zamiast przez indeks.
Budujemy i utrzymujemy takie systemy, więc odpowiednio zważ to nastawienie. Pięć testów nie kosztuje ani złotówki, ani żadnego dostawcy. Jeśli wolisz drugą parę oczu na to, które ograniczenie uderzy w konkretną bazę jako pierwsze, krótki audyt to niskostawkowy sposób, żeby ją mieć.
FAQ
Która baza wektorowa jest najlepsza w 2026?
Nie ma jednej odpowiedzi, a każdy tekst, który ją daje, coś sprzedaje. Silniki są na tyle blisko siebie w recallu bez filtrów, że decyzja zapada gdzie indziej: jak zachowuje się wyszukiwanie przy wąskich filtrach, czy wyszukiwanie leksykalne jest wbudowane czy do złożenia, czy koszt rośnie z objętością danych czy z liczbą zapytań i ile pracy utrzymaniowej jesteś w stanie wchłonąć. Wybieraj pod swoje obciążenie i zaczynaj od tego, co już utrzymujesz, a nie od tabelki funkcji.
Kiedy pgvector przestaje wystarczać?
Później, niż większość zakłada. Ograniczenia, które realnie wymuszają zmianę, są konkretne: stale wysoka równoległość zapytań, przy której wąskim gardłem staje się obsługa połączeń w Postgresie; filtry tak wąskie, że nakładanie ich po skanie gubi wyniki nawet z iteracyjnymi skanami indeksu; liczba wymiarów powyżej tego, co obsługują typy indeksowane; albo zbiór na tyle duży, że czas budowy i pamięć czynią rutynową przebudowę niepraktyczną. Jeśli żadne z tego dziś nie zachodzi, druga baza to infrastruktura do utrzymywania bez zysku na jakości odpowiedzi.
Czy Chroma nadaje się na produkcję?
Zależy wyłącznie od rozmiaru zbioru, a Chroma (ChromaDB) jest w tej sprawie nietypowo szczera. Jej własna dokumentacja wydajności pojedynczego węzła mówi, że indeks HNSW musi siedzieć w pamięci systemowej, że poniżej 2 GB RAM wdrożenia się nie zaleca, i że po przekroczeniu dostępnej pamięci system zaczyna swapować i staje się bezużyteczny, a nie tylko wolniejszy. Ich reguła doboru rozmiaru mówi, że maksymalna kolekcja w milionach to dostępny RAM w gigabajtach razy 0,245. Poniżej tego sufitu działa; ryzykiem jest wzrost, który przekroczy go bez ostrzeżenia.
Czy zmiana bazy wektorowej oznacza przeliczenie wszystkich embeddingów?
Niekoniecznie. Embeddingi to tablice liczb, więc jeśli trzymasz źródłowe fragmenty i wektory, większość migracji to eksport i masowy import. Od nowa robi się budowa indeksu oraz struktury specyficzne dla silnika, jak indeksy payloadu czy reprezentacje rzadkie. Pełne przeliczenie wymusza dopiero zmiana modelu embeddingów, a to osobne wydarzenie i warto trzymać je osobno w planie, bo te dwie rzeczy często zlepia się w jeden straszny projekt, choć nie muszą nim być.
- 10 sierpnia 2026Publikacja.