Costruire Applicazioni RAG con Contenuti Web

Introduzione

Negli ultimi anni, i Large Language Models (LLMs), come ChatGPTLLAMA e Mistral, hanno rivoluzionato il modo in cui interagiamo con l’intelligenza artificiale. Questi modelli sono in grado di generare testo simile a quello umano, creare chatbot personalizzati e molto altro. Tuttavia, c’è un limite significativo: questi modelli sono vincolati alle informazioni su cui sono stati addestrati e non possono aggiornarsi autonomamente con nuove conoscenze. Questo rappresenta un problema quando si tratta di rispondere a domande specifiche o legate a eventi recenti.

Ecco dove entra in gioco il Retrieval-Augmented Generation (RAG). Il RAG permette di integrare informazioni contestuali in tempo reale nei LLMs, migliorando la pertinenza e l’accuratezza delle risposte. Una delle fonti più ricche di informazioni contestuali è il contenuto dei siti web.

In questo articolo, esploreremo come estrarre contenuti da siti web e utilizzarli per migliorare le risposte dei LLMs in un’applicazione RAG. Copriremo tutto, dalle basi del web scraping alle strategie di chunking e alla creazione di embedding vettoriali per un recupero efficiente. Pronti? Iniziamo!

Web Scraping: Le Basi

Il primo passo per integrare il contenuto di un sito web in un sistema RAG è estrarre i dati. Questo processo è noto come web scraping. Mentre alcuni siti web offrono API per accedere ai loro dati, molti non lo fanno. In questi casi, il web scraping diventa essenziale.

Esistono diverse librerie Python che possono aiutare in questo compito. In questo esempio, utilizzeremo Beautiful Soup per analizzare il contenuto HTML e requests per effettuare richieste HTTP. Per contenuti più dinamici o per progetti su larga scala, strumenti come Selenium o Scrapy possono essere più adatti.

Esempio: Scraping di una Pagina Wikipedia

Ecco un esempio di come estrarre il contenuto di una pagina Wikipedia utilizzando BeautifulSoup:

import requests from bs4 import BeautifulSoup # Invia una richiesta alla pagina Wikipedia di Data Science response = requests.get( url="https://en.wikipedia.org/wiki/Data_science", ) # Analizza il contenuto HTML soup = BeautifulSoup(response.content, 'html.parser') # Estrai il contenuto testuale del corpo principale dell'articolo content = soup.find(id="bodyContent") print(content.text)

Questo codice invia una richiesta alla pagina Wikipedia di Data Science, estrae il contenuto del corpo principale e lo stampa per ulteriori elaborazioni.

Chunking: Suddividere il Contenuto

Una volta estratto il contenuto, il passo successivo è suddividerlo in chunk (blocchi). Questo processo è importante per diversi motivi:

  • Granularità: Suddividere il testo in parti più piccole facilita il recupero delle informazioni più rilevanti.
  • Semantica Migliorata: Utilizzare un singolo embedding per un intero documento può portare alla perdita di informazioni significative.
  • Efficienza: Chunk più piccoli rendono il processo di embedding più efficiente.

Chunking a Dimensione Fissa vs. Chunking Basato sul Contesto

Esistono due metodi principali per il chunking: a dimensione fissa e basato sul contesto. Il primo divide il testo in intervalli predefiniti, mentre il secondo adatta la dimensione dei chunk in base ai confini delle frasi o dei paragrafi.

In questa guida, utilizzeremo il RecursiveCharacterTextSplitter del framework LangChain per suddividere il testo in chunk di circa 512 caratteri, mantenendo i punti di divisione logici.

from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=512, # Imposta la dimensione del chunk a 512 caratteri length_function=len ) chunked_text = text_splitter.split_text(content.text)

Da Chunk a Embedding Vettoriale

Una volta ottenuti i chunk di testo, il passo successivo è convertirli in embedding vettoriali. Gli embedding sono rappresentazioni numeriche del testo che catturano il significato semantico, permettendo confronti di similarità efficienti.

Tipi di Embedding

Esistono due tipi principali di embedding:

  • Embedding Densi: Generati da modelli di deep learning come quelli di OpenAI o Sentence Transformers. Sono ottimi per catturare la similarità semantica.
  • Embedding Sparsi: Generati da metodi classici come TF-IDF o BM25. Sono efficaci per la similarità basata su parole chiave.

