Scopri di più su ciò che il RAG Cookbook ha da offrire per approfondire le soluzioni RAG di oggi

Un leadspace ricolorato che utilizza come base il leadspace Watson for Customer Care.
Informazioni generali

L'ingestione è il processo di analisi delle informazioni dai documenti sorgente affinché possano essere incorporate in uno spazio di ricerca per un successivo recupero. Sebbene si tratti di un processo semplice per il testo normale, sorgono complicazioni quando i documenti sorgente sono in formati non "testuali", ad esempio Microsoft Word o PDF, e quando contengono formattazioni complesse come intestazioni e piè di pagina ripetuti, testo in più colonne o tabelle.

Questa sezione contiene suggerimenti, tecniche, acceleratori e punti di riferimento per asset che aiutano a superare queste sfide.

Da dove iniziare?

 

Se non sei un utente tecnico, i tuoi documenti sono relativamente semplici e hai bisogno di una soluzione senza uso di codice, usa watsonx Orchestrate.

 

Se sei un utente tecnico e i tuoi documenti sono relativamente semplici e hai accesso a Watson Discovery, puoi iniziare da lì. Watson Discovery ha un'interfaccia utente piacevole da usare, con una necessità minima di codifica. Per documenti complessi, puoi usare Watson Discovery per annotare ed estrarre parti specifiche dei tuoi documenti, sia per la pipeline di backend RAG, sia per il front-end e per evidenziare i testi ottenendo la posizione esatta di un paragrafo all'interno dei tuoi documenti.

 

Tuttavia, Watson Discovery ha i suoi limiti. Se i tuoi documenti sono più grandi di 50 MB, non potrai caricarli in Watson Discovery. Se il documento è troppo complesso (ad es include tabelle annidate o formati di tabella irregolari), Watson Discovery potrebbe non catturare l'intera struttura del documento. In questi casi potrebbe essere opportuno implementare una pipeline di data ingestion personalizzata utilizzando librerie open source.

 

Per quanto riguarda le librerie open source, LangChain e LlamaIndex sono molto simili in termini di capacità di ingestione dati. Tuttavia, LangChain sarebbe più facile da usare rispetto a LlamaIndex in termini di documentazione e integrazione con database vettoriali e altre applicazioni. Sia LangChain che LlamaIndex hanno funzioni helper che possono convertire un oggetto documento da uno all'altro, quindi il passaggio tra LangChain e LlamaIndex può avvenire in qualsiasi punto della pipeline.

Inserimento di documenti con tabelle complesse

Le note seguenti includono diverse lezioni apprese per l'ingestione di documenti che includono tabelle complesse e diversi approcci che sono stati testati durante uno degli incarichi. Si prega di notare che alcune osservazioni possono variare da un incarico all'altro, tuttavia la maggior parte delle conclusioni dovrebbe essere simile per gli incarichi che includono documenti con tabelle annidate e complesse.

Watson Discovery

Watson Discovery non supporta documenti di dimensioni superiori a 50 MB. Nei casi in cui le grandi dimensioni sono dovute al fatto che i documenti originali sono in formato Word, è necessario convertire i documenti Word originali in PDF per poter utilizzare Watson Discovery. Tuttavia, utilizzando questo approccio, Watson Discovery a volte non è in grado di acquisire correttamente i formati di tabella, soprattutto nei casi di tabelle complesse o annidate.

