Potenzia la tua ricerca con un assistente AI LLM adattato alle tue esigenze

Migliora la tua ricerca con un assistente AI LLM personalizzato alle tue esigenze

Introduzione

In un mondo pieno di informazioni, accedere ed estrapolare dati rilevanti in modo efficiente è prezioso. ResearchBot è un progetto di applicazione all’avanguardia alimentato da LLM che utilizza le capacità del modello di linguaggio LLM di OpenAI (Modelli di Linguaggio Avanzati) con Langchain per il recupero di informazioni. Questo articolo è come un manuale passo-passo per creare il tuo ResearchBot e come può essere utile nella vita reale. È come avere un assistente intelligente che trova le informazioni di cui hai bisogno tra un mare di dati. Che tu ami il coding o sia interessato all’intelligenza artificiale, questa guida è qui per aiutarti ad arricchire la tua ricerca con un assistente AI su misura alimentato da LLM. È il tuo percorso per sbloccare il potenziale di LLM e rivoluzionare il modo in cui accedi alle informazioni.

Obiettivi di Apprendimento

  • Comprendere i concetti più approfonditi di LLM (Large Language Models), Langchain, Database di Vettori ed Embeddings.
  • Esplorare le applicazioni del mondo reale di LLM e ResearchBot in campi come la ricerca, il supporto clienti e la generazione di contenuti.
  • Scoprire le migliori pratiche per integrare ResearchBot in progetti o flussi di lavoro esistenti, migliorando produttività e processo decisionale.
  • Costruire ResearchBot per semplificare il processo di estrazione dati e risposta a domande.
  • Restare aggiornato sulle tendenze nella tecnologia LLM e sul suo potenziale per rivoluzionare il modo in cui accediamo e utilizziamo le informazioni.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Cos’è ResearchBot?

ResearchBot è un assistente di ricerca alimentato da LLM. È uno strumento innovativo che può accedere e riassumere contenuti in modo rapido, diventando un ottimo partner per professionisti di diverse industrie.

Immagina di avere un assistente personalizzato che può leggere e comprendere molti articoli, documenti e pagine web e fornirti riassunti pertinenti e concisi. Il nostro obiettivo con ResearchBot è ridurre il tempo e lo sforzo necessario per i tuoi scopi di ricerca.

Casi d’Uso del Mondo Reale

  • Analisi Finanziaria: Resta aggiornato sulle ultime notizie di mercato e ricevi risposte immediate a domande finanziarie.
  • Giornalismo: Raccogli informazioni di base, fonti e referenze per articoli in modo efficiente.
  • Assistenza Sanitaria: Accedi a ricerche mediche attuali e riepiloghi per scopi di ricerca.
  • Accademico: Trova articoli accademici rilevanti, materiali di ricerca e risposte alle domande di ricerca.
  • Ricerca Legale: Recupera documenti legali, sentenze e approfondimenti su questioni legali velocemente.

Terminologia Tecnica

Database di Vettori

Un contenitore per memorizzare gli embeddings vettoriali dei dati di testo è fondamentale per ricerche basate sulla similarità efficienti.

Comprendere l’intento e il contesto della query dell’utente per effettuare ricerche senza dipendere esclusivamente dalla corrispondenza perfetta delle parole chiave.

Embedding

Una rappresentazione numerica dei dati di testo che consente una comparazione e ricerca efficienti.

Architettura Tecnica del Progetto

Architettura Tecnica
  • Utilizziamo il modello di embedding per creare embeddings vettoriali per le informazioni o il contenuto che desideriamo indicizzare.
  • L’embedding vettoriale viene inserito nel database di vettori, facendo riferimento al contenuto originale da cui è stato creato.
  • Quando l’applicazione emette una query, utilizziamo lo stesso modello di embedding per creare embeddings per la query, e utilizziamo questi embeddings per interrogare il database in cerca di embeddings vettoriali simili.
  • Questi embeddings simili sono associati al contenuto originale da cui sono stati creati.

Come Funziona ResearchBot?

Lavoro

Questa architettura facilita la memorizzazione, il recupero e l’interazione con il contenuto, rendendo il nostro ResearchBot uno strumento potente per il recupero e l’analisi delle informazioni. Sfrutta gli embedding vettoriali e un database vettoriale per facilitare ricerche di contenuti rapide e precise.

