💡 Esempio completo funzionante disponibile su GitHub: parse-docx-to-markdown-chunks-dotnet

Il vecchio modo era doloroso

Il chunking di Markdown basato su intestazioni è un percorso di preparazione al recupero .NET che trasforma i file Office e PDF in testo di dimensione sezione prima dell’incorporamento. Senza di esso, i team spesso scaricano un intero file Word in un unico vettore. Il paragrafo di copertina e la clausola di dovere condividono un unico embedding, quindi la ricerca coseno classifica la sezione sbagliata. Mi è capitato su una guida interna di tre pagine: ogni query estraeva prima il blocco del titolo perché l’indice non vedeva la struttura # / ## già scritta dagli autori.

Creare manualmente un convertitore DOCX‑to‑Markdown significa percorrere gli stili Open XML, mappare i livelli delle liste e fallire comunque quando il prossimo upload è un PDF il cui percorso Markdown formattato restituisce null. Le finestre di token a dimensione fissa ignorano quelle intestazioni. I corpora notturni mescolano quindi gusci vuoti con enormi manuali perché nessuno ha filtrato prima per numero di pagine o dimensione.

Il vero costo degli embedding dell’intero file: risposte nella sezione sbagliata, spese di estrazione sprecate su upload inutili, e nessun artefatto chunk-01.md che gli operatori possano aprire durante un batch difettoso.

C’è un modo migliore

GroupDocs.Parser per .NET sostituisce quel caos con quattro chiamate mirate: GetDocumentInfo, GetFormattedText(FormattedTextMode.Markdown), GetText quando Markdown è null, e un piccolo walker delle intestazioni che scrive chunk‑NN.md. Lo stesso tipo Parser gestisce l’analisi di Word DOCX e il fallback al testo PDF, così mantieni un unico percorso di servizio C#.

Avrai bisogno di:

  • .NET 8 SDK
  • GroupDocs.Parser 26.10.0 (licenza temporanea)
  • Un file Word DOCX (il campione è di 3 pagine / 14820 byte) e opzionalmente un PDF
dotnet add package GroupDocs.Parser --version 26.10.0

Il nuovo modo: ispeziona, estrai, fallback, dividi

Ecco la stessa preparazione al recupero dopo il cambiamento. Il campione console sotto parse-docx-to-markdown-chunks-dotnet scrive informazioni, Markdown, testo PDF e quattro chunk di intestazione per il DOCX fornito.

Passo 1 - Filtra con GetDocumentInfo

Richiedi il tipo di file, il numero di pagine e la dimensione prima di pagare per un’estrazione completa.

using var parser = new Parser(sourcePath);
var info = parser.GetDocumentInfo();

var sb = new StringBuilder();
sb.AppendLine($"Source: {Path.GetFileName(sourcePath)}");
sb.AppendLine($"File type: {info?.FileType}");
sb.AppendLine($"Pages: {info?.PageCount}");
sb.AppendLine($"Size: {info?.Size} bytes");
File.WriteAllText(outputPath, sb.ToString());

Registra questi fatti accanto all’ID del lavoro. Sul DOCX di esempio dovresti vedere un Microsoft Word Open XML Document, 3 pagine e 14820 byte.

Passo 2 - Preferisci Markdown da GetFormattedText

Per Word e formati che supportano l’estrazione formattata, mantieni le intestazioni ATX per lo splitter.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetFormattedText(
    new FormattedTextOptions(FormattedTextMode.Markdown));

if (reader == null)
{
    return null;
}

string markdown = reader.ReadToEnd();
File.WriteAllText(outputPath, markdown, Encoding.UTF8);
return markdown;

Null è un segnale per cambiare percorso, non un crash. Scrivi in UTF‑8 così gli strumenti di chunk e i diff di git rimangono leggibili.

Passo 3 - Fallback con GetText per PDF

Quando la modalità Markdown restituisce null, mantieni il file nella pipeline con uno strato di testo semplice.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetText();
if (reader == null)
{
    return null;
}

string text = reader.ReadToEnd();
File.WriteAllText(outputPath, text, Encoding.UTF8);
return text;

Attendi .txt, non intestazioni # inventate. L’host di esempio scrive Result/sample-pdf.txt per il ramo PDF.

Passo 4 - Dividi Markdown su # e

Scorri le righe; svuota quando appare un nuovo ATX # o ##; scrivi chunk‑NN.md.

var chunks = new List<string>();
var current = new StringBuilder();
using (var reader = new StringReader(markdown))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        bool isHeading = line.StartsWith("# ", StringComparison.Ordinal)
            || line.StartsWith("## ", StringComparison.Ordinal);
        if (isHeading && current.Length > 0)
        {
            chunks.Add(current.ToString().TrimEnd());
            current.Clear();
        }
        current.AppendLine(line);
    }
}

Cancella prima i file chunk-*.md obsoleti. Le linee più profonde ### rimangono all’interno della sezione corrente. Il DOCX di esempio genera quattro file sotto Result/chunks/.

Quando dovrei sostituire gli embedding dell’intero file con questo percorso?

Sostituisci gli embedding dell’intero file quando l’ingestione mescola Word DOCX e PDF e l’indice necessita di Markdown a dimensione di sezione. Esegui prima GetDocumentInfo, prova Markdown per i file Office, chiama GetText quando il lettore formattato è null, e dividi solo quando esistono i marcatori # / ##. Lascia il testo PDF non strutturato intatto finché non aggiungi una politica di chunk separata. Questo ordine corrisponde all’app console di esempio e mantiene i modi di fallimento espliciti per gli operatori.

Cosa è cambiato per gli operatori

Prima Dopo
Un embedding per file Un embedding per blocco di intestazione
Markdown Open XML personalizzato GetFormattedText modalità Markdown
PDF scartato su lettore formattato null GetText scrive un artefatto di testo
Nessun controllo preliminare economico Controllo preliminare su numero di pagine e dimensione

Disporre di ogni Parser e TextReader. Applica una licenza o licenza temporanea prima del volume notturno. Tratta gli upload come input non attendibili e analizza in un worker isolato.

Provalo sui tuoi file

Clona parse-docx-to-markdown-chunks-dotnet, ripristina GroupDocs.Parser 26.10.0 ed esegui su documents/sample.docx. Poi indirizza gli stessi quattro metodi verso uno dei tuoi manuali e apri i file chunk-*.md generati.