Per il RAG, converti i PDF in Markdown quando vuoi chunk che seguano le sezioni del documento: ogni titolo diventa un punto di divisione, e i titoli sopra un chunk viaggiano con esso come metadati. Controlla titoli, tabelle e testo OCR prima di indicizzare, poi verifica il recupero su domande di cui conosci già la risposta.

Questa guida è per gli sviluppatori che costruiscono il recupero su documenti privati: policy, manuali, contratti, ricerche. Presuppone che tu abbia già in mente un modello di embedding e un database vettoriale.

Pile di schede a righe e strisce di carta accanto a un portatile, una pila legata con un nastro lilla.
Chunk che seguono le sezioni del documento.

Perché i titoli danno chunk migliori

Un divisore a dimensione fissa taglia il testo in pezzi di lunghezza stabilita, ovunque cada il taglio. Un chunk può iniziare nel mezzo di una sezione e perdere il titolo che dice di cosa parla. «Le richieste sono limitate a 100 al minuto» significa una cosa sotto «Sandbox» e un'altra sotto «Production». Senza il titolo, né il retriever né il modello possono capire quale.

Il Markdown conserva la struttura del documento come righe che iniziano con #, ## e ###. Un divisore basato sui titoli taglia in corrispondenza di quelle righe, così ogni chunk copre una sezione, e registra i titoli sopra il chunk: il suo percorso di sezione. La guida di LangChain alla divisione del Markdown descrive la stessa idea: tenere il testo insieme ai titoli a cui appartiene.

Le sezioni hanno lunghezze diverse, quindi una sezione lunga richiede comunque una seconda divisione per dimensione. Il percorso di sezione vale per ogni suo pezzo.

Convertire i PDF e controllare i titoli

In MD Factory, aggiungi la cartella con i tuoi PDF. Ogni file Markdown viene salvato accanto al suo PDF con lo stesso nome, con le immagini in una cartella lì accanto. I PDF scansionati passano attraverso l'OCR sul tuo PC. I file Word e gli altri formati nella cartella vengono convertiti allo stesso modo.

Apri alcuni risultati prima di scrivere qualsiasi codice di divisione:

  • Assicurati che capitoli, sezioni e sottosezioni escano come #, ## e ###. Un PDF spesso segna un titolo solo con un carattere più grande o più marcato, quindi qualsiasi convertitore deve dedurre dove iniziano le sezioni e come sono annidate. Correggi i livelli dove sono sbagliati.
  • Nei file Word, i titoli passano se l'autore ha usato gli stili Titolo di Word. Il testo solo in grassetto o grande resta un normale paragrafo.
  • Cerca intestazioni correnti, piè di pagina e numeri di pagina in mezzo al testo.
  • Fai clic su Da controllare accanto a un file per vedere cosa verificare. Per un PDF, l'elenco può includere pagine in cui l'OCR non ha potuto leggere parte del testo o non ha trovato testo.

Correggi i problemi nel file Markdown prima di indicizzarlo. Ogni chunk li eredita.

Dividere per titoli nel codice

MarkdownHeaderTextSplitter di LangChain divide in base ai livelli di titolo che elenchi e mette ogni titolo nei metadati del chunk. Un secondo divisore limita la dimensione all'interno di ogni sezione:

from pathlib import Path
from langchain_text_splitters import (
    MarkdownHeaderTextSplitter,
    RecursiveCharacterTextSplitter,
)

corpus = Path("corpus")
by_heading = MarkdownHeaderTextSplitter(
    [("#", "h1"), ("##", "h2"), ("###", "h3")], strip_headers=False
)
by_size = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150)

chunks = []
for path in sorted(corpus.rglob("*.md")):
    sections = by_heading.split_text(path.read_text(encoding="utf-8"))
    for chunk in by_size.split_documents(sections):
        chunk.metadata["source"] = path.relative_to(corpus).as_posix()
        chunk.metadata["section"] = " > ".join(
            chunk.metadata[h] for h in ("h1", "h2", "h3") if h in chunk.metadata
        )
        chunks.append(chunk)

La dimensione dei chunk e la sovrapposizione qui sono un punto di partenza; adattale al tuo modello di embedding. Per tenere le righe dei titoli nel testo del chunk, in modo che il testo di cui calcoli l'embedding nomini anche la sua sezione, l'esempio imposta strip_headers=False.

MarkdownNodeParser di LlamaIndex esegue la stessa divisione per titoli. Quando carichi i file con SimpleDirectoryReader, ogni nodo porta anche il nome del file:

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import MarkdownNodeParser

