Menu
About me Kontakt

Vector Databases - A Quick Introduction to Working with Them (film, 15m)

Jakub 'unknow' Mrugalski from UW-TEAM.org demonstrates in his video that using vector databases does not require advanced technical knowledge. He emphasizes that mastering two essential skills, launching a Docker container and communicating with a simple JSON API, is sufficient. Within a few minutes, he can show how to effectively utilize a local database called QUADRANT, which is particularly relevant for application development. They begin by launching the Docker container with the QUADRANT database while making sure to prepare a directory for data storage.

Once the container is up and running, Jakub moves to the user interface, showcasing the key features of the database. He starts by creating a new database collection using the PUT method, which is intuitive, and explains how to add new vectors to the database. He clarifies the differences between various methods and their applications, as well as stressing the importance of the correct format for the added vectors. All of this is presented in the context of using the most popular comparison algorithms.

Continuing, Jakub demonstrates how to create the first collection and how to import data into the quadrant from a text file. The episode contains essential information about converting text into vectors and how to handle API responses in PHP. Using the KWADRANT.PHP and EMBEDDING.PHP libraries, viewers can easily implement functionality in their projects. Jakub also explains where to obtain the necessary parameters for AI models that will generate embeddings, a critical aspect when working with vector databases.

Concluding the presentation, Jakub shows how to query the database using natural language. This is an important step as it allows interaction with the database as if speaking to a person, eliminating the need for complex querying. He emphasizes that even with grammatical mistakes or typos, the database still understands the query and retrieves the relevant answer. This showcases the power of vector databases, which respond semantically, rather than relying solely on literal searching.

At the end, Jakub invites viewers to join the AIDevs training, where they will learn not only how to communicate with vector databases but also other practical programming techniques. At the time of writing this article, the video has 9011 views and 391 likes, indicating a strong interest in the topic. Jakub combines theory with practical examples, making complex topics related to modern databases much easier to understand.

Toggle timeline summary

  • 00:00 Introduction to vector databases and their usability without deep understanding.
  • 00:05 Comparison of using vector databases to using SQL databases without knowing their internal workings.
  • 00:11 The learning process requires two key skills: running a Docker container and interacting with a JSON API.
  • 00:19 Promised demonstration of setting up a local database in QUADRANT using Docker.
  • 00:27 Creating a directory for the QUADRANT database to store data.
  • 00:41 Docker run command with options to run QUADRANT in the background.
  • 01:16 Checking if the QUADRANT database is running and accessible via the defined port.
  • 01:31 Exploring the QUADRANT web UI, observing collections and vector data.
  • 01:47 Instructions to create a new collection using the PUT method.
  • 02:04 Adding new vectors to the database through a structured API call.
  • 02:41 Search operation setup using POST method with parameters for vector matching.
  • 03:18 Creating a new collection named 'Knowledge' with specified parameters.
  • 04:08 Using PHP for handling requests to create and manage collections.
  • 06:11 Inserting records into the vector database from a prepared text file.
  • 10:12 Successfully queried the database with natural language and received relevant responses.
  • 13:15 Highlighting the semantic nature of vector databases compared to lexical searching.
  • 13:27 Invitation to a training program on working with vector databases and building agents.

Transcription