Componenti

  1. Documenti: Sono gli articoli o i contenuti che si desidera indicizzare per futuri riferimenti e recupero.
  2. Divisioni: Questo gestisce il processo di suddivisione dei documenti in parti più piccole e gestibili. Questo è importante per lavorare con documenti o articoli di grandi dimensioni, garantendo che si adattino perfettamente ai vincoli del modello linguistico e per un indicizzazione efficiente.
  3. Database vettoriale: Il database vettoriale è una parte cruciale dell’architettura. Memorizza gli embedding vettoriali generati dal contenuto. Ogni vettore è associato al contenuto originale da cui è stato derivato, creando un collegamento tra la rappresentazione numerica e il materiale di origine.
  4. Recupero: Quando un utente effettua una query al sistema, lo stesso modello di embedding viene utilizzato per creare embedding per la query. Questi embedding della query vengono quindi utilizzati per cercare nel database vettoriale altri embedding vettoriali simili. Il risultato è un grande gruppo di vettori simili, ciascuno associato alla sua origine di contenuto originale.
  5. Prompt: Viene definito dove l’utente interagisce con il sistema. Gli utenti inseriscono query e il sistema elabora queste query per recuperare informazioni rilevanti dal database vettoriale, fornendo risposte e riferimenti al contenuto di origine.

Caricatori di documenti in LangChain

Utilizzare i caricatori di documenti per caricare dati da una sorgente sotto forma di documento. Un documento è un pezzo di testo e metadati associati. Ad esempio, ci sono caricatori di documenti per caricare un semplice file .txt, per caricare i contenuti di testo di articoli o blog, o anche per caricare una trascrizione di un video di YouTube.

Esistono molti tipi di caricatori di documenti:

Esempio – TextLoader

Questo codice mostra la funzionalità di un TextLoader dal Langchain. Carica i dati di testo dal file esistente, “Langchain.txt”, nella classe TextLoader, preparandolo per ulteriori elaborazioni. La variabile ‘file_path’ memorizza il percorso del file che viene caricato per utilizzi futuri.

# Importa la classe TextLoader dal modulo langchain.document_loadersfrom langchain.document_loaders import TextLoader# Considera la classe TextLoader specificando il file da caricare, qui "Langchain.txt"loader = TextLoader("Langchain.txt")# Carica il contenuto dal file fornito ("Langchain.txt") nella classe TextLoaderloader.load()# Verifica il tipo dell'istanza 'loader', che dovrebbe essere 'TextLoader'type(loader)# Il percorso del file associato al TextLoader nella variabile 'file_path'loader.file_path
TextLoaders

Divisori di testo in LangChain

I divisori di testo sono responsabili di suddividere un documento in documenti più piccoli. Queste unità più piccole rendono più facile lavorare con il contenuto in modo efficiente. Nel contesto del nostro progetto ResearchBot, utilizziamo i divisori di testo per preparare i dati per ulteriori analisi e recupero.

Perché abbiamo bisogno di divisori di testo?

Gli LLM hanno limiti di token. Pertanto, è necessario suddividere il testo, che può essere ampio, in frammenti più piccoli in modo che ogni frammento sia inferiore al limite di token.

Approccio manuale per suddividere il testo in frammenti

# Prendendo del testo casuale da Wikipediatext# Supponiamo che il limite di token dell'LLM sia 100, nel nostro codice possiamo fare una cosa semplice come questotext[:100]
testo
pezzo

Ebbene, vogliamo parole complete e vogliamo farlo per l’intero testo, potremmo utilizzare la funzione di divisione di Python

parole = testo.split(" ")len(parole)spezzoni = []s = ""for parola in parole:    s += parola + " "    if len(s)>200:        spezzoni.append(s)        s = ""        spezzoni.append(s)spezzoni[:2]
Spezzoni

La suddivisione dei dati in spezzoni può essere fatta in python nativo, ma è un processo tedioso. Inoltre, se necessario, potresti dover sperimentare con più delimitatori in modo consecutivo per assicurarti che ciascun spezzone non superi il limite di lunghezza del token del rispettivo LLM.

