Semantische Suche in PostgreSQL mit pgvector und Node.js – kein LLM erforderlich
Semantische Suche in PostgreSQL mit pgvector und Node.js – ohne LLM: MiniLM-Embeddings, Chunks, ein IVFFlat-Index, Abstandsberechnungsfunktionen und wie man die Scores liest.
Quellen: Talk to Your Database Like a Human — No LLM Required: Semantic Search with Node.js + PostgreSQL
Eine Datenbank kann Fragen, die in gewöhnlichem Englisch gestellt werden, beantworten, ohne dass ein Sprachmodell im Prozess involviert ist. Was das möglich macht, ist die Vektorsuche. Mit Node.js, PostgreSQL und der pgvector-Erweiterung kann man ein System erstellen, das nach Absicht statt nach Schlüsselwörtern abgleicht, wobei das einzige verwendete Modell ein kleines Embedding-Modell ist, das Text in Zahlen umwandelt.
Relationale Datenbanken waren schon immer hervorragend darin, Daten zu speichern und abzurufen, vorausgesetzt, man stellt die genau richtige Frage. Genaue Übereinstimmungen, Filter und strukturierte Abfragen sind mächtig, aber eingeschränkt. Vektordatenbanken scheinen ein anderes Problem zu lösen: das Suchen in unstrukturiertem Inhalt nach seinem Sinn und nicht nach den enthaltenen Wörtern.
Vier Begriffe, die man klar verstehen sollte
Token. Der kleinste Textabschnitt, mit dem ein Modell arbeitet. Tokens sind nicht immer ganze Wörter. Ein Token kann ein vollständiges Wort wie „hello“ sein, ein Fragment (so könnte „unbelievable“ zu „un“, „believ“ und „able“ werden), ein einzelnes Zeichen wie „!“ oder ein Teil von Satzzeichen oder Leerzeichen. Ein Satz wie „ChatGPT ist großartig“ könnte in „Chat“, „G“, „PT“, „ist“ und „großartig“ aufgeteilt werden.
Chunk. Ein kleiner, eigenständiger Teil eines größeren Dokuments, in der Regel ein paar Sätze oder ein Absatz. Die Einbettung eines ganzen Dokuments auf einmal funktioniert schlecht, daher wird es zunächst in Chunks aufgeteilt. Das Chunking beschleunigt die Verarbeitung, verbessert die Suchgenauigkeit und sorgt dafür, dass die Ergebnisse zur Anfrage passen.
Embedding. Eine numerische Vertretung für einen Text-, Bild- oder Audiodatenpunkt, die dessen Bedeutung einfängt. Ein maschinelles Lernmodell, wie zum Beispiel MiniLM, erzeugt sie. Man kann sich ein Embedding als die Idee hinter den Daten vorstellen.
Vector. Die Art und Weise, wie ein Embedding tatsächlich gespeichert wird: ein Array von Zahlen.
[0.2, -0.5, 0.9, ...]
Das ist die in Erinnerung und in PostgreSQL gespeicherte Struktur, sobald eine Erweiterung wie pgvector installiert ist.
Zusammengefasst sieht die Idee so aus: Wörter, Sätze oder Bilder werden zu Embeddings. Objekte mit ähnlichen Bedeutungen weisen Vektoren auf, die nah beieinander liegen, während unverwandte Objekte weiter voneinander entfernt sind. Ein String wie „Hello world“ wird zu einer langen Liste von Fließkommazahlen:
// "Hello world" turns into this embedding:
const embedding = [0.01, -0.42, 0.15, ..., 0.99]; // ← this is a vector
Und diese Liste wird anschließend neben den ursprünglichen Text gespeichert:
INSERT INTO documents (content, embedding)
VALUES ('Hello world', embedding);
Warum eine relationale Datenbank nicht ausreicht
Ein traditionelles RDBMS arbeitet mit Tabellen, Zeilen und Spalten. Es bewältigt Anfragen wie „alle Tickets mit Status OPEN“ oder „Benutzer, die nach 2024 erstellt wurden“, mühelos. Moderne Anwendungen müssen jedoch auch mit Dokumenten, Konversationen, Bildern und Audio umgehen. Eine exakte Abgleichung hilft dabei kaum.
Eine Vektor-Datenbank speichert Embeddings zusammen mit den Daten und findet Elemente anhand von Ähnlichkeit und Kontext. Statt nach Dokumenten zu suchen, die das Wort „Fehler“ enthalten, kann man nach Dokumenten zu Authentifizierungsproblemen fragen – und diese erhalten, selbst wenn „Fehler“ nie vorkommt. Der Vergleich erfolgt dabei über eine mathematische Distanz zwischen Vektoren, nicht über einen Textabgleich.
Der übliche Ablauf sieht so aus:
- Roher Inhalt wird eingegeben: Dokumente, Tickets oder alles andere.
- Ein Embedding-Modell wandelt es in Vektoren um.
- Die Vektoren werden beispielsweise mit pgvector in PostgreSQL gespeichert.
- Ein annähernder Näherungs-Nachbarn-Index (ANN-Index) sorgt für schnelle Abfragen.
- Auch die Abfrage des Benutzers wird in einen Vektor umgewandelt.
- Die Datenbank gibt die Vektoren zurück, die am nächsten liegen, also die am ähnlichsten sind.
Was dies ermöglicht:
- Semantische Suche, die von der Bedeutung einer Abfrage und nicht nur von ihren genauen Worten gesteuert wird.
- Langfristiges Gedächtnis für ein LLM, indem vergangene Interaktionen, Dokumente oder Fakten gespeichert werden, die das Modell auch dann abrufen kann, wenn sie nicht in seinen Trainingsdaten enthalten waren.
- Empfehlungen, indem Elemente oder Verhaltensweisen gefunden werden, die im Embedding-Raum ähnlich sind.
- Ähnlichkeitssuche in Texten, Bildern, Audiodateien oder Videos.
PostgreSQL als Vektorlager
Man muss dafür keine neue Datenbank einsetzen. Mit pgvector kann PostgreSQL Embeddings direkt speichern, Ähnlichkeitsabfragen ausführen und Indizes wie IVFFlat nutzen, um schnell zu bleiben. Die bereits vorhandene Datenbank erlangt einfach eine neue Fähigkeit – von der Speicherung von Daten bis hin zur Möglichkeit, deren Bedeutung zu vergleichen. Die Suche konzentriert sich nicht mehr auf das Abgleichen von Wörtern, sondern auf das Abgleichen von Bedeutungen.
Schritt für Schritt einrichten
1. PostgreSQL installieren und pgvector aktivieren. Installieren Sie PostgreSQL von der offiziellen Download-Seite, verbinden Sie sich und aktivieren Sie die Erweiterung:
CREATE EXTENSION IF NOT EXISTS vector;
2. Erstellen Sie die Dokumententabelle. Sie enthält den Rohtext sowie seine Embedding-Daten. Die 384 in der Definition gibt die Ausgabegröße des all-MiniLM-L6-v2-Modells an.
-- Note: 384 is the embedding size output by all-MiniLM-L6-v2
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(384)
);
3. Fügen Sie einen Vektorindex hinzu. Um die Ähnlichkeitssuche effizient zu halten, verwenden Sie einen annähernden nächsten-Nachbarn-Index:
CREATE INDEX documents_embedding_idx
ON documents
USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);
Dies wählt IVFFlat für schnelle, annähernde Abfragen aus, misst mit der L2-(euklidischen) Distanz und teilt die Vektoren in 100 Clustern auf.
4. Passen Sie den Abfragesoperator zum Index an. Der Operator in Ihrer Abfrage muss zur Operatorklasse passen, mit der Sie den Index erstellt haben. pgvector bietet mehrere Operatoren, von denen jeder mit seiner eigenen Klasse verbunden ist. Im Beispiel wird mit <#> abgefragt und vector_l2_ops konfiguriert. Beachten Sie, dass in der Dokumentation von pgvector die L2-Distanz normalerweise mit <-> dargestellt wird und <#> das negative Skalarprodukt ist – überprüfen Sie daher, ob Ihre Abfrage und der Index übereinstimmen.
5. Führen Sie das Projekt aus. Ein vollständiges, funktionierendes Beispiel befindet sich in einem GitHub-Repositorium namens vectorgarquer/vectorized-database-embeddings. Klonen oder Forken Sie es und installieren Sie anschließend die Abhängigkeiten:
npm install
Kopieren Sie .env.example in eine .env-Datei im Projektverzeichnis und füllen Sie die Details Ihrer PostgreSQL-Verbindung ein. Starten Sie das Projekt anschließend:
npm start
6. Beobachten Sie die Suche. Sobald das Programm läuft, stellt es eine Verbindung her, fügt Embeddings ein und führt semantische Suchen ohne weitere Eingaben durch. Von dort aus können Sie Ihr eigenes Datenmaterial sowie Abfragen eingeben. Die Beispielsuche nach „Was bedeutet generative KI?“ liefert die in der Datenbank gefundenen Ergebnisse wie folgt:
Search Results:
[1] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[2] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[3] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[4] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[5] Distance: -0.7094
assistants, personalized education and service, and digital
art. As such, generative AI has substantial implications for
BISE practitioners and scholars as an interdisciplinary
research community. In our Catchword article, we offered
a conceptualization of the principles of generative AI along
a model-, system-, and application-level view as well as a
social-technical view and described limitations of current
generative AI. Ultimately, we provided an impactful
rese
Lesen der Ausgabe
Nach einem Lauf erhalten Sie eine Liste in dieser Form:
Search Results:
[1] Distance: -0.7562
...
[5] Distance: -0.7094
...
Die Distanz gibt an, wie nah ein gespeicherter Chunk dem Abfragedatenvektor ist. Sowohl der Chunk als auch die Abfrage werden in Vektoren umgewandelt, und PostgreSQL mit pgvector misst, wie weit sie voneinander entfernt sind. Die Regel ist einfach: Je kleiner die Distanz, desto ähnlicher ist der Inhalt.
Die Werte sind negativ, beispielsweise:
-0.7562
Das liegt daran, dass der Operator <#> einen negierten Wert zurückgibt, sodass eine normale Sortierung die am nächsten liegenden Zeilen zuerst anordnet. In der Praxis bedeutet eine noch negativere Zahl eine näher liegende, relevantere Übereinstimmung, während eine Zahl, die dem Nullwert näher ist, eine schwächere Übereinstimmung darstellt.
Wenn man sich die Rangliste ansieht, weisen die Ergebnisse 1 bis 4 denselben Wert von -0,7562 auf, wodurch sie für die Abfrage gleich relevant sind. Ergebnis 5 hat einen Wert von -0,7094, was ebenfalls relevant ist, aber etwas weniger ähnlich.
Man wird möglicherweise auch mehrere Ergebnisse mit nahezu identischem Text feststellen. Das ist zu erwarten. Das Quelldokument wurde in Blöcke aufgeteilt, benachbarte Blöcke überschneiden sich oder wiederholen ähnlichen Inhalt, und jeder Block hat seine eigene Embedding-Darstellung.
Was das Ergebnis zeigt
Auch ohne LLM zum Zeitpunkt der Abfrage verstand das System den Sinn und nicht nur die Schlüsselwörter, holte Texte ab, die zum Kontext passten, und ordnete sie nach vektorieller Ähnlichkeit. Die zurückgegebenen Abschnitte konzentrierten sich auf generative KI-Modelle, eine modellbasierte Sichtweise sowie Machine-Learning-Architekturen, sodass die Suche eindeutig auf Konzeptebene statt auf exakten Wörtern funktionierte.
Das Ergebnis ist eine semantische Suchmaschine, die in PostgreSQL integriert ist. Bei Abfragen ist kein LLM erforderlich, es gibt keine separate Vektordatenbank, und Embeddings zusammen mit pgvector übernehmen die gesamte Arbeit.
Verwandte Artikel
- Semantisches Caching für LLM-Agenten mit Redis: Kosteneinsparung und Verkürzung der Latenzzeit — Wie semantisches Caching die Ausgaben und Latenzzeit bei LLMs verringert: Embeddings, Schwellenwerte, Redis und RedisVL, Feinabstimmung der Genauigkeit sowie Caching von Unterfragen innerhalb der Agentenworkflows.
- Warum KI-Analyse-Agenten eine semantische Schicht statt roher SQL-Abfragen benötigen — Text-zu-SQL-Abfragen auf rohen Lagerdatenblättern erfinden Metriken und ignorieren Zugriffsregeln. Erfahren Sie, wie eine semantische Schicht wie Cube in Kombination mit MCP den Agenten eine regelbasierte Geschäftslogik bietet.