Startseite / Artikel / Spring-AI-RAG mit einem Vector Store in der JVM: Schluss mit Qdrant und Docker

Spring-AI-RAG mit einem Vector Store in der JVM: Schluss mit Qdrant und Docker

Qdrant in einer Spring-AI-RAG-App durch die In-Process-Bibliothek Vectors ersetzen: Abhängigkeit, JVM-Flag, Persistenz-Einstellung und die Kompromisse eines Index in der JVM.

1003 Wörter

Retrieval-Augmented Generation heißt nicht automatisch, dass man eine separate Vektordatenbank betreiben und pflegen muss. Eine Spring-AI-Anwendung kann ihren Vektorindex innerhalb der JVM halten, im selben Prozess wie alles andere. So richten Sie das ein.

Wo die In-Memory-Variante nicht reicht

Spring AI arbeitet mit vielen Vector Stores, darunter Chroma, Pinecone, PostgreSQL mit pgVector und Qdrant. Außerdem bringt es eine eingebaute Klasse mit, SimpleVectorStore. Die eignet sich für Demos und Tests, aber nicht für die Produktion. Eine echte Vektorsuche führt sie nicht aus. Bei jeder Anfrage geht sie alle Dokumente einzeln durch. Metadaten von Dokumenten unterstützt sie auch nicht, sodass man Ergebnisse nicht zusätzlich zur Vektorähnlichkeit nach Metadaten eingrenzen kann.

Eine neuere Bibliothek namens Vectors schließt diese Lücke. Sie ist ein echter Vektorindex, der vollständig in der JVM lebt, Metadaten unterstützt und effizient nach Ähnlichkeit sucht. Sie bekommen semantische Suche, ohne eine weitere Datenbank deployen oder betreiben zu müssen.

Der Plan: Eine RAG-Anwendung, die derzeit Qdrant nutzt, bekommt Vectors als Store, damit die ganze App ohne externen Dienst läuft.

Die Abhängigkeit austauschen

Fügen Sie zuerst den Vectors-Starter hinzu:

implementation 'com.integrallis:vectors-spring-boot-starter:0.1.4'

Die Version muss 0.1.4 oder neuer sein. Frühere Releases hatten keine Auto-Konfiguration, sodass Sie das VectorStore-Bean selbst anlegen mussten, zusammen mit einem VectorCollection-Bean. Ab 0.1.4 erzeugt die Auto-Konfiguration beide für Sie.

Entfernen Sie anschließend drei Dinge: die Qdrant-Abhängigkeit, die Abhängigkeit für die Docker-Compose-Unterstützung und compose.yaml selbst. Nichts davon wird mehr gebraucht, denn Vectors läuft innerhalb der Anwendung, und es gibt keinen Server, den man starten oder überwachen müsste.

Der Rest der App bleibt, wie er war. Weil Spring AI den Store hinter der Abstraktion VectorStore verbirgt, läuft der Ingestion-Code in load() genau wie vorher, und der am ChatClient eingerichtete QuestionAnswerAdvisor braucht keine Änderungen.

Ein JVM-Flag vor dem ersten Start

Vectors nutzt die Java Vector API, die im JDK noch als Inkubator-Feature gilt, deshalb muss dieses Modul eingeschaltet werden. Für Gradle ergänzen Sie in build.gradle Folgendes:

tasks.named('bootRun') {
    jvmArgs('--add-modules', 'jdk.incubator.vector')
}

Das deckt Starts über Gradle ab, etwa ./gradlew bootRun.

Wer aus IntelliJ IDEA startet, muss das Argument zusätzlich in der Run Configuration eintragen:

  1. Öffnen Sie Run → Edit Configurations.
  2. Wählen Sie die Run Configuration Ihrer Anwendung.
  3. Suchen Sie das Feld VM options. Wird es nicht angezeigt, klicken Sie auf Modify options und aktivieren Add VM options.
  4. Tragen Sie --add-modules=jdk.incubator.vector in das Feld ein.
  5. Klicken Sie auf Apply und dann auf OK.
  6. Starten Sie die Anwendung wie gewohnt.

Beim Start sollten Log-Meldungen bestätigen, dass die Dokument-Chunks in den Vector Store geladen wurden.

Warum sonst nichts geändert werden muss

Die Abstraktion erledigt die schwere Arbeit. Bei der Ingestion zerlegt die App das Regelheft in Chunks und übergibt sie an das vorhandene VectorStore-Bean. Bei einer Frage holt der QuestionAnswerAdvisor von demselben Bean die Chunks, die der Nutzerfrage am nächsten sind, und fügt sie in den Prompt ein, bevor das Modell antwortet. Keiner der beiden Schritte weiß oder kümmert sich darum, ob das Bean über das Netzwerk mit einem Qdrant-Container spricht oder mit einem Index, der ein paar Objekte weiter im selben Heap liegt. Genau deshalb besteht der ganze Wechsel aus einer geänderten Abhängigkeit und einem JVM-Flag.

Ein paar Fragen ausprobieren

Mit dem Regelheft des Brettspiels Sagrada als Quelldokument (dasselbe wie mit Qdrant) können Sie zum Beispiel fragen:

  • Wie heißt das Spiel?
  • Wie viele Spieler können mitmachen?
  • Wie viele Würfel werden in jeder Runde geworfen?

Die Antworten sollten sich auf das Regelheft stützen, genau wie mit Qdrant. Der Unterschied ist, dass jetzt alles innerhalb der JVM passiert.

Den Index zwischen den Starts speichern

Vectors kann mehr, als die Grundeinrichtung oben nutzt, und ein Blick in die Dokumentation lohnt sich. Persistenz ist das, was die meisten Apps wollen werden, damit die Vektoren einen Neustart überstehen und nicht jedes Mal neu geladen werden müssen. Eingeschaltet wird sie mit einer einzigen Property:

java-vectors.storage-path=/tmp/vectors/rag-recipe

Als Speicherpfad können Sie ein beliebiges Verzeichnis wählen. Danach behält der Store seinen Inhalt von einem Start zum nächsten, und Dokumente müssen nicht bei jedem Hochfahren neu eingelesen werden. Eine Folge: Entfernen Sie nach dem ersten Start die Methode load() aus RagIngestionConfig, sonst fügt jeder Neustart doppelte Kopien desselben Dokuments hinzu.

Wann es passt

Für Anwendungen, die keine eigenständige Vektordatenbank brauchen, ist Vectors ein praktischer Mittelweg. Es bietet echte Vektorsuche, Unterstützung für Metadaten und optionale Persistenz, alles innerhalb der JVM der Anwendung. Wer RAG in einer Spring-AI-App möchte, ohne noch einen Dienst deployen und pflegen zu müssen, sollte es sich ansehen.

Abzuwägende Kompromisse

Ein Index im selben Prozess hat Kosten, die man leicht vergisst. Die Vektoren belegen Speicher in derselben JVM, die auch die Anfragen bedient, ein großer Korpus konkurriert also mit dem Rest der Anwendung um den Heap. Mit aktivierter Persistenz liegen die Daten auf der Festplatte dieser einen Maschine und sind damit an eine einzige Anwendungsinstanz gebunden. Müssen sich mehrere Instanzen einen Index teilen oder ist der Korpus weit größer, als eine einzelne JVM halten sollte, ist eine eigenständige Datenbank wie Qdrant weiterhin die bessere Wahl. Für einen Dokumentationshelfer, einen internen Regelwerk-Bot oder einen Prototyp ist es aber oft ein sehr guter Tausch, einen ganzen Dienst aus dem Deployment zu streichen.