Langchain fornisce un modo migliore attraverso le classi divise-testo. Ci sono diverse classi di divise-testo in langchain che ci permettono di farlo.

1. Carattere spezzatore di testo

Questa classe è progettata per suddividere il testo in spezzoni più piccoli, in base a separatori specifici. Come paragrafi, periodi, virgole e interruzioni di linea (\n). È più utile per suddividere il testo in una combinazione di spezzoni per ulteriori elaborazioni.

from langchain.text_splitter import CharacterTextSplitterspezzatore = CharacterTextSplitter(    separator = "\n",    dim_spezzone=200,    sovrapposizione_spezzone=0)spezzoni = spezzatore.spezza_testo(testo)len(spezzoni)for spezzone in spezzoni:    print(len(spezzone))
Carattere spezzatore di testo

Come puoi vedere, anche se abbiamo dato una dimensione di spezzone di 200, poiché la divisione era basata su \n, ha finito per creare spezzoni più grandi della dimensione 200.

Un’altra classe di Langchain può essere utilizzata per dividere in modo ricorsivo il testo in base a una lista di separatori. Questa classe è RecursiveTextSplitter. Vediamo come funziona.

2. Spezzatore ricorsivo di testo

Si tratta di una sorta di spezzatore di testo che opera analizzando in modo ricorsivo i caratteri in un testo. Cerca di suddividere il testo per diversi caratteri, trovando in modo iterativo diverse combinazioni di caratteri finché non identifica un approccio di suddivisione che divide efficacemente il testo e diversi tipi di gusci.

