DevTurtle logo DevTurtle

Spring AI – Integrazione con database vettoriali

guide/ Guida Spring AI

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:

git icon
Git RepositoryDownload source

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:

XML
<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.

Qdrant API key button
Qdrant API key button

Successivamente, nel popup che verrà visualizzato è possibile digitare una chiave a propria scelta:

Qdrant api key
Qdrant api key

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:

Plaintext
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
XML
<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:

Java
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.

Plaintext
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:

Qdrant collection example
Qdrant collection example

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:

Java
@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à:

JSON
[
  {
    "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:

Java
@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.