Blog

La nostra raccolta di articoli e approfondimenti tecnici

Video

Una raccolta di video dai nostri eventi, webinar e molto altro

Use case

Una raccolta dei nostri Casi di successo

Introduzione

Durante il prossimo OpenSearchCon 2025 avremo l’occasione di illustrare l’utilizzo di NiFi e dei suoi processor python per l’indicizzazione e ricerca tramite vector embedding utilizzando Opensearch quale database vettoriale. Cogliamo l’occasione per illustrare brevemente cosa è il vector embedding e come utilizzarlo tramite OpenSearch, e in questo caso anche NiFi.

Cosa è il vector embedding

Un vector embedding è una rappresentazione numerica di dati (come parole, frasi o immagini) in uno spazio multidimensionale. In altre parole, il contenuto testuale viene trasformato in una serie di numeri che ne rappresentano il significato semantico all’interno di un determinato modello di embedding. Ad esempio, le parole ‘cane’ e ‘gatto’ avranno vettori simili perché appartengono alla stessa categoria concettuale (animali), mentre ‘automobile’ avrà un vettore molto diverso. Tuttavia, va notato che modelli di embedding diversi possono generare rappresentazioni differenti per lo stesso testo.

La ricerca basata su vector embedding differisce da quella tradizionale per parole chiave, che si concentra sull’individuazione delle occorrenze esatte delle parole nel testo e sulla valutazione della loro rilevanza. La ricerca vettoriale, invece, si basa sul confronto del ‘significato’ complessivo del testo, misurando la somiglianza tra vettori attraverso metriche come la similarità del coseno o la distanza euclidea.

Come utilizzare efficacemente il vector embedding

Per utilizzare efficacemente la ricerca vettoriale, i dati devono essere trasformati in vettori tramite modelli di embedding. È importante distinguere tra i modelli statici di embedding, che generano una rappresentazione fissa per ogni parola, e modelli contestuali che generano embedding dinamici basati sul contesto in cui la parola appare.

Nel caso di testi lunghi, che potrebbero superare la capacità di un modello di elaborazione del linguaggio naturale, il testo viene suddiviso in ‘chunk’ o segmenti, che vengono poi trasformati in vettori separati. Questo processo migliora l’efficienza e la precisione della ricerca.

Una volta trasformati in vettori, i dati vengono memorizzati in un database vettoriale o in un motore di ricerca che supporta la ricerca semantica. OpenSearch è una delle soluzioni che permettono l’archiviazione e l’indicizzazione di vettori per effettuare ricerche basate sul significato. Inoltre, supporta la ricerca ibrida, combinando la ricerca per parole chiave con quella vettoriale, migliorando così la capacità di recupero delle informazioni.

Sotto una immagine riassuntiva dei passi necessari per la indicizzazione tramite vector embedding.

Vector Embedding in OpenSearch con NiFi 2.0

A partire dalla versione 2.0-M3 di Apache NiFi, è stata introdotta la possibilità di sviluppare estensioni (processor) direttamente in Python. Questo permette sicuramente una maggiore flessibilità e personalizzazione e abbassa notevolmente i tempi di sviluppo rispetto alla precedente architettura basata su Java e archetipi Maven. Nella distribuzione della versione 2.0-M3, alcuni processor di esempio sono stati inclusi nella distribuzione.

Tra i processor disponibili, alcuni in particolare sono rilevanti per la costruzione di un flusso di acquisizione dati e indicizzazione basato su vector embedding, in corsivo un estratto della descrizione degli stessi tratto dalla documentazione NiFi:

  • ParseDocument: Parses incoming unstructured text documents …
  • ChunkDocument: Create chunks of text from a single larger chunk…
  • PutOpenSearchVector: Create vectors/embeddings that represent text content and send the vectors to OpenSearch…

Da quanto prima descritto emerge quindi che questi processor possono essere considerati “mattoni” utili per costruire una pipeline di ingestion di documenti orientata al vector embedding in OpenSearch. Tuttavia, affinché la pipeline sia completa, è necessario integrare un modello, che nel caso dei processor presenti in NiFi è limitato all’ambito non locale, che generi gli embedding dai testi prima di archiviarli.

La ricerca delle informazioni

Il processo di ricerca ripercorre sostanzialmente gli stessi passi della indicizzazione : a partire dalla query di ricerca calcoliamo il vector embedding della stessa e procediamo a cercare nello spazio multidimensionale le occorrenze più vicine al vettore creato a partire dalla stringa di ricerca. In questo caso specifico non si ricorre solitamente al chunking poichè il testo e abbastanza più ridotto rispetto a quello sottoposto ad indicizzazione.

I risultati sono ordinati per distanza e vengono quindi esposti tramite una apposita interfaccia di fruizione dei risultati.

Anche in questo caso Nifi ci fornisce un processor python apposito :

  • QueryOpenSearchVector : Queries OpenSearch in order to gather a specified number of documents that are most closely related to the given query.

E’ abbastanza semplice, per testare il processor, implementare un semplice flusso che mostri una pagina HTML per la ricerca e la relativa pagina dei risultati ovviamente utilizzando unicamente Nifi e javascript. I risultati saranno elencati in base a come vengono erogati in output dal processor QueryOpenSearchVector previa elaborazione del JSON di ritorno per adattarlo alle necessità del client Javascript che impagina i risultati della ricerca.

Le customizzazioni

Partendo da quanto implementato, anche per testare la possibilità di estensione e customizzazione dei processor python NiFi, abbiamo modificato i processor che gestiscono l’embedding per potere utilizzare i modelli tramite risorse locali. Nello specifico, un server Ollama che ha ospitato i modelli per il vector embedding utilizzati per la seconda parte della sperimentazione.