from langchain.text_splitter import RecursiveCharacterTextSplitterr_spezzatore = RecursiveCharacterTextSplitter(    separatori = ["\n\n", "\n", " "],  # Lista di separatori     dim_spezzone = 200,  # dimensione di ogni spezzone creato    sovrapposizione_spezzone  = 0,  # dimensione sovrapposizione tra spezzoni     funzione_lunghezza = len  # Funzione per calcolare la dimensione,)spezzoni = r_spezzatore.spezza_testo(testo)for spezzone in spezzoni:    print(len(spezzone))    prima_suddivisione = testo.split("\n\n")[0]len(prima_suddivisione)  seconda_suddivisione = prima_suddivisione.split("\n")seconda_suddivisionefor suddivisione in seconda_suddivisione:    print(len(suddivisione))    seconda_suddivisione[2]seconda_suddivisione[2].split(" ")
spezzatore

Vediamo come abbiamo formato questi spezzoni:

prima_suddivisione

Lo splitter di testo ricorsivo utilizza una lista di separatori, ovvero separators = [“\n\n”, “\n”, “.”]

Quindi ora dividerà prima utilizzando \n\n e poi, se la dimensione del chunk risultante è maggiore del parametro chunk_size, che in questa scena è 200, utilizzerà il prossimo separatore, ovvero \n.

second_split

La terza divisione supera la dimensione del chunk di 200. Ora cercherà di dividerla ulteriormente utilizzando il terzo separatore, ovvero ’ ’ (spazio)

final_split

Quando viene diviso utilizzando lo spazio (ovvero second_split[2].split(” “)), separerà ogni parola e successivamente unirà quelle parti in modo che la loro dimensione sia vicina a 200.

Database vettoriale

Ora, considera uno scenario in cui è necessario archiviare milioni o addirittura miliardi di word embedding, sarebbe una scena importante in un’applicazione del mondo reale. I database relazionali, pur essendo in grado di archiviare dati strutturati, potrebbero non essere adatti a causa dei loro limiti nella gestione di tali quantità di dati.

Qui entrano in gioco i Database vettoriali. Un Database vettoriale è progettato per archiviare ed estrarre in modo efficiente dati vettoriali, rendendolo adatto per gli embedding di parole.

I Database vettoriali stanno rivoluzionando il recupero delle informazioni utilizzando la ricerca semantica. Sfruttano la potenza degli embedding di parole e delle tecniche di indicizzazione intelligente per rendere le ricerche più rapide e accurate.

Qual è la differenza tra un indice vettoriale e un database vettoriale?

Gli indici vettoriali autonomi come FAISS (Facebook AI Similarity Search) possono migliorare la ricerca e il recupero di embedding vettoriali, ma mancano delle capacità che esistono in uno dei database. I database vettoriali, d’altra parte, sono appositamente progettati per gestire gli embedding vettoriali, offrendo diversi vantaggi rispetto all’uso di indici vettoriali autonomi.

FAISS

Passaggi:

1: Creare gli embedding di origine per la colonna di testo

2: Creare un indice FAISS per i vettori

3: Normalizzare i vettori di origine e aggiungerli all’indice

4: Codificare il testo di ricerca utilizzando lo stesso codificatore e normalizzare il vettore di output

5: Cercare un vettore simile nell’indice FAISS creato

df = pd.read_csv("sample_text.csv")df# Passaggio 1: Creare gli embedding di origine per la colonna di testofrom sentence_transformers import SentenceTransformerencoder = SentenceTransformer("all-mpnet-base-v2")vectors = encoder.encode(df.text)vectors# Passaggio 2: Creare un indice FAISS per i vettoriimport faissindex = faiss.IndexFlatL2(dim)# Passaggio 3: Normalizzare i vettori di origine e aggiungerli all'indiceindex.add(vectors)index# Passaggio 4: Codificare il testo di ricerca utilizzando lo stesso codificatoresearch_query = "looking for places to visit during the holidays"vec = encoder.encode(search_query)vec.shapesvec = np.array(vec).reshape(1,-1)svec.shape# Passaggio 5: Cercare un vettore simile nell'indice FAISSdistances, I = index.search(svec, k=2)distancesrow_indices = I.tolist()[0]row_indicesdf.loc[row_indices]

Se controlliamo questo dataset,

data

convertiremo questi testi in vettori utilizzando gli embedding di parole

vectors

Considerando la mia ricerca_query = “sto cercando posti da visitare durante le vacanze”

Risultati

Sta fornendo i 2 risultati più simili correlati alla mia query usando la ricerca semantica della categoria Viaggi.

Quando esegui una query di ricerca, il database utilizza tecniche come Locality-Sensitive Hashing (LSH) per velocizzare il processo. LSH raggruppa vettori simili in bucket, consentendo ricerche più veloci e mirate. Ciò significa che non è necessario confrontare il vettore della tua query con ogni vettore archiviato.

Recupero

Quando un utente interroga il sistema, viene utilizzato lo stesso modello di incorporamento per creare incorporamenti per la query. Questi incorporamenti della query vengono quindi utilizzati per cercare nel database vettoriale gli incorporamenti vettoriali simili. Il risultato è un gruppo di vettori simili, ciascuno associato alla propria origine di contenuto originale.

Sfide del Recupero

Il recupero nella ricerca semantica presenta diverse sfide come il limite di token imposto dai modelli di linguaggio come GPT-3. Quando si tratta di più frammenti di dati rilevanti, si verifica il superamento del limite delle risposte.

Metodo Stuff

In questo modello, si tratta di raccogliere tutti i frammenti di dati rilevanti dal database vettoriale e combinarli in un prompt (individuale). Lo svantaggio principale di questo processo è il superamento del limite dei token, il che comporta risposte incomplete.

Stuff

Metodo Map Reduce

Per superare la sfida del limite dei token e ottimizzare il processo di domande e risposte, questo metodo fornisce una soluzione che invece di combinare i frammenti rilevanti in un prompt (individuale), se ci sono 4 frammenti. Passali tutti attraverso LLM isolati e discreti. Queste domande forniscono informazioni contestuali che consentono al modello di linguaggio di concentrarsi sul contenuto di ogni frammento in modo indipendente. Questo porta a un insieme di singole risposte per ogni frammento. Infine, viene effettuata una chiamata finale a LLM per combinare tutte queste risposte singole al fine di trovare la migliore risposta basata sugli insight raccolti da ogni frammento.

Map Reduce

Flusso di lavoro di ResearchBot

(1) Carica i Dati

In questa fase, i dati, come testo o documenti, vengono importati e resi pronti per ulteriori elaborazioni, rendendoli disponibili per l’analisi.

#fornisci gli URL per scaricare i dati loaders = UnstructuredURLLoader(urls=[    "",    ""])data = loaders.load() len(data)

(2) Dividi i Dati per Creare Frammenti

I dati vengono divisi in sezioni o frammenti più piccoli e gestibili, facilitando l’elaborazione di testi o documenti di grandi dimensioni.

text_splitter = RecursiveCharacterTextSplitter(    chunk_size=1000,    chunk_overlap=200)# usa split_documents invece di split_text per ottenere i frammenti.docs = text_splitter.split_documents(data)len(docs)docs[0]

(3) Crea gli Incorporamenti per questi Frammenti e Salvali nell’Indice FAISS

I frammenti di testo vengono convertiti in rappresentazioni vettoriali numeriche (incorporamenti) e memorizzati in un indice Faiss, ottimizzando il recupero di vettori simili.

# Crea gli incorporamenti dei frammenti usando OpenAIEmbeddingsembeddings = OpenAIEmbeddings()# Passa i documenti e gli incorporamenti per creare l'indice vettoriale FAISSvectorindex_openai = FAISS.from_documents(docs, embeddings)# Salva l'indice vettoriale creato in un file localefile_path="vector_index.pkl"with open(file_path, "wb") as f:    pickle.dump(vectorindex_openai, f)        if os.path.exists(file_path):    with open(file_path, "rb") as f:        vectorIndex = pickle.load(f)

(4) Recupera le Embedding Simili per una Domanda Data e Chiama LLM per Recuperare la Risposta Finale

Per una determinata query, recuperiamo embedding simili e utilizziamo questi vettori per interagire con un modello di linguaggio (LLM) al fine di semplificare il recupero delle informazioni e fornire la risposta finale alla domanda dell’utente.

# Inizializza LLM con i parametri necessarillm = OpenAI(temperature=0.9, max_tokens=500) chain = RetrievalQAWithSourcesChain.from_llm(  llm=llm,   retriever=vectorIndex.as_retriever())chainquery = "" # chiedi la tua query langchain.debug=Truechain({"question": query}, return_only_outputs=True)

Applicazione Finale

Dopo aver utilizzato tutte queste fasi (Caricatore di Documenti, Divisore di Testo, DB di Vettori, Recupero, Prompt) e aver costruito un’applicazione con l’aiuto di streamlit, abbiamo completato la costruzione della nostra ResearchBot.

URL

Questa è una sezione della pagina dove vengono inseriti gli URL dei blog o degli articoli. Ho inserito i link degli ultimi iPhone usciti nel 2023. Prima di iniziare a costruire questa applicazione ResearchBot, tutti si chiederanno perché abbiamo già ChatGPT, quindi perché stiamo costruendo questo ResearchBot. Ecco la risposta:

Risposta di ChatGPT:

ChatGPT

Risposta di ResearchBot:

Research Bot

Qui, la mia domanda è “Qual è il prezzo dell’iPhone 15 di Apple?”

Questi dati sono del 2023 e non sono disponibili con ChatGPT 3.5, ma abbiamo addestrato il nostro ResearchBot con le ultime informazioni sugli iPhone. Quindi abbiamo ottenuto la risposta richiesta dal nostro ResearchBot.

Questi sono i 3 problemi nell’utilizzo di ChatGPT:

  1. Copiare e incollare il contenuto dell’articolo è un lavoro tedioso.
  2. Abbiamo bisogno di una base di conoscenza aggregata.
  3. Limite di parole: 3000 parole.

Conclusione

Abbiamo assistito ai concetti di ricerca semantica e database di vettori nel contesto del mondo reale. La capacità del nostro ResearchBot di recuperare efficientemente risposte da un Database di Vettori utilizzando la Ricerca Semantica mostra il potenziale incredibile dei modelli di linguaggio profondo (LLM) nel campo del recupero delle informazioni e dei sistemi di domanda-risposta. Abbiamo creato uno strumento estremamente richiesto che facilita la ricerca e il riassunto di informazioni importanti con elevate capacità e funzionalità di ricerca. È una soluzione potente per coloro che cercano conoscenza. Questa tecnologia apre nuovi orizzonti per il recupero delle informazioni e i sistemi di domanda-risposta, rendendola un elemento rivoluzionario per chiunque cerchi conoscenze basate sui dati.

Domande Frequenti

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e vengono utilizzati a discrezione dell’autore.