Per l'ingestione tramite Watson Discovery si possono testare due approcci diversi: un modello pre-addestrato e un modello addestrato dall'utente annotando manualmente alcune pagine tramite la Smart Document Understanding di Discovery. Di seguito si discutono le lezioni apprese da questi due approcci per uno degli incarichi che includeva tabelle complesse annidate.

  • Modello pre-addestrato
    • Pro: 
      • L'output HTML di Discovery poteva spesso catturare meglio la struttura delle tabelle rispetto a un modello addestrato dall'utente (SDU)
    • Contro: 
      • Discovery aveva ancora difficoltà a rilevare le tabelle annidate. In alcuni casi, vedeva solo la tabella più interna come tabella e catturava le tabelle esterne come testo, perdendo la struttura dei dati.
    • Quando si utilizza Watson Discovery, l'utilizzo del testo non elaborato può portare a risultati migliori rispetto all'utilizzo di output HTML.  

  • Modello definito dall'utente
    • Pro: 
      • Molto facile da annotare e ottimo per documenti semplici in cui si desidera filtrare sezioni specifiche in un secondo momento.
    • Svantaggi:
      • Non è stato possibile rilevare le strutture delle tabelle annidate, anche se abbiamo annotato tutte le tabelle all'interno delle tabelle.
      • Anche per le tabelle semplici si sono verificati problemi nel rilevarle nei nuovi documenti, anche dopo aver annotato manualmente 20 pagine da più di 7 documenti.

Sebbene i risultati di Watson Discovery non fossero accurati come altre librerie personalizzate menzionate di seguito per tabelle complesse e annidate, i risultati includevano molti metadati come numeri di pagina e coordinate dei testi, che sono utili per mostrare i punti salienti dell'interfaccia utente. Questi metadati possono essere davvero utili per aggiungere filtri aggiuntivi durante il recupero o anche per suddividere i documenti in base a determinate sezioni HTML.

PyPDFLoader e PyMuPDF a confronto

La libreria PyMuPDF è molto più veloce e produce risultati più accurati, specialmente su dati più ampi. Utilizzando le due funzioni seguenti su un set di dati da 50,6 MB, PyMuPDF ha impiegato 0,131 secondi contro 0,366 secondi per PypdfLoader di LangChain. Quindi, per file di grandi dimensioni e se non si desidera estrarre sezioni separate del documento come sezioni separate, utilizzare la libreria PyMuPdf.

def pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None) -> (list[str], list[str], list[dict]):
    """
    Converte PDF in testo normale.

    Params:
        path (str): Percorso per il file PDF.
        start_page (int): Pagina da cui iniziare a ricevere testo.
        end_page (int): Ultima pagina da cui prendere il testo.
    """
    logger.debug("Elaborazione PDF %s".format(path))

    from langchain_community.document_loaders import PyPDFLoader

    loader = PyPDFLoader(path)
    pages = loader.load()
    total_pages = len(pages)
    logger.debug(f'Totale pagine: {total_pages}')
    _ids = []
    _metadata = []
    _file_name = Path(path).name

    if end_page is None:
        end_page = len(pages)

    _text_list = []
    for index in range(start_page - 1, end_page):
        text = pages[index].page_content
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        _text_list.append(text)
        _ids.append(f'page_{index + 1}')
        _metadata.append({
            "id": index + 1,
            "file_name": _file_name,
            "page": f'page {index + 1}'
        })

    return _text_list, _ids, _metadata

def large_pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None,
                        remove_string_list=None) -> (list[str], list[str], list[dict]):
    """
        Converte PDF in testo semplice utilizzando PyMuPDF, una velocità di esecuzione impressionante, che lo rende adatto all'elaborazione
        PDF su larga scala. Sebbene non offra caratteristiche dedicate all'estrazione dalle tabelle, può
        comunque essere utilizzato per analizzare la struttura PDF ed estrarre dati dalle tabelle con ulteriori
        passaggi se necessario.

        50,6 MB -> 0,131 secondi (PyMuPDF) contro 0,366 secondi (pypdf)

Parametri:
file_stream (stream): trasmette in streaming al file PDF.
            path (str): percorso al PDF
            start_page (int): pagina da cui iniziare ad acquisire il testo.
            end_page (int): ultima pagina da cui acquisire il testo.
            remove_string_list (lista[str]): rimuove la lista delle stringhe.
        """
    logger.debug("Elaborazione PDF %s".format(path))
    if remove_string_list is None:
        remove_string_list = []

    import fitz

    # pdf_reader = fitz.open(stream = file_stream, filetype = "pdf")
    pdf_reader = fitz.open(path)
    if end_page is None:
        end_page = pdf_reader.page_count

    _text_list = []
    _ids = []
    _metadata = []
    _file_name = Path(path).name
    for index in range(start_page - 1, end_page):
        text = pdf_reader[index].get_text("text")
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        for remove_string in remove_string_list:
            re_compile = re.compile(remove_string)
            text = re_compile.sub('', text)
            _metadata.append({
                "id": index + 1,
                "file_name": _file_name,
                "page": f'page {index + 1}'
            })
        _ids.append(f'page_{index + 1}')
        _text_list.append(text)

    return _text_list, _ids, _metadata