documents = SimpleDirectoryReader(
    "corpus", recursive=True, required_exts=[".md"]
).load_data()
nodes = MarkdownNodeParser().get_nodes_from_documents(documents)
print(nodes[0].metadata["file_name"], nodes[0].metadata["header_path"])

header_path elenca i titoli sopra un nodo, ad esempio /Employee handbook/Expenses/; il titolo del nodo stesso è la prima riga del suo testo.

Tenere la fonte con ogni chunk

Memorizza almeno tre cose con ogni chunk:

  • Il file. Un file Markdown ha lo stesso nome del suo PDF, quindi un chunk di handbook.md rimanda a handbook.pdf. Memorizza il percorso relativo alla cartella del corpus, perché due file chiamati terms.md in cartelle diverse sono documenti diversi.
  • Il percorso di sezione, così una risposta può nominare la sezione usata e un lettore può andare direttamente al passaggio.
  • L'edizione o la data. Quando una policy cambia, elimini i chunk del vecchio file e indicizzi i nuovi, invece di servire entrambe le versioni.

Se persone con diritti di accesso diversi interrogheranno lo stesso indice, memorizza anche chi può leggere ogni documento e filtra su questo al momento della query. Un retriever restituisce qualsiasi chunk che corrisponda alla domanda.

Il Markdown prodotto da MD Factory non ha numeri di pagina. Se le risposte devono citare le pagine, cerca il passaggio nel PDF originale.

Gestire con cura tabelle e testo OCR

Una tabella Markdown ha la riga di intestazione una sola volta, in cima. Quando una tabella è più lunga della dimensione del chunk, il divisore per dimensione la taglia, e i pezzi successivi arrivano senza i nomi delle colonne. Nel nostro test con LangChain (langchain-text-splitters 1.1.3), una tabella di 40 righe divisa in chunk da 300 caratteri ha dato quattro pezzi, e solo il primo conteneva la riga di intestazione.

Due modi per risolvere: tenere ogni tabella in un unico chunk anche se è più lunga degli altri, oppure ripetere la riga di intestazione all'inizio di ogni pezzo.

Per i file Word ed Excel, MD Factory contrassegna un documento come Da controllare quando celle unite o complesse sono state semplificate. Le tabelle dei PDF non ricevono alcun segnale del genere, quindi confrontane alcune con l'originale, soprattutto quelle con intestazioni unite.

Il testo OCR richiede un controllo a parte. Numeri, nomi e codici letti male finiscono nelle risposte invariati, e nulla nel chunk sembra sbagliato. Confronta le cifre in alcuni chunk con la scansione e lascia fuori dall'indice le pagine segnalate finché non le hai corrette. Cosa cercare è spiegato nella nostra guida al controllo dell'OCR nei PDF scansionati.

Verificare il recupero su domande reali

Scrivi le domande che le persone faranno davvero e, per ciascuna, il file e la sezione che contiene la risposta. Parti da una dozzina o due.

  1. Fai passare ogni domanda attraverso il tuo retriever e guarda i primi risultati.
  2. Annota se la sezione giusta è tra questi.
  3. Per ogni mancato risultato, confronta i chunk restituiti con quello che avrebbe dovuto esserci. Cause tipiche: un livello di titolo sbagliato, una tabella separata dalla sua intestazione, un errore OCR nel termine chiave, o una sezione così lunga che la risposta si perde.
  4. Cambia una cosa sola, come la dimensione dei chunk, i livelli di titolo o il modo di dividere le tabelle, e rilancia le stesse domande.

Tieni le domande in un file accanto al corpus e rilanciale ogni volta che cambiano i documenti o il codice di divisione.

Quando usare MD Factory e quando una libreria

MD Factory è un'app desktop per Windows. Va bene per una raccolta privata che vuoi convertire sul tuo PC e controllare a occhio: aggiungi una cartella, guarda gli stati, apri i file che richiedono controllo, correggili, poi punta il tuo script di indicizzazione alla cartella.

MD Factory tiene i documenti sul tuo PC mentre li converte. Se poi calcoli l'embedding dei chunk con un'API di embedding ospitata o li invii a un modello ospitato, il loro testo va a quel fornitore. Usa un modello locale se i documenti non devono uscire dalla tua rete.

Se i documenti arrivano di continuo, stanno su un server o devono essere convertiti senza che qualcuno controlli, usa invece una libreria di conversione nel tuo codice. Per le opzioni, consulta il nostro confronto tra convertitori da PDF a Markdown.