RAG Chatbot mit Spring AI, AWS Bedrock und Vektor Datenbank
Wie bringt man einem KI Chatbot Unternehmens-Knowhow bei? Retrieval Augmented Generation (RAG) ist eine Lösung, die ich in diesem Artikel mit Amazon Bedrock, der Vektor Datenbank pgvector und Spring AI umsetze.
RAG Architektur-Überblick
Unsere Anwendung sucht in der Vektor Datenbank nach zur Chat-Anfrage des Benutzers passenden Dokumenten. Mit diesen Unternehmens-spezifische Texten reichert unsere Anwendung die Interaktion mit dem Bedrock Titan Chat Service an. Dadurch bekommt der Benutzer eine Chat-Antwort passend zum Kontext des Unternehmens.
Um die Datenbank mit vektorisierten Dokumenten zu füllen, verwendet der "Document Importer" unserer Anwendung den AWS Bedrock Embedding Service des Sprachmodells Titan. Die Dokumente bzw. Texte werden aus Quellen des Unternehmens eingelesen, mit Hilfe des AWS Embedding Services vektorisiert und dann in der Vektor-DB gespeichert.
Video about Spring AI and Azure
Dokumente in pgvector importieren
Als Vektor-Datenbank verwende ich PostgreSQL mit der Erweiterung pgvector. Diese Datenbank wird von Spring AI unterstützt und lässt sich im Docker Container starten:
docker run -d --name vector_db --restart always -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=vector_store -e PGPASSWORD=postgres --log-opt max-size=10m --log-opt max-file=3 -p 5433:5432 ankane/pgvector:v0.5.1
Die Maven Konfiguration für den Document Importer und den Chatbot im nächsten Abschnitt sieht so aus:
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bedrock-ai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
...
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>0.8.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement><repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
- Da Spring AI noch keine 1.0 Version hat, finden wir es noch nicht im zentralen Maven-Repository sondern im Spring Milestones Repository.
- Für den Chatbot und die Vektorisierung benötigen wir die "spring-ai-bedrock-ai-spring-boot-starter" Bibliothek.
- Für die Vektor-Datenbank die andere Spring AI und PostgreSQL Dependencies.
Damit bauen wir einen einfachen Importer:
@Component
public class DocumentImport {
private final VectorStore vectorStore;
private final TokenTextSplitter textSplitter = new TokenTextSplitter();
private static final String EXAMPLE_DOCUMENT = "Long text with company specific content.";
...
public void importDocuments() {
var vectorizedDocumentChunks = textSplitter.split(EXAMPLE_DOCUMENT, 800)
.stream()
.map(chunk -> new Document(chunk, Map.of("Add", "meta", "data", "here")))
.toList();
vectorStore.accept(vectorizedDocumentChunks);
}
}
- Die VectorStore Bean stellt Spring AI automatisch zur Verfügung.
- TokenTextSplitter ist eine Klasse aus den Spring AI Bibliotheken zum Aufsplitten langer Texte. 800 definiert die Größe der aufgeteilten Textblöcke. Den Wert 800 habe ich in einer Spring-Klasse als dortigen default abgeschaut.
- Die aufgeteilten Textblöcke wandle ich in Spring AI Document Objekte um. Die Document Objekte könnt ihr mit Metadaten in Form einer Map anreichern.
- Der letzte Schritt vectorStore.accept schreibt eine Liste aus allen Document Objekten des ursprünglichen, Firmen-spezifischen Beispiel-Dokuments in die Vektor-DB. Im VectorStore ist eine Spring AI Bean EmbeddingClient injiziert, welche die Vektorisierung der Document Objekte vornimmt.
Damit Spring AI automatisch alle benötigten Beans bereitstellt, konfiguriert ihr in application.porperties:
spring.ai.bedrock.aws.region=eu-central-1
spring.ai.bedrock.aws.access-key=TODO_set_me
spring.ai.bedrock.aws.secret-key=TODO_set_me
spring.ai.bedrock.titan.chat.enabled=true
spring.ai.bedrock.titan.embedding.enabled=true
spring.ai.bedrock.titan.embedding.input-type=text
spring.ai.bedrock.titan.embedding.model=amazon.titan-embed-text-v1
spring.datasource.password=postgres
spring.datasource.username=postgres
spring.datasource.url=jdbc:postgresql://localhost:5433/vector_store
- spring.ai.bedrock.aws definiert die Verbindung zum AWS Bedrock Service sowohl für den Embedding Service zur Dokumenten-Vektorisierung als auch für den Chat Service.
- spring.ai.bedrock.titan aktiviert und konfiguriert die beiden AWS Bedrock Dienste. Hier sind noch weitere Feineinstellungen der KI möglich (maxtoken, temprature etc.).
- spring.datasource beschreibt die Verbindung zur Datenbank.
RAG Chatbot mit Vektor-DB
Einen Chat mit Spring AI und AWS Bedrock implementieren wir so:
private final BedrockTitanChatClient chatClient;
public void chat(String message) {
var userMessage = new UserMessage(message);// TODO Integrate VectorStore var prompt = new Prompt(List.of(userMessage)); var aiResponse = chatClient.call(prompt); log.info("AI response: {}", aiResponse.getResult().getOutput().getContent()); }
- Die Chat message des Benutzers wandeln wir in ein Objekt der Spring AI Klasse UserMessage um.
- Das UserMessage Objekt überführen wir in ein Spring AI Prompt Objekt. (In das Prompt Objekt integrieren wir in einem späteren Schritt noch die Daten aus der Vektor-DB.)
- Das Prompt Objekt schicken wir an den AWS Bedrock Chat Service. Dazu verwenden wir eine Bean, die von Spring AI automatisch erzeugt wurde: BedrockTitanChatClient. Die Antwort der KI logge ich in die Konsole - es ist eine kleine Demo-Anwendung.
Um aus diesem einfachen Chat-Service ein RAG zu machen, binden wir noch die Vektor-Datenbank mit unserem Unternehmens Know-How ein. Dazu ergänzen wir den zuvor gezeigten Code:
private final VectorStore vectorStore;
private static final SystemPromptTemplate template = new SystemPromptTemplate("""
Du assistierst bei Fragen zum UseCase X.
Verwende die Informationen aus dem Abschnitt DOKUMENTE, um genaue Antworten zu geben,
aber tu so, als ob du diese Informationen von Natur aus wüsstest.
Wenn du dir nicht sicher bist, gib einfach an, dass du es nicht weißt.
DOKUMENTE:
{documents}
""");public void chat(String message) { ... var similarDocuments = vectorStore.similaritySearch(message) .stream() .map(Document::getContent) .collect(Collectors.joining(System.lineSeparator()));var contextMessage = template.createMessage(Map.of("documents", similarDocuments));
var prompt = new Prompt(List.of(contextMessage, userMessage));
... }
- Mit der VectorStore Bean suchen wir nach ähnlichen Dokumenten in der Vektor-Datenbank: vectorStore.similaritySearch
- Die so gefundenen Document Objekte wandeln wir in einen langen String um. Das passiert in den Stream-Operationen direkt nach der Ähnlichkeits-Suche. Mehr zu Streams gibt es hier.
- Neben der UserMessage stecken wir dieses mal ein 2. Message Objekt (contextMessage) als Kontext in die Prompt Instanz. Diese 2. Message bauen wir mit dem Spring AI SystemPromptTemplate. Das Template beschreibt das Verhalten unserer KI und auf welche Dokumente mit Firmen-spezifischen Wissen die KI zugreift. Auf diese Weise ist im AWS Bedrock Chat Service kein Firmen-spezifisches Wissen dauerhaft persistiert. Den Unternehmenskontext übertragen wir mit jeder Chat Anfrage passend zur Nachricht des Benutzers.
Fazit
Durch AWS Bedrock habe ich einfachen Zugriff auf KI Sprachmodelle. Eine einfache Vektor Datenbank lässt sich schnell mit Docker starten. Mit Spring AI benutzen wir diese Dienste leicht und verbinden sie so zu einem RAG.
Wenn euch die Qualität des Amazon Titan Sprachmodells nicht überzeugt, testet andere Sprachmodelle wie Mistral AI oder Llama in AWS Bedrock. Dank Spring AI braucht ihr nur die Maven-Dependencies und die application.properties anzupassen.
Den von mir gezeigten Code, findet ihr bei GitHub: https://github.com/elmar-brauch/aws-bedrock-rag
OpenAI Chatbot
Kommentare