Caricatore PDF non strutturato per l'inserimento di HTML UnstructuredPDFLoader di LangChain

  • Pro: 
    • Includeva molti metadati come numeri di pagina e coordinate x,y dei testi, simili a Watson Discovery, che potrebbero essere utili per mostrare i punti salienti in futuro.
  • Contro: 
    • Impossibile acquisire correttamente molte tabelle nidificate o complesse. Inoltre, per questo approccio dovremmo convertire ciascuno dei formati docx originali in PDF.

Caricatore di Word non strutturato per l'inserimento HTML UnstructuredWordDocumentLoader di LangChain

  • Pro: 
    • L'utilizzo dei documenti ha portato a una migliore acquisizione delle tabelle e aiuta gli LLM a generare risposte in un contesto migliore.
  • Contro: 
    • I metadati come i numeri di pagina non venivano acquisiti correttamente nemmeno dopo aver aggiunto interruzioni di pagina rispetto al caricatore PDF.

Riassunto delle tabelle HTML

Nel tentativo di preservare la struttura delle tabelle, inseriamo elementi di tabella da HTML estratto (usando una libreria non strutturata) in un LLM (Mixtral per tabelle lunghe è una buona opzione per la dimensione del suo contesto), poi possiamo creare un prompt per riassumere le tabelle oppure usare Few Shot Learning per convertire le tabelle in un formato che pensiamo possa essere più comprensibile dai grandi modelli linguistici.

  • Pro: 
    • Il testo di output sarebbe stato più facile da suddividere e inserire e avrebbe preservato la struttura delle tabelle
  • Contro: 
    • Per le tabelle multipagina di grandi dimensioni, per la richiesta di riepilogo potremmo perdere alcune informazioni, quindi i pochi tentativi di imparare a convertire le tabelle anziché riepilogarle potrebbero produrre risultati migliori.

Sono state testate anche altre librerie di lettura delle tabelle, come camelot e tabula, ma queste librerie non sono risultate accurate come non strutturate per le tabelle complesse e, poiché rilevano solo le tabelle all'interno di un documento, potrebbe essere necessaria una maggiore pre-elaborazione per creare blocchi significativi da passare ai modelli linguistici di grandi dimensioni.

In conclusione, mentre Watson Discovery sarebbe un modo semplice e veloce per assorbire documenti e raccogliere molti metadati aggiuntivi che possono essere utili per costruire l'interfaccia utente (ad esempio per evidenziare i passaggi), la libreria "non strutturata" (che ha wrapper sia in LlamaIndex che in LangChain) potrebbe catturare le tabelle complesse all'interno dei documenti in modo più accurato rispetto a Discovery. Quindi una combinazione di entrambi gli approcci può essere utile per i progetti che includono tabelle complesse nei loro documenti.

Strumenti IBM

Ingestione con watsonx Orchestrate

L'ingestione senza un'interfaccia utente grafica può essere difficile per gli utenti business, in quanto richiede esperienza e conoscenza tecnica. watsonx Orchestrate elimina la frustrazione degli utenti business, implementando un'interfaccia utente drag and drop. Facendo clic sul pulsante blu di caricamento, si avvia la possibilità di caricare e memorizzare direttamente i documenti in watsonx Discovery (alias Elasticsearch).

Schermata del portale drag and drop di watsonx Orchestrate

Questo permette all'utente di caricare direttamente in Elasticsearch la propria documentazione per creare una base di conoscenza personalizzata.