Per la nostra applicazione RAG, utilizzeremo embedding densi generati dal modello all-MiniLM-L6-v2 di Sentence Transformers.

from langchain.embeddings import SentenceTransformerEmbeddings # Carica il modello per generare gli embedding embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2") # Crea l'embedding per il terzo chunk di testo chunk_embedding = embeddings.embed_documents([chunked_text[3]])

Memorizzare e Recuperare Embedding con Milvus

Dopo aver generato gli embedding, è necessario memorizzarli in un database vettoriale per un recupero efficiente. Milvus è un database vettoriale open-source specializzato nella memorizzazione e ricerca di embedding. Si integra bene con LangChain, rendendolo una scelta eccellente per applicazioni RAG.

Ecco come memorizzare i chunk di embedding in Milvus:

from langchain.vectorstores.milvus import Milvus # Memorizza gli embedding in Milvus vector_db = Milvus.from_texts(texts=chunked_text, embedding=embeddings, collection_name="rag_milvus")

Costruire la Pipeline RAG

Con i chunk memorizzati e gli embedding pronti, è il momento di costruire la nostra pipeline RAG. Questa pipeline recupererà gli embedding più rilevanti in base alle query dell’utente e li passerà al LLM per generare risposte.

Passo 1: Configurare il Retriever

Il primo passo è configurare un retriever che recuperi gli embedding più rilevanti dal database vettoriale in base alla query dell’utente.

retriever = vector_db.as_retriever()

Passo 2: Inizializzare il LLM

Successivamente, inizializziamo il nostro modello di linguaggio utilizzando GPT-3.5-turbo di OpenAI:

from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-3.5-turbo-0125")

Passo 3: Definire un Prompt Personalizzato

Creiamo un template di prompt che guiderà il LLM a generare risposte appropriate basate sul contenuto recuperato.

from langchain_core.prompts import PromptTemplate template = """Usa i seguenti pezzi di contesto per rispondere alla domanda alla fine. Se non conosci la risposta, di' semplicemente che non lo sai, non cercare di inventare una risposta. Usa un massimo di tre frasi e mantieni la risposta concisa. Alla fine della risposta, di' sempre "grazie per aver chiesto!". {context} Domanda: {question} Risposta Utile:""" custom_rag_prompt = PromptTemplate.from_template(template)

Passo 4: Costruire la Catena RAG

Infine, creiamo la catena RAG che recupererà i chunk più rilevanti, li passerà al LLM e genererà la risposta.

from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | custom_rag_prompt | llm | StrOutputParser() )

Con la catena RAG configurata, possiamo inviare una query alla pipeline e ricevere una risposta basata sul contenuto del sito web.

for chunk in rag_chain.stream("Cos'è un Data Scientist?"): print(chunk, end="", flush=True)

Conclusione

In questa guida, abbiamo esplorato come estrarre contenuti da siti web e utilizzarli per migliorare le risposte dei LLMs in un’applicazione RAG. Abbiamo discusso il web scraping, il chunking del testo, la generazione di embedding vettoriali e la memorizzazione di questi embedding in un database vettoriale come Milvus.

Utilizzando questa tecnica, puoi sviluppare applicazioni AI più informate e consapevoli del contesto. Che tu stia creando un chatbot o un sistema di risposta alle domande, il RAG migliora la pertinenza e l’accuratezza delle risposte generate.

Ricorda che il successo della tua pipeline RAG dipende dalla qualità dei dati e da come organizzi i chunk, gli embedding e il processo di recupero. Sperimenta con diversi modelli, dimensioni dei chunk e metodi di recupero per affinare il tuo sistema.

Buon coding e grazie per la lettura!

Domande Frequenti (FAQ)

1. Cos’è il RAG?
Il Retrieval-Augmented Generation (RAG) è una tecnica che combina il recupero di informazioni contestuali con la generazione di testo da parte di un LLM, migliorando la pertinenza e l’accuratezza delle risposte.

2. Quali sono i vantaggi del chunking?
Il chunking migliora la granularità, la semantica e l’efficienza del processo di embedding, rendendo più facile recuperare informazioni rilevanti.

3. Quali database vettoriali posso usare oltre a Milvus?
Oltre a Milvus, puoi utilizzare database vettoriali come FAISSPinecone o Weaviate.

Risorse Aggiuntive

Spero che questa guida ti sia stata utile! Se hai domande o vuoi approfondire, non esitare a lasciare un commento. Buona programmazione!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Translate »
Torna in alto