Iniziare con Weaviate Una guida per principianti alla ricerca con database vettoriali
Guida a Weaviate per principianti nella ricerca con database vettoriali
Come usare database vector per la ricerca semantica, la risposta alle domande e la ricerca generativa in Python con OpenAI e Weaviate

Se sei arrivato su questo articolo, assumo che tu abbia sperimentato la creazione di un’app con un grande modello di linguaggio (LLM) e ti sia imbattuto nel termine database vector.
Il panorama degli strumenti per la creazione di app con LLM sta crescendo rapidamente, con strumenti come LangChain o LlamaIndex che stanno guadagnando popolarità.
In un recente articolo, ho descritto come iniziare con LangChain, e in questo articolo voglio continuare ad esplorare il panorama degli strumenti LLM sperimentando con Weaviate.
Cosa è Weaviate?
Weaviate è un database vector open-source. Ti consente di memorizzare oggetti dati e incorporamenti vector e interrogarli in base a misure di similarità.
- Un Ode alla mia laurea in Fisica
- Prime sessioni e relatori annunciati per ODSC West 2023
- Le emoji stanno diventando sempre più vincolanti dal punto di vista legale. Ma sono ancora aperte a un’ampia interpretazione.
GitHub – weaviate/weaviate: Weaviate è un database vector open source che memorizza sia oggetti che…
Weaviate è un database vector open source che memorizza sia oggetti che vettori, consentendo la combinazione della ricerca vettoriale…
github.com
I database vector stanno ottenendo molta attenzione dal momento che i LLM sono diventati oggetto di grande attenzione mediatica. Probabilmente il caso d’uso più popolare dei database vector nel contesto dei LLM è quello di “fornire ai LLM una memoria a lungo termine”.
Se hai bisogno di un ripasso sul concetto di database vector, potresti voler dare un’occhiata al mio articolo precedente:
Spiegare i Database Vector in 3 Livelli di Difficoltà
Da principiante a esperto: Demistificare i database vector in differenti contesti
towardsdatascience.com
In questo tutorial, vedremo come popolare un database vector Weaviate con gli incorporamenti del tuo dataset. Poi analizzeremo tre diversi modi per recuperare informazioni da esso:
- Ricerca vettoriale
- Risposta alle domande
- Ricerca generativa
Prerequisiti
Per seguire questo tutorial, avrai bisogno di quanto segue:
- Ambiente Python 3
- Chiave API di OpenAI (o, in alternativa, una chiave API per Hugging Face, Cohere o PaLM)
Una nota sulla chiave API: In questo tutorial, genereremo incorporamenti da testo tramite un servizio di inferenza (in questo caso, OpenAI). A seconda del servizio di inferenza che utilizzi, assicurati di controllare la pagina dei prezzi del provider per evitare costi imprevisti. Ad esempio, il modello Ada utilizzato (versione 2) costa $0.0001 per 1.000 token al momento della scrittura e ha comportato meno di 1 centesimo di costo di inferenza per questo tutorial.
Configurazione
Puoi eseguire Weaviate sulle tue istanze (usando Docker, Kubernetes o Embedded Weaviate) o come servizio gestito utilizzando Weaviate Cloud Services (WCS). Per questo tutorial, eseguiremo un’istanza di Weaviate con WCS, poiché questa è la modalità consigliata e più semplice.
Come Creare un Cluster con Weaviate Cloud Services (WCS)
Per poter utilizzare il servizio, devi prima registrarti presso WCS.
Una volta registrato, puoi creare un nuovo cluster Weaviate cliccando il pulsante “Crea cluster”.

Per questo tutorial, utilizzeremo il piano di prova gratuito, che ti fornirà un sandbox per 14 giorni. (Non sarà necessario aggiungere alcuna informazione di pagamento. Invece, il sandbox scade semplicemente dopo il periodo di prova. Ma è possibile creare un nuovo sandbox di prova gratuito in qualsiasi momento.)
Sotto la scheda “Sandbox gratuito”, effettua le seguenti impostazioni:
- Inserisci un nome del cluster
- Abilita l’autenticazione (imposta su “SÌ”)