Screenshot della schermata di caricamento del documento in watsonx Orchestrate

Invece di inserire direttamente i documenti in watsonx Discovery, hai anche l'opzione di collegarti a Watson Discovery tramite watsonx Orchestrate. Puoi trovare maggiori dettagli su come inserire i dati tramite Watson Discovery di seguito.

Watson Discovery

Uno degli altri strumenti interni che possono essere utilizzati per l'ingestione è Watson Discovery. Watson Discovery non è un semplice strumento di acquisizione e con esso puoi inserire, normalizzare, arricchire e cercare i tuoi dati non strutturati. L'immagine seguente mostra i componenti e le funzionalità principali di Watson Discovery.

Illustrazione delle funzioni principali di Watson Discovery

Come puoi vedere sopra, una delle funzionalità principali di Watson Discovery è l'ingestione di dati strutturati e non strutturati in diversi formati come JSON, HTML, PDF, Word e altri, e Smart Document Understanding.

Se nel tuo ambiente è stato eseguito il provisioning di Watson Discovery, puoi facilmente inserire i documenti creando prima un progetto e poi una raccolta in cui puoi caricare i tuoi dati dallo storage locale o da una posizione cloud, ad esempio un bucket di object storage.

Dopo aver creato la raccolta, puoi andare alla scheda Gestisci raccolte nel menu hamburger e poi, in Campi identità, puoi scegliere il metodo che desideri utilizzare per elaborare i documenti.

Schermata della schermata di elaborazione dei documenti di Watson Discovery

Ci sono tre opzioni:

  • Estrazione solo di testo: estrae solo il testo dai documenti.
  • Modelli addestrati dall'utente: dove puoi annotare diverse parti del tuo documento con tag personalizzati e addestrare i tuoi modelli basandoti su pattern visivi ripetuti all'interno dei tuoi documenti
  • Modelli pre-addestrati: che possono estrarre testo e identificare tabelle, liste e sezioni utilizzando modelli IBM® pre-addestrati.

Potrebbe volerci un po' di tempo prima che tutti i tuoi documenti vengano elaborati. Dopodiché puoi utilizzare Watson Discovery API per leggere e utilizzare i tuoi dati nel codice.

Di seguito è riportato un codice di esempio che mostra come puoi usare l'API per leggere i dati della tua collezione:

from langchain.docstore.document import Document
import os
import logging
import time
from ibm_watson import DiscoveryV2
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator

WD_PAGE_SIZE = 200
MAX_RETRIES = 2
def get_documents_from_wd(collection_ids=[WD_COLLECTION_ID]):
    
    print("Recupero documenti da Watson Discovery")
    documents = []
    
    print("Configurazione del client WD")
    authenticator = IAMAuthenticator(WD_API_KEY)
    wd_client = DiscoveryV2(
        version="2023-03-31",
        authenticator=authenticator
    )
    
    print("Configurazione del servizio WD URL")
    wd_client.set_service_url(WD_SERVICE_URL)
    
    print("Recupero documenti da WD")
    page_id = 0
    retries = 0
    while True:
        try:
            print("Acquisizione pagina: " + str(page_id))
            response = wd_client.query(
                project_id=WD_PROJECT_ID,
                collection_ids=collection_ids,
                return_=["text"],
                count=WD_PAGE_SIZE,
                offset=page_id*WD_PAGE_SIZE
            ).get_result()
            if response is None or not isinstance(response, dict):
                print("Nessun risultato per la query")
                raise ValueError("Nessun risultato per la query")
            if "results" not in response or response["results"] is None or not isinstance(response["results"], list):
                print("Nessun risultato per la query 2")
                raise ValueError("Nessun risultato per la query")
            results = response["results"]
            if len(results) == 0:
                print("Nessun risultato rimanente")
                break
            print("Acquisito" + str(len(results)) + " documenti")
            documents.extend(list(map(lambda result: Document(page_content=result["text"][0],  
                                                              metadata= {"collection_id": WD_COLLECTION_ID, "document_id" : result['document_id']}), results)))
            page_id += 1
        except Exception as error:
            logging.error("Impossibile acquisire documenti da WD", str(error))
            retries += 1
            time.sleep(5)
            if retries > MAX_RETRIES:
                break
            print("Nuovo tentativo...")
    print("Acquisito" + str(len(documents)) + " documenti")
    return documents

 