Jest jedna dobra wiadomość, wcale nie musisz wiedzieć, jak bazy wektorowe działają pod spodem, aby z nich korzystać. Podobnie jak, nie wiesz na przykład, jak działają algorytmy, załóżmy, w Postgresie, MySQL-u czy Oracle-u. W takim razie, jak się tego nauczyć? Potrzebujesz tylko znaleźć dwie rzeczy. Pierwsza rzecz to jest, jak odpalić kontener dockerowy, a druga rzecz, jak porozumiewać się z prostym API JSON-owym. I jeżeli to upanujesz, to w zasadzie jesteś w stanie korzystać z baz wektorowych. A ja chcę Ci pokazać, jak to zrobić dosłownie w kilka minut. To co? Idziemy do terminala. Wykorzystamy do tego celu lokalną bazę danych, zwaną QUADRANT. Pisze się to QUDRANT. QUADRANT-a uruchomimy jako obraz dockerowy i gdzieś on musi trzymać dane. Chciałbym, aby trzymał to w katalogu, więc założę sobie tutaj katalog mkdir QUADRANT, podaję ENTER, a następnie uruchamiam dockera. Standardowo DOCKER RUN. Chcę, żeby to działało w tle, więc MINUS D. A następnie wystawiam porty. PORT 6333, to jest standardowy port QUADRANT-a. I następnie podaję, co chcę zamontować jako volumen. Aktualny katalog, łamany przez QUADRANT. I jest to ten katalog, który przed chwilą właśnie założyłem. Tam będą trzymane dane. Muszę to podmontować do aplikacji, do SLASH QUADRANT, łamane przez STORAGE. Tam po prostu ta nasza baza danych trzyma swoje dane związane z wektorami. I teraz podaję nazwę obrazu, czyli to jest QUADRANT, łamane przez QUADRANT. To powinno być OK, jeżeli się nie pomyliłem. Naciskam tylko ENTER i rozpoczyna się ściąganie obrazu, ponieważ nie mam go lokalnie na dysku zainstalowanego. Poczekamy chwilę. I mamy swoją bazę danych uruchomioną. Sprawdźmy tylko, czy na pewno wszystko działa. DOCKER PS, tak mamy wystawione, jest OK. I widzę tutaj PORT 633. Jest on jak najbardziej dostępny, a znajduje się na nim WEB UI do zarządzania bazą danych. Będziemy tu widzieć wszystkie nasze kolekcje, czyli bazę danych, jak i wektory, czyli rekordy w nich znajdujące się. Jest tam też mini dokumentacja do API, która pomoże nam rozpocząć pracę z QUADRANTem. Klikamy na QUICK START i widzimy tutaj kilka możliwości. Na początku musimy założyć kolekcję, czyli bazę danych. I robić to bardzo prosto metodą PUT. Wysyłamy sobie do SLASH COLLECTIONS. I ta nazwa na końcu, TEST COLLECTION, to jest po prostu nazwa bazy danych. Rozmiar kolekcji, co to jest, potem powiem. Oraz metoda porównywania dystansów. Tutaj domyślna jest DOT, my użyjemy czegoś innego. Mamy tutaj już dodawanie nowych wektorów, a więc nowych rekordów do bazy. To podobnie jak przed chwilą, realizuje się metodą PUT. I wysyłamy tutaj zapytanie do takiego ENDPOINTA. Tutaj co jest ważne. SLASH COLLECTIONS i następnie nazwa kolekcji oraz ENDPOINT POINTS. Tam dodajemy nowe punkty. Ten nowy punkt musi mieć IDK, to jest dowolny ciąg znaków. Musi mieć wektor, ten właściwy, co tam przechowujemy w bazie danych. Oraz może mieć, a nie musi, payloada. Tam na końcu jest podane. Zwróć uwagę, że tutaj mamy 4. I tutaj też jest wektor o rozmiarze 4. Więc to się musi wszystko zgadzać. Te payloady to są po prostu dane tekstowe, które opisują to coś, co gromadzisz w bazie danych. Nie musi to być wcale wypełnione. I na końcu mamy wysługiwanie danych. To się robi trochę inaczej, ale tutaj używamy metody POST. I mamy nowego endpointa, który bardzo podobnie wygląda. Czyli SLASH COLLECTIONS, nazwa kolekcji, a następnie SLASH POINTS, SEARCH. Łatwo to zapamiętać, nie jest to jakoś skomplikowane. I po czym szukamy? Po wektorze. Tu jest bardzo ważne, żeby ten wektor miał dokładnie taki rozmiar, taki zdefiniowany. I limit, ile rekordów chcemy dostać i czy chcemy pobrać payloada, czy też nie. Bo nie zawsze te payloady są potrzebne. To jest ten payload, on będzie zwrócony w ramach wyszukiwania, jeśli ustawimy sobie WITH PAYLOAD na TRUE. No to co? Wiemy wszystko, jak powinno działać. Kolekcji aktualnie nie mamy, są puste. Więc utwórzmy sobie nową kolekcję. Mam nadzieję, że wybaczysz mi, ale posłuchę się językiem PHP, dlatego że wiele, wiele, naście lat pracowałem jako programista tego języka. Więc z tym jest najprościej. Mam tutaj dwie biblioteki. KWADRANT.PHP oraz EMBEDDING.PHP. KWADRANT.PHP, zobaczmy sobie jak to wygląda. To jest bardzo, bardzo prosty klient HTTP, który na lokalny host, w ramach funkcji REQUEST, za pomocą KURLA, zwanego CURL, do lokalnego serwera poznawanego na dockerze, wysyła REQUESTY do API. Do slash collection, slash wiedza. Wiedza to jest nazwa mojej kolekcji. I tam dodatkowe parametry, np. endpoint jest doklejany na końcu. Wołając więc tą funkcję, definiujemy jaką metodę chcemy użyć. Czy to jest POST, czy to jest PUT. Jakie dane chcemy wysłać. Te dane oczywiście pakowane są w JSON-a. Ja to piszę w PHP, ale ty sobie to przepiszesz np. na PYTHON-a, na JS-a, na cokolwiek tam chcesz. I ta funkcja zwraca po prostu obiekt, który jest zwrócony przez serwer KWADRANTA, który jest też JSON-em. Tak to wygląda. Pora na założenie pierwszej kolekcji. Oczywiście zrobimy to za pomocą funkcji REQUEST. I jej wynik zapiszemy do zmiennej RET. Funkcja REQUEST, metoda PUT, tak jak w dokumentacji. To co widzisz tutaj, to są podpowiedzi z Copilota. Czasami będą one słuszne, czasami niesłuszne. Tutaj napiszemy sobie ręcznie po prostu metoda PUT. A następnie nasza tablica, która będzie zawierała dane, jakie chcemy przesłać. Ta tablica będzie zamieniona na JSON-a, więc mogę sobie to tak zapisać. Musi tam być takie coś jak VECTORS. I w ramach VECTORS podajemy, jakie parametry chcemy mieć w ramach tej bazy danych. Dwa parametry są obowiązkowe. Przede wszystkim rozmiar. Czyli jakiego rozmiaru, ilu wymiarowy wektor będzie tutaj wsadzany domyślnie. Oraz jaką funkcję do porównywania wektorów będziemy używać. I skąd mamy wziąć te informacje? SIZE. SIZE to jest rozmiar wektora. I jest on zależny od tego, z jakiego modelu AI korzystasz do generowania embeddingów. Ja wykorzystuję text Embedding Ada 2 od OpenAI. I to jest zawsze 1536 wymiarów. Jeżeli chodzi o funkcję dystansu, to nie musisz tego szczególnie rozumieć. Wystarczy zapamiętać, że pracując z LLM-ami, zwykle stosujemy jedną funkcję porównania. Jest to funkcja cosinusa, cosin. I tak też użyjemy i tym razem. I teraz uruchommy sobie to. Tylko wcześniej zdefiniuję tutaj, aby wyprintowało mi z formatowanej wersji, wynik działania tej funkcji. Abym wiedział, co tam się potem stało. Uruchamiam. I widzę tutaj, no wszystko się udało. Czy mamy RESULT TRUE, status OK. Sprawdźmy w webowym interfejsie, czy naprawdę jest OK. Odświeżam. I widzę, że tak. Pojawiła się nowa kolekcja o nazwie Wiedza. I mamy tutaj pustą kolekcję. Tylko skąd on wiedział, że trzeba Wiedzę akurat założyć? Przypomnę Ci, jak wyglądał ten nasz kod rant.php. I tutaj mamy tą nazwę kolekcji zdefiniowaną na stałe w kodzie. Oczywiście warto byłoby to sparametryzować, no ale ja to wpisałem na stałe. OK. Sprawdzamy tutaj jeszcze informacje. Punktów nie ma żadnych, dlatego że żadnych nowych wektorów nie dodaliśmy. Dodajmy zatem coś do naszej bazy. Mam przygotowany specjalny plik z informacjami, które chciałbym zaindeksować. Nazywa się on Baza.txt. Otworzę go teraz. Tab Edit Baza.txt. I tu mamy kilka informacji. Mamy informacje np. jak ma na imię pies Adama, gdzie ja mieszkam, co Mateusz lubi robić i informacje na temat różnych. Są też wpisy, które mają tylko zaśmiecić bazę danych i chciałbym je wszystkie zaindeksować. Oczywiście na początku muszę w php odczytać sobie ten plik. Skasuję to, co tutaj zakładam na bazę danych, bo nie jest nam to do niczego potrzebne. A następnie do zmiennej data załaduję plik o nazwie Baza.txt i tutaj Copilot podpowiada nam, że powinniśmy użyć jeszcze przełączników dodatkowych, żeby było bardziej dobrze. Niech tak będzie. I wyprintuję sobie, co tu ciekawego się wczytało. Te dodatkowe przełączniki to jest ignorowanie nowych linii na końcu wpisu oraz ignorowanie pustych znaków. Wczytały się wszystkie rekordy. Mamy tutaj 8 rekordów od 0 do 7. No i teraz chcemy przez nie przejść. No to zastosuję pętlę ForEach, czyli ForEach Data As, załóżmy, rekord. Teraz co ja chcę zrobić z tymi rekordami? Chcę je zamienić na wektory. I do tego służy funkcja o nazwie Embed, która jest z tego pakietu embeddings.php. Pokażę Ci najpierw, jak to się robi. Na początku tutaj mamy Embedding, równa się, Embed, i tu podajemy nasz rekord. To nam zamienia proste zdanie na 1536-wymiarowy wektor. Wyprintujemy sobie go, aby wiedzieć w ogóle, o czym mówimy. I tutaj damy sobie die, żeby po pierwszym rekordzie już wyłączyła się nasza pętla, bo to jest tylko w ramach testów na razie. Ten embedding, jak to dokładnie wygląda? Pokażę Ci. Otwieram sobie ten plik, php, i widzimy tutaj, że jest funkcja Embed, która znowu przez curla odwołuje się do apip od OpenAI i wysyła taką strukturę danych, czyli jakiego modelu używamy oraz jaki jest input, czyli tekst. No i to, skąd wziąłem, z dokumentacji. Po prostu. Dobrze. W takim razie wracamy tutaj i uruchamiamy ten kod. Powinno nam to wyprintować w tym miejscu, jaki wektor powstał. Uruchamiam i co się dzieje? Trwa wykonywanie, bo to oblicza się po stronie API zewnętrznego. I widzę, że coś się wyprintowało. I mamy tu mnóstwo, mnóstwo jakichś dziwnych cyferek. To jest właśnie ten wektor. Pamiętasz, ile miał mieć wymiarów? Tu mamy ostatni rekord. 1535. Dlaczego? Bo są numerowane od zera. Więc pierwszy będzie zerowy, czyli mamy dokładnie 1536 wymiarów wektora w ramach takiej struktury JSON-a. I tu mamy coś takiego jak obiekt data. Tu mamy tablicę o zerowym rekordzie i tu mamy embedding. I to musimy wyciągnąć. W takim razie do tego, co tutaj wyprintowuję, dopiszę namiary na to, co przed chwilą powiedziałem. W takim razie daję tutaj strzałkę, data, rekord zerowy i tu mamy embedding. Sprawdzamy, czy to na pewno działa, czy wyciągnę to, co trzeba. Tak tu jest sam wektor. Czyli mamy zamieniony konkretny wpis na wektora. W takim razie chcemy zamienić je wszystkie. Nie będzie nam już ten DAJ potrzebny oczywiście. Możemy go sobie skasować. Zamiast tego printa do debugowania damy tu jakąś zmienną. Niech ona nazywa się tak samo. Przepisujemy to. Czyli tu mamy embedding. Dajemy w ten sposób. I to jest zapisane. Dobrze. I teraz musimy na tym wywołać funkcję o nazwie request. Dajemy tu oczywiście metodę put, po to, żeby wsadzić dane do bazy. Tu będzie nasz obiekt do wsadzenia. A następnie musimy zdefiniować oddzielnego endpointa. Bo już nie jest to ten sam endpoint API, który jest przy zakładaniu kolekcji. Jest to slash points. I skąd ja to wiem, że tu jest slash points, a nie coś innego? No wiem to oczywiście z dokumentacji. Pamiętasz? Jest tutaj na dole quick start. I szukamy dodawania rekordów. I widzimy, jaką metodą do jakiego endpointa należy wysłać jakie dane. Metoda put do endpointa slash points. A niżej jest struktura. Ja ją znam na pamięć. Widzimy na razie, że baza jest pusta i nie ma żadnych punktów. A my zaraz zmienimy ten stan. W takim razie definiujemy, co konkretnie chcemy dodać. W ramach jednego inserta możemy wsadzić więcej niż jeden punkt. Więc musimy wysłać tablicę punktów, a więc points. I tu otwieramy sobie nawias. I teraz zwróć uwagę. Copilot nam podpowiada, jaka jest struktura. Możemy ją zaakceptować. Mi się ona podoba. I widzimy tu kilka elementów, które muszą być obecne. Czyli mamy ID-ka. On po prostu wyliczył sumę MD5 dla tego rekordu. I to będzie jego ID unikalny. Następnie do payloada wrzucił po prostu ten rekord. A jako wektor użył embeddinga, który wcześniej obliczyłem. Czyli wszystko jest idealnie napisane. I na pierwszy rzut oka powinno to działać. Pytanie tylko, czy na pewno tak będzie. Sprawdźmy to. Uruchomimy ten fragment kodu. Czekamy chwilę. Czekamy, czekamy. Każdy z tych rekordów jest wyliczany i rzucany do bazy danych. A następnie sprawdzimy w interfejsie webowym, czy naprawdę one zostały dodane. Odświeżam. I jak najbardziej. Mamy tu dodane. Pies Adama ma na imię Alexa. Jest tu point o tym haszu. Wszystko się zgadza. Sprawdzimy sobie, czy na pewno wszystkie dane są tutaj zebrane. Możemy to sprawdzić i tu mamy 8 rekordów, w zasadzie wektorów zaindeksowanych. Świetnie. Czyli umiemy już tworzyć kolekcję. Umiemy dodawać nowe wektory. Wypadałoby coś znaleźć w tych wektorach. Do tego momentu wszystko wygląda tak jak w standardowych bazach danych. Czy to SQL, czy NoSQL. Gdzie tu jest więc magia? No właśnie chcę Ci to pokazać. Będziemy odpytywać naszą bazę danych za pomocą normalnego języka naturalnego. Bez żadnej składni, bez niczego. Po prostu napiszemy zwykłe zdanie. Nasze query będzie tutaj zapisane. Tylko na początku musimy przypomnieć sobie, co mamy w bazie danych. W bazie danych mamy na przykład pierwszą informację na temat psa Adama ma na imię Alexa. Sprawdźmy w takim razie, czy możemy wyciągnąć tą informację. Musimy z niej zrobić embeddinga, a więc embed i tu piszemy normalnym językiem jak ma na imię pies Adama i znak zapytania. Nasz embedding jest gotowy. Ma 1536 wymiarów i nim będziemy odpytywać bazę danych kwadrantów. W takim razie request i metodą post, dlatego że postem się to szuka. Jest to w dokumentacji zdefiniowane. Następnie jest obiekt i points łamane przez search. To jest nasz endpoint do wyszukiwania danych. Co chcemy teraz wysłać? Chcemy wysłać wektor do wyszukiwania i naszym wektorem jest nasz embedding wyliczony wcześniej, czyli to jest query oraz limit. Interesuje nas tylko jedna odpowiedź na nasze pytanie. Mogłoby ich być więcej, gdyby baza była większa, ale my chcemy jedną. I oczywiście with payload, bo w payload jest informacja na temat całego zdania. Na pewno nam się to przyda. Logika wyszukiwania jest naprawdę bardzo prosta, to znaczy szukasz wektorów za pomocą wektora, więc na początku musisz co? Zbudować tego wektora. I ten limit ustawiasz na tyle rekordów, ile chcesz wyciągnąć. Ten with payload czasami się przyda, czasami nie. Wyprintujemy na ekran efekt działania tej funkcji, aby zobaczyć, czy naprawdę ona działa i uruchamiam to. Co się teraz dzieje? No coś jest nie tak. JSON jest źle zbudowany. Nie jest to taka struktura, jak ja się spodziewałem. Dlaczego? Pamiętam, jak wcześniej odwoływałem się do obiektu? To nie może być po prostu cały obiekt query, tylko tu będzie data, tu mamy rekord zerowy i następnie jest embeddings i w ten sposób teraz co widzimy? Pies Adama ma na imię Aleksa. No dobra, tylko ty teraz mi powiesz, każda baza danych na świecie jest w stanie coś takiego zrobić. To popatrz w takim razie na to. Zmienimy sobie zapytanie na inne. Na przykład imię psiny Adasia. O, tyle literówki. Spoko. I teraz zwróć uwagę, imię się nie zgadza, psina się nie zgadza z psem, a on dalej wie, że pytasz o psa Adama. No to to jest całkiem niezłe, prawda? Zapytajmy o coś innego. Na przykład piesek Adasia jakiego ma nejma. Niech to będzie I co? I mamy tutaj informację, że pies Adama ma na imię Aleksa, czyli znowu dostajemy ten sam rekord, który chcieliśmy, pomimo tego, że źle napisaliśmy zapytanie. Nie tymi słowami, którymi powinniśmy tutaj to opisać. Zapytamy teraz o tą drugą informację. Gdzie mieszka Kuba? Tylko tam była informacja, gdzie mieszka Jakub. A on co? I jest w stanie to skojarzyć. Wie, że o Jakuba chodzi. Świetnie. Zapytajmy w takim razie o Mateusza. Niech tutaj będzie kto robi najlepsze memy. Odpowiedź na to pytanie jest znana nawet bez przeszukiwania bazy danych. To jest Mateusz. I jak najbardziej Mateusz lubi produkować memy. I w ten sposób widzisz, na czym polega magia tych baz wektorowych. Są to bazy, które szukają pod względem semantycznym co masz na myśli i o czym mówisz, a nie pod względem leksykalnym, czy popełniłeś literówki, czy też nie. Jeśli takie tematy jak ten Cię interesują, to zapraszam na szkolenie AIDevs. Linka masz w opisie. I tam uczymy między innymi tego jak porozumiewać się z bazami wektorowymi, w jaki sposób lepiej wyszukiwać informacje, jak pracować z własnymi danymi, w jaki sposób rozmawiać z tymi danymi. No i co najważniejsze w tej edycji, jak budować tak zwane agenty. To co? Zapraszam na stronę i do zobaczenia w kolejnym filmie. Cześć.