Infine, fai clic su “Crea” per creare la tua istanza di sandbox.
Come installare Weaviate in Python
Ultimo ma non meno importante, aggiungi il weaviate-client
al tuo ambiente Python con pip
$ pip install weaviate-client
e importa la libreria:
import weaviate
Come accedere a un cluster Weaviate tramite un client
Per il passaggio successivo, avrai bisogno delle seguenti due informazioni per accedere al tuo cluster:
- L’URL del cluster
- Chiave API Weaviate (in “Abilitato – Autenticazione”)

Ora puoi istanziare un client Weaviate per accedere al tuo cluster Weaviate come segue.
auth_config = weaviate.AuthApiKey(api_key="YOUR-WEAVIATE-API-KEY") # Sostituisci con la tua chiave API Weaviate# Istanza del clientclient = weaviate.Client( url="https://<your-sandbox-name>.weaviate.network", # Sostituisci con l'URL del tuo cluster Weaviate auth_client_secret=auth_config, additional_headers={ "X-OpenAI-Api-Key": "YOUR-OPENAI-API-KEY", # Sostituisci con la tua chiave OpenAI })
Come puoi vedere, stiamo usando la chiave API OpenAI sotto additional_headers
per accedere al modello di embedding in seguito. Se stai utilizzando un provider diverso da OpenAI, cambia il parametro della chiave in uno dei seguenti che si applicano: X-Cohere-Api-Key
, X-HuggingFace-Api-Key
o X-Palm-Api-Key
.
Per verificare se tutto è configurato correttamente, esegui:
client.is_ready()
Se restituisce True
, sei pronto per i prossimi passaggi.
Come creare e popolare un database di vettori Weaviate
Ora siamo pronti per creare un database di vettori in Weaviate e popolarlo con alcuni dati.
Per questo tutorial, utilizzeremo le prime 100 righe del dataset “Jeopardy Questions” di oltre 200.000 [1] da Kaggle.
import pandas as pddf = pd.read_csv("your_file_path.csv", nrows = 100)
![Prime poche righe del dataset 'Jeopardy Questions' di oltre 200.000 [1] da Kaggle.](https://miro.medium.com/v2/resize:fit:640/format:webp/1*PFx77R8i42o_1Zs3PC_HiQ.png)
Una nota sul numero di token e i costi correlati: Nell’esempio seguente, incorporeremo le colonne “category”, “question” e “answer” per le prime 100 righe. Sulla base di un calcolo con la libreria tiktoken
, questo comporterà circa 3.000 token da incorporare, che corrispondono approssimativamente a $0.0003 di costi di inferenza con il modello Ada di OpenAI (versione 2) a luglio 2023.
Passo 1: Creare uno Schema
Prima di tutto, dobbiamo definire la struttura dei dati sottostante e alcune configurazioni:
class
: Come saranno chiamati i oggetti nella collezione di questo spazio vettoriale?properties
: Le proprietà di un oggetto, incluse il nome della proprietà e il tipo di dati. Nell’analogia del DataFrame di Pandas, queste sarebbero le colonne nel DataFrame.vectorizer
: Il modello che genera le embedding. Per gli oggetti di testo, di solito si selezionerebbe uno dei modulitext2vec
(text2vec-cohere
,text2vec-huggingface
,text2vec-openai
otext2vec-palm
) in base al provider che si sta utilizzando.moduleConfig
: Qui è possibile definire i dettagli dei moduli utilizzati. Ad esempio, il vectorizer è un modulo per il quale è possibile definire quale modello e versione utilizzare.
class_obj = { # Definizione della classe "class": "JeopardyQuestion", # Definizioni delle proprietà "properties": [ { "name": "category", "dataType": ["text"], }, { "name": "question", "dataType": ["text"], }, { "name": "answer", "dataType": ["text"], }, ], # Specifica un vectorizer "vectorizer": "text2vec-openai", # Impostazioni dei moduli "moduleConfig": { "text2vec-openai": { "vectorizeClassName": False, "model": "ada", "modelVersion": "002", "type": "text" }, },}
Nello schema sopra, è possibile vedere che creeremo una classe chiamata "JeopardyQuestion"
, con le tre proprietà di testo "category"
, "question"
e "answer"
. Il vectorizer che stiamo utilizzando è il modello Ada di OpenAI (versione 2). Tutte le proprietà verranno vettorizzate tranne il nome della classe ("vectorizeClassName" : False
). Se hai proprietà che non vuoi incorporare, puoi specificarlo (vedi la documentazione).
Una volta definito lo schema, è possibile creare la classe con il metodo create_class()
.
client.schema.create_class(class_obj)
Per verificare se la classe è stata creata con successo, è possibile visualizzare il suo schema nel seguente modo:
client.schema.get("JeopardyQuestion")
Lo schema creato appare come segue:
{ "class": "JeopardyQuestion", "invertedIndexConfig": { "bm25": { "b": 0.75, "k1": 1.2 }, "cleanupIntervalSeconds": 60, "stopwords": { "additions": null, "preset": "en", "removals": null } }, "moduleConfig": { "text2vec-openai": { "model": "ada", "modelVersion": "002", "type": "text", "vectorizeClassName": false } }, "properties": [ { "dataType": [ "text" ], "indexFilterable": true, "indexSearchable": true, "moduleConfig": { "text2vec-openai": { "skip": false, "vectorizePropertyName": false } }, "name": "category", "tokenization": "word" }, { "dataType": [ "text" ], "indexFilterable": true, "indexSearchable": true, "moduleConfig": { "text2vec-openai": { "skip": false, "vectorizePropertyName": false } }, "name": "question", "tokenization": "word" }, { "dataType": [ "text" ], "indexFilterable": true, "indexSearchable": true, "moduleConfig": { "text2vec-openai": { "skip": false, "vectorizePropertyName": false } }, "name": "answer", "tokenization": "word" } ], "replicationConfig": { "factor": 1 }, "shardingConfig": { "virtualPerPhysical": 128, "desiredCount": 1, "actualCount": 1, "desiredVirtualCount": 128, "actualVirtualCount": 128, "key": "_id", "strategy": "hash", "function": "murmur3" }, "vectorIndexConfig": { "skip": false, "cleanupIntervalSeconds": 300, "maxConnections": 64, "efConstruction": 128, "ef": -1, "dynamicEfMin": 100, "dynamicEfMax": 500, "dynamicEfFactor": 8, "vectorCacheMaxObjects": 1000000000000, "flatSearchCutoff": 40000, "distance": "cosine", "pq": { "enabled": false, "bitCompression": false, "segments": 0, "centroids": 256, "encoder": { "type": "kmeans", "distribution": "log-normal" } } }, "vectorIndexType": "hnsw", "vectorizer": "text2vec-openai"}
Passaggio 2: Importare dati in Weaviate
In questa fase, il database vettoriale ha uno schema ma è ancora vuoto. Quindi, popoliamolo con il nostro dataset. Questo processo è anche chiamato “upserting”.
Inseriremo i dati a lotti di 200. Se hai prestato attenzione, sai che questo non è necessario qui perché abbiamo solo 100 righe di dati. Ma una volta pronto a inserire quantità più grandi di dati, vorrai farlo a lotti. Ecco perché lascerò qui il codice per i lotti:
from weaviate.util import generate_uuid5with client.batch( batch_size=200, # Specificare la dimensione del lotto num_workers=2, # Parallelizzare il processo) as batch: for _, row in df.iterrows(): question_object = { "category": row.category, "question": row.question, "answer": row.answer, } batch.add_data_object( question_object, class_name="JeopardyQuestion", uuid=generate_uuid5(question_object) )
Anche se Weaviate genererà automaticamente un identificatore univoco universale (uuid
), genereremo manualmente l’uuid
con la funzione generate_uuid5()
dall’question_object
per evitare l’importazione di elementi duplicati.
Per un controllo di coerenza, puoi verificare il numero di oggetti importati con il seguente frammento di codice:
client.query.aggregate("JeopardyQuestion").with_meta_count().do()
{'data': {'Aggregate': {'JeopardyQuestion': [{'meta': {'count': 100}}]}}}
Come interrogare il database vettoriale di Weaviate
L’operazione più comune che eseguirai con un database vettoriale è recuperare gli oggetti. Per recuperare gli oggetti, interroghi il database vettoriale di Weaviate con la funzione get()
:
client.query.get( <Classe>, [<proprietà>]).<argomenti>.do()
Classe
: specifica il nome della classe degli oggetti da recuperare. Qui:"JeopardyQuestion"
proprietà
: specifica le proprietà degli oggetti da recuperare. Qui: una o più di"category"
,"question"
e"answer"
.argomenti
: specifica i criteri di ricerca per recuperare gli oggetti, come limiti o aggregazioni. Ne copriremo alcuni negli esempi seguenti.
Recuperiamo alcune voci dalla classe JeopardyQuestion
con la funzione get()
per vedere come sono fatte. Nell’analogia con Pandas, puoi pensare a ciò come a df.head(2)
. Poiché la risposta della funzione get()
è in formato JSON, importeremo la libreria correlata per visualizzare il risultato in un formato gradevole alla vista.
import jsonres = client.query.get("JeopardyQuestion", ["question", "answer", "category"]) .with_additional(["id", "vector"]) .with_limit(2) .do()print(json.dumps(res, indent=4))
{ "data": { "Get": { "JeopardyQuestion": [ { "_additional": { "id": "064fee53-f8fd-4513-9294-432170cc9f77", "vector": [ -0.02465364, ...] # Il vettore è troncato per una migliore leggibilità }, "answer": "(Lou) Gehrig", "category": "ESPN's TOP 10 ALL-TIME ATHLETES", "question": "No. 10: FB/LB for Columbia U. in the 1920s; MVP for the Yankees in '27 & '36; \"Gibraltar in Cleats\"" }, { "_additional": { "id": "1041117a-34af-40a4-ad05-3dae840ad6b9", "vector": [ -0.031970825, ...] # Il vettore è troncato per una migliore leggibilità }, "answer": "Jim Thorpe", "category": "ESPN's TOP 10 ALL-TIME ATHLETES", "question": "No. 2: 1912 Olympian; football star at Carlisle Indian School; 6 MLB seasons with the Reds, Giants & Braves" }, ] } }}
Nella porzione di codice sopra, puoi vedere che stiamo recuperando oggetti dalla classe “JeopardyQuestion”. Abbiamo specificato di recuperare le proprietà “category”, “question” e “answer”.
Abbiamo specificato due ulteriori argomenti: Prima, abbiamo specificato con l’argomento “.with_additional()” di recuperare ulteriori informazioni sull’id dell’oggetto e l’embedding vettoriale. E con l’argomento “.with_limit(2)”, abbiamo specificato di recuperare solo due oggetti. Questa limitazione è importante e la vedrai di nuovo negli esempi successivi. Questo perché il recupero di oggetti da un database vettoriale non restituisce corrispondenze esatte ma restituisce oggetti in base alla similarità, che deve essere limitata da una soglia.
Ricerca vettoriale
Ora siamo pronti per fare una ricerca vettoriale! Ciò che è interessante nel recuperare informazioni da un database vettoriale è che puoi ad esempio dire di recuperare domande Jeopardy correlate ai “concepts” legati agli animali.
Per fare ciò, possiamo utilizzare l’argomento “.with_near_text()” e passare i “concepts” di nostro interesse come mostrato di seguito:
res = client.query.get( "JeopardyQuestion", ["question", "answer", "category"])\ .with_near_text({"concepts": "animals"})\ .with_limit(2)\ .do()
Il “vectorizer” specificato converte quindi il testo di input (“animals”) in un embedding vettoriale e recupera i due risultati più vicini:
{ "data": { "Get": { "JeopardyQuestion": [ { "answer": "un polpo", "category": "VEDI E DICI", "question": "Di' il nome di <a href=\"http://www.j-archive.com/media/2010-07-06_DJ_26.jpg\" target=\"_blank\">questo</a> tipo di mollusco che vedi" }, { "answer": "la formica", "category": "PAROLE DI 3 LETTERE", "question": "Nel titolo di una favola di Esopo, questo insetto era protagonista insieme a una cavalletta" } ] } }}
Puoi già vedere quanto sia interessante: possiamo vedere che la ricerca vettoriale ha restituito due domande in cui la risposta è un animale appartenente a due categorie completamente diverse. Con una ricerca classica per parole chiave, avresti dovuto prima definire una lista di animali e poi recuperare tutte le domande che contengono uno degli animali definiti.
Risposta alle domande
Rispondere alle domande è uno degli esempi più popolari quando si tratta di combinare LLM (Linguistic Language Model) con database vettoriali.
Per abilitare la risposta alle domande, è necessario specificare un “vectorizer” (che dovresti già avere) e un modulo di risposta alle domande nella configurazione del modulo, come mostrato nell’esempio seguente:
# Impostazioni del modulo "moduleConfig": { "text2vec-openai": { ... }, "qna-openai": { "model": "text-davinci-002" } },
Per la risposta alle domande, è necessario aggiungere l’argomento “.with_ask()” e recuperare anche le proprietà “_additional”.
ask = { "question": "Quale animale è stato menzionato nel titolo della favola di Esopo?", "properties": ["answer"]}res = ( client.query .get("JeopardyQuestion", [ "question", "_additional {answer {hasAnswer property result} }" ]) .with_ask(ask) .with_limit(1) .do())
Il pezzo di codice sopra cerca tutte le domande che potrebbero contenere la risposta alla domanda “Quale animale è stato menzionato nel titolo della favola di Esopo?” e restituisce la risposta “La formica”.
{ "JeopardyQuestion": [ { "_additional": { "answer": { "hasAnswer": true, "property": "", "result": " La formica" } }, "question": "Nel titolo di una favola di Esopo, questo insetto era protagonista insieme a una cavalletta" } ]}
Ricerca generativa
Integrando LLM, è possibile trasformare i dati prima di restituire il risultato della ricerca. Questo concetto è chiamato ricerca generativa.
Per abilitare la ricerca generativa, è necessario specificare un modulo generativo nella configurazione del modulo, come mostrato nell’esempio seguente:
# Impostazioni del modulo "moduleConfig": { "text2vec-openai": { ... }, "generative-openai": { "model": "gpt-3.5-turbo" } },
Per la ricerca generativa, è sufficiente aggiungere l’argomento with_generate()
al codice di ricerca vettoriale precedente, come mostrato di seguito:
res = client.query.get( "JeopardyQuestion", ["question", "answer"])\ .with_near_text({"concepts": ["animals"]})\ .with_limit(1)\ .with_generate(single_prompt= "Genera una domanda alla quale la risposta è {answer}")\ .do()
Il codice precedente fa quanto segue:
- Cerca la domanda più vicina al concetto di
"animals"
- Restituisce la domanda
"Di' il nome di questo tipo di mollusco che vedi"
con la risposta"un polpo"
- Genera un completamento per il prompt
"Genera una domanda alla quale la risposta è un polpo"
con il risultato finale:
{ "generate": { "error": null, "singleResult": "Che creatura marina ha otto braccia ed è conosciuta per la sua intelligenza e le sue abilità di mimetizzazione?" }}
Riepilogo
La popolarità dello spazio LLM non ha solo portato a molti nuovi strumenti interessanti per gli sviluppatori come LangChain o LLaMaIndex. Ci ha anche mostrato come utilizzare strumenti già esistenti, come i database vettoriali, per migliorare il potenziale delle applicazioni basate su LLM.
In questo articolo, abbiamo iniziato a esplorare Weaviate per utilizzare non solo i database vettoriali per la ricerca vettoriale, ma anche per la ricerca di domande e risposte e la ricerca generativa in combinazione con LLM.
Se sei interessato a una guida più approfondita, ti consiglio di dare un’occhiata a questo completo corso in quattro parti sui database vettoriali e Weaviate:
1. Da zero a MVP | Weaviate – database vettoriale
Panoramica del corso
weaviate.io
Avvertenza: Sono un Developer Advocate presso Weaviate al momento della stesura di questo articolo.
Ti è piaciuta questa storia?
Iscriviti gratuitamente per ricevere una notifica quando pubblico una nuova storia.
Vuoi leggere più di 3 storie gratuite al mese? – Diventa membro di VoAGI a 5$/mese. Puoi supportarmi utilizzando il mio link di riferimento quando ti registri. Riceverò una commissione senza costi aggiuntivi per te.
Unisciti a VoAGI con il mio link di riferimento – Leonie Monigatti
Come membro di VoAGI, una parte della tua quota di iscrizione viene destinata agli scrittori che leggi e hai accesso completo a ogni storia…
VoAGI.com
Trovami su LinkedIn, Twitter e Kaggle!
Riferimenti
Dataset
[1] Ulrik Thyge Pedersen (2023). Oltre 200.000 domande Jeopardy nei Kaggle Datasets.
Licenza: Attribuzione 4.0 Internazionale (CC BY 4.0)
Riferimenti immagine
Se non diversamente specificato, tutte le immagini sono create dall’autore.
Web e letteratura
[2] Weaviate (2023). Documentazione di Weaviate (consultato il 14 luglio 2023)