Web crawler in Watson Discovery

Quando crei un progetto in Watson Discovery, puoi pianificare una scansione web per recuperare informazioni da un URL specificato. Tutto ciò che devi fare è selezionare la scansione web quando selezioni la fonte dati e poi specificare l'URL e il programma di scansione.

Schermata di configurazione per il web crawler di Watson Discovery.
Schermata del programmatore di web crawler di Watson Discovery

Successivamente, puoi eseguire i passaggi sopra menzionati per gestire la tua raccolta e utilizzare l'API per leggere i dati dalla tua raccolta. Per maggiori dettagli su come configurare la scansione web in Watson Discovery, puoi guardare questo video.

Per maggiori informazioni e caratteristiche puoi consultare la pagina sviluppatore di Watson Discovery.

Deep Search

Deep Search è il toolkit open source di IBM Research per l'ingestione. Deep Search sfrutta metodi AI all'avanguardia per raccogliere, convertire, arricchire e collegare continuamente grandi collezioni di documenti. Può essere utilizzato sia per documenti PDF pubblici che proprietari.

Deep Search converte documenti PDF non strutturati in file JSON strutturati con precisione e facilità. Ti permette di automatizzare l'estrazione di conoscenze e di perfezionare i tuoi foundational model proprietari e i modelli linguistici di grandi dimensioni.

Puoi trovare maggiori informazioni su Deep Search qui.

Deep Search può essere un buono strumento interno per la lettura di documenti complessi, al posto delle librerie non strutturate o di altre librerie open source. Puoi anche trovare un'ottima documentazione sul repository con diversi tipi di esempi come i notebook Python:

Notebook di esempio di deep search

Ingestione di tabelle da PDF

Deep Search fornisce buoni risultati quando si estraggono tabelle da PDF in formato JSON.

Per i test, abbiamo utilizzato un documento PDF parziale del CIPP, che include tre pagine, ciascuna delle quali mostra i dati delle tabelle. La migliore performance di RAG in questo caso si ottiene usando Deep Sarch per estrarre le tabelle in formato JSON, poi convertendole in HTML per l'uso in Watson Discovery / watsonx Discovery invece di usare direttamente i PDF, convertendo PDF in HTML senza Deep Search, o usando solo il JSON di Deep Search in Watson Discovery / watsonx Discovery. Abbiamo scoperto che Watson Discovery seleziona la risposta sbagliata dalla cella sbagliata nella tabella in alcuni casi, ma più velocemente, e nello stesso caso, Watson Discovery può individuare la risposta esatta all'interno della griglia della tabella.

IBM Datacap

IBM Datacap è una soluzione completa per la cattura di documenti e dati; offrendo scansione, classificazione, riconoscimento, validazione ed esportazione rapida, accurata ed economica, di dati e immagini di documenti. Raccoglie documenti, estrae dati rilevanti e li integra nei processi aziendali a valle.

Datacap acquisisce documenti cartacei tramite scanner, stampanti multifunzione o dispositivi mobili e importa documenti elettronici da sistemi di file, fax o server di posta elettronica. Migliora l'estrazione dei dati con caratteristiche di elaborazione delle immagini, come il deskewing e la rimozione di linee, sbavature e bordi.

Datacap utilizza il riconoscimento ottico dei caratteri (OCR), il riconoscimento intelligente dei caratteri (ICR) per la scrittura a mano, il riconoscimento ottico dei segni (OMR) per i segni e la lettura dei codici a barre per estrarre i dati in modo efficiente.

Per istruzioni dettagliate di installazione e utilizzo, consulta la documentazione IBM Datacap.

Strumenti open source

Caricatori di dati LangChain

