Attraverso l'integrazione con avanzati algoritmi di AI, i database vettoriali non sono più semplici repository di informazioni multidimensionali, ma diventano piattaforme dinamiche per l'analisi, la previsione e la presa di decisioni intelligenti basate sui dati. In questo articolo vedremo come il framework Spring AI offre una soluzione semplice ed intuitiva per l'integrazione con i database vettoriali.
Prerequisiti
Per il nostro tutorial useremo il database vettoriale Qdrant e il modello “text-embedding-3-small” di OpenAI per generare vettori multidimensionali (embeddings). Vi invitiamo quindi a leggere i nostri precedenti articoli su questi argomenti:
Potete inoltre scaricare il codice sorgente del nostro tutorial da GitHub:
Database vettoriali compatibili con Spring AI
Nonostante in questo tutorial abbiamo deciso di usare Qdrant, il modo in cui funziona l’integrazione in Spring AI con i database vettoriali è sempre lo stesso. Riportiamo di seguito l’elenco dei DB compatibili:
Avete quindi una vasta scelta per trovare la soluzione più adatta alle vostre esigenze.
Tutorial integrazione Qdrant con Spring AI
Connessione al database
Iniziamo il nostro tutorial sull’integrazione di Qdrant in Spring AI importando la dipendenza maven relativa al modulo che fa da connettore:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store-spring-boot-starter</artifactId>
</dependency>
Per permettere l’autenticazione del nostro applicativo a Qdrant è necessario generare una API Key. Per farlo è necessario accedere alla Web-UI di Qdrant (come spiegato nell’articolo precedente) e cliccare sull’icona della chiave in alto a destra.
Successivamente, nel popup che verrà visualizzato è possibile digitare una chiave a propria scelta:
Una volta creata la chiave, possiamo procedere con la configurazione di Spring AI. A tal proposito è necessario aggiungere nel file application.properties le seguenti righe:
spring.ai.vectorstore.qdrant.host=localhost
spring.ai.vectorstore.qdrant.port=6334
spring.ai.vectorstore.qdrant.api-key=ewwk1201nqndkq12u14uop
spring.ai.vectorstore.qdrant.collectionname=spring-test-collection
Come si nota, a parte i parametri per la connessione (host, porta e API Key), abbiamo anche definito il nome della collection a cui vogliamo collegarci.
A questo punto, provando ad avviare l’applicativo Spring AI, verrà automaticamente creata la collezione vuota e sarà possibile visualizzarla all’interno della Web-UI.
Salvare gli embeddings nel DB
Come abbiamo già visto nel nostro precedente tutorial, con OpenAI è molto semplice generare delle rappresentazioni vettoriali partendo da una stringa. Procediamo dunque importando le dipendenze dai moduli:
- “Spring Boot – Web” necessario per l’esposizione di un RestEndpoint
- “Spring AI – OpenAI” necessario per generare gli embeddings
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
E implemenentiamo un RestController per generare embeddings ed inserirli in Qdrant:
package com.devturtle.springai.vectordbdemo;
import java.util.ArrayList;
import java.util.List;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OpenAiEmbeddingController {
@Autowired
private VectorStore vectorStore;
@PostMapping("/ai/embeddings/add")
public void add(@RequestBody List<String> docs) {
List<Document> documents = new ArrayList<Document>();
docs.forEach((el) -> {
documents.add(new Document(el));
});
vectorStore.add(documents);
}
}
Ancora una volta, il codice è veramente molto semplice ed Intuitivo:
- definiamo un endpoint che riceve in POST una lista di stringhe;
- per ogni stringa creiamo un oggetto di tipo Document;
- usiamo il VectorStore per inserire i documenti all’interno del database vettoriale.
In tutto questo non abbiamo mai fatto riferimento ad OpenAI o a Qdrant ma Spring AI è in grado di capire automaticamente il modello e il DB da usare in quanto abbiamo importato le dipendenze in precedenza (mediante dependency injection). Potremmo quindi facilmente sostituire sia l’LLM che il database.
Eseguendo una curl all’endpoint appena creato possiamo verificarne il funzionamento.
curl --location 'http://localhost:8080/ai/embeddings/add' \
--header 'Content-Type: application/json' \
--data '[
"We will use the Qdrant vector database",
"With Spring AI we will generate embeddings",
"We will use Qdrant to save the embeddings",
"We will do a similarity search using Qdrant"
]'
Aprendo la collection tramite la Web-UI di Qdrant possiamo verificare che i dati siano presenti:
Eseguire ricerche per similarità nei database vettoriali
Avendo inserito i dati nella collection, possiamo ora usarli per effettuare delle ricerche per similarità. Per farlo è sufficiente definire un secondo endpoint che prende in input una stringa da usare come filtro per la ricerca:
@GetMapping("ai/embeddings/search")
public List<Document> search(@RequestParam(value = "query") String query) {
return vectorStore.similaritySearch(query);
}
Di seguito un esempio di invocazione all’endpoint di ricerca:
http://localhost:8080/ai/embeddings/search?query=Quadrant vector database
Il risultato sarà:
[
{
"embedding": [
],
"content": "We will use the Qdrant vector database",
"id": "5b4843e6-c9e4-4af0-9e7c-79d113f1d3b9",
"metadata": {
"distance": 0.36218166
}
},
{
"embedding": [
],
"content": "We will do a similarity search using Qdrant",
"id": "1b2a4f99-2be7-4d54-acf6-bc4cceed903f",
"metadata": {
"distance": 0.58939946
}
},
{
"embedding": [
],
"content": "We will use Qdrant to save the embeddings",
"id": "2a6a692e-4b71-4738-9c1e-7d4d709a249e",
"metadata": {
"distance": 0.6111447
}
},
{
"embedding": [
],
"content": "With Spring AI we will generate embeddings",
"id": "3d39f18f-4279-4dc5-aaf2-6552dbb74e8d",
"metadata": {
"distance": 0.80965024
}
}
]
Come si può notare, al primo posto (con un valore di “distanza” più basso) troviamo la frase “We will use the Qdrant vector database” che contiene esattamente la stringa cercata. Successivamente ci sono quelle che contengono la parola “Qdrant” e infine la frase che non lo contiene.
E’ possibile impostare dei parametri aggiuntivi alla ricerca come ad esempio un valore limite alla distanza:
@GetMapping("ai/embeddings/search")
public List<Document> search(@RequestParam(value = "query") String query) {
return vectorStore.similaritySearch(
SearchRequest.defaults()
.withQuery(query)
.withSimilarityThreshold(0.5));
}
Così facendo la query restituirà solo il primo risultato che ha un valore di distanza inferiore a 0.5.
Come sempre mi auguro che il tutorial sia stato utile per farvi comprendere in maniera semplice come funziona l’integrazione tra Spring AI e i database vettoriali e vi invito a leggere gli altri articoli del nostro blog su tematiche simili.