LangChain supporta diversi tipi di caricatori di documenti che caricano i dati da una sorgente come oggetto documenti di LangChain. Un oggetto documento è una parte di testo e metadati associati che possono essere facilmente utilizzati per la divisione in blocchi e il recupero in un secondo momento lungo la pipeline.

Attualmente LangChain supporta caricatori per caricare un'intera directory o diversi tipi di file, come ad esempio:

  • CSV
  • HTML
  • JSON
  • Markdown
  • Microsoft Office
  • PDF

È possibile utilizzare questi caricatori in poche righe in Python. Ad esempio, per caricare un PDF è possibile utilizzare il seguente codice:

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()

Per sapere come utilizzare questi caricatori, consulta la documentazione di LangChain.

Oltre a quelli menzionati nel link sopra, puoi trovare altri caricatori che possono caricare dati direttamente da applicazioni specifiche come e-mail, Github, Google Drive, ecc. Qui puoi trovare l'elenco di tutti i diversi caricatori.

Nei casi in cui si hanno documenti con strutture, formati e tabelle complesse, è possibile utilizzare i caricatori non strutturati di LangChain. Questi caricatori dietro le quinte utilizzano la libreria non strutturata, una delle migliori librerie per documenti complessi (in diversi formati come PDF, MSWord, Power Point, ecc.) e per suddividere i documenti in diversi componenti: LangChain UnstructuredLoader

Caricatori di dati LlamaIndex

Simile a LangChain, anche LlamaIndex supporta diversi tipi di caricatori. Uno dei modi più semplici per caricare dati utilizzando LlamaIndex è utilizzare SimpleDirectoryReader. SimpleDirectoryReader supporta i seguenti tipi:

  • .csv - Valori separati da virgole
  • .docx - Microsoft Word
  • .epub - Formato ebook EPUB
  • .hwp - Elaboratore di testi Hangul
  • .ipynb - Jupyter Notebook
  • .jpeg, .jpg - Immagine JPEG
  • .mbox - Archivio e-mail MBOX
  • .md - Markdown
  • .mp3, .mp4 - Audio e video
  • .pdf - Formato di documento portatile
  • .png - Grafica di rete portatile
  • .ppt, .pptm, .pptx - Microsoft PowerPoint

È possibile utilizzare SimpleDirectoryLoader utilizzando il seguente frammento di codice:

from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()

Puoi anche aggiungere estensioni specifiche per leggere solo quelle estensioni dalla directory:

SimpleDirectoryReader(
   input_dir="path/to/directory", required_exts=[".pdf", ".docx"]
)

Per dettagli su come utilizzare i LlamaIndex Loaders, si prega di consultare la sua documentazione.

Analogamente a Huggingface, puoi creare i tuoi caricatori personalizzati o utilizzare quelli di altre persone su llamahub.ai.

Uno svantaggio di LlamaIndex rispetto a LangChain è che la documentazione e il supporto della community non sono validi come quelli di LangChain, quindi il debug del codice potrebbe risultare più difficile.

Non strutturato

Nel caso in cui carichi documenti complessi, puoi anche usare direttamente la libreria non strutturata. La libreria non strutturata è davvero efficace per leggere e analizzare tabelle.

La libreria non strutturata è supportata sia da wrapper LangChain che LlamaIndex e potrebbe essere più facile da usare per pipeline RAG in futuro, poiché creeranno e restituiranno automaticamente gli oggetti documenti. Tuttavia, se il progetto necessita di maggiore personalizzazione e si desidera utilizzare direttamente la libreria non strutturata, è possibile fare riferimento alla seguente documentazione: Non strutturata.

Scopri di più

Ricevi le ultime tecnologie, le architetture di soluzioni, e le pubblicazioni di architettura di IBM.

  1. Vai all'IBM Architecture Center
Collaboratori

Sara Golestaneh, Vicky Kuo, Fahad Bhutta, Luke Major, Dean Sacoransky, Chris Kirby

Data di aggiornamento: 15 novembre 2024