💡 Volledig werkend voorbeeld beschikbaar op GitHub:
parse-docx-to-markdown-chunks-dotnet
De oude manier was pijnlijk
Heading‑gebaseerde Markdown chunking is een .NET retrieval prep pad dat Office‑ en PDF‑bestanden omzet in sectiegrootte tekst voordat je embedt. Zonder dit dumpen teams vaak een heel Word‑bestand in één vector. De inleidende alinea en de verplichtingsclausule delen één embedding, waardoor cosine‑zoekopdrachten de verkeerde sectie rangschikken. Ik kwam dit tegen in een interne handleiding van drie pagina’s: elke query haalde eerst het titelblok op omdat de index de # / ##‑structuur die de auteurs al hadden geschreven nooit zag.
Het zelf bouwen van een DOCX‑naar‑Markdown converter betekent het doorlopen van Open XML‑stijlen, het in kaart brengen van lijstniveaus, en nog steeds falen wanneer de volgende upload een PDF is waarvan het geformatteerde Markdown‑pad null retourneert. Vaste token‑vensters negeren die koppen. Overnachtingscorpora mengen dan lege schelpen met enorme handleidingen omdat niemand eerst op paginacount of grootte controleert.
De werkelijke kosten van volledige‑bestand embeddings: antwoorden uit de verkeerde sectie, verspilde extractiekosten aan onnodige uploads, en geen
chunk-01.mdartefacten die operators kunnen openen tijdens een slechte batch.
Er is een betere manier
GroupDocs.Parser voor .NET vervangt die chaos door vier gerichte aanroepen: GetDocumentInfo, GetFormattedText(FormattedTextMode.Markdown), GetText wanneer Markdown null is, en een kleine heading‑walker die chunk‑NN.md schrijft. Hetzelfde Parser‑type behandelt Word DOCX parsing en PDF‑tekst fallback, zodat je één C# servicepad behoudt.
Je hebt nodig:
- .NET 8 SDK
- GroupDocs.Parser 26.10.0 (temporary license)
- Een Word DOCX (het voorbeeld is 3 pagina’s / 14820 bytes) en optioneel een PDF
dotnet add package GroupDocs.Parser --version 26.10.0
De nieuwe manier: Inspecteren, extraheren, fallback, splitsen
Hier is dezelfde retrieval prep na de omschakeling. Het console‑voorbeeld onder parse-docx-to-markdown-chunks-dotnet schrijft info, Markdown, PDF‑tekst, en vier heading‑chunks voor de meegeleverde DOCX.
Stap 1 - Filter met GetDocumentInfo
Vraag naar bestandstype, paginacount en grootte voordat je betaalt voor een volledige extractie.
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());
Log die feiten naast de job‑id. Op de voorbeeld DOCX zou je een Microsoft Word Open XML Document, 3 pagina’s, en 14820 bytes moeten zien.
Stap 2 - Geef de voorkeur aan Markdown van GetFormattedText
Voor Word en formaten die geformatteerde extractie ondersteunen, behoud ATX‑koppen voor de 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 is een signaal om van pad te wisselen, niet een crash. Schrijf UTF‑8 zodat chunk‑tools en git‑diffs leesbaar blijven.
Stap 3 - Fallback met GetText voor PDF
Wanneer Markdown‑modus null retourneert, houd het bestand in de pijplijn met een platte tekstlaag.
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;
Verwacht .txt, geen verzonnen # koppen. De voorbeeldhost schrijft Result/sample-pdf.txt voor de PDF‑tak.
Stap 4 - Split Markdown op # en
Loop door de regels; flush wanneer een nieuwe ATX # of ## verschijnt; schrijf 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);
}
}
Verwijder eerst verouderde chunk-*.md bestanden. Diepere ### regels blijven binnen de huidige sectie. De voorbeeld DOCX levert vier bestanden op onder Result/chunks/.
Wanneer moet ik volledige‑bestand embeddings vervangen door dit pad?
Vervang volledige‑bestand embeddings wanneer de invoer Word DOCX en PDF mixt en de index sectiegrootte Markdown nodig heeft. Voer eerst GetDocumentInfo uit, probeer Markdown voor Office‑bestanden, roep GetText aan wanneer de geformatteerde lezer null is, en split alleen wanneer # / ## markers bestaan. Laat ongestructureerde PDF‑tekst ongemoeid tot je een aparte chunk‑policy toevoegt. Die volgorde komt overeen met de voorbeeld console‑app en houdt foutmodi expliciet voor operators.
Wat is er veranderd voor operators
| Voor | Na |
|---|---|
| Eén embedding per bestand | Eén embedding per heading‑blok |
| Aangepaste Open XML Markdown | GetFormattedText Markdown‑modus |
| PDF weggelaten bij null geformatteerde lezer | GetText writes a text artifact |
| Geen goedkope pre‑check | Paginacount en grootte filter werken |
Dispose elke Parser en TextReader. Pas een licentie toe of temporary license voordat er overnachtingsvolume is. Behandel uploads als onbetrouwbare invoer en parse in een geïsoleerde worker.
Probeer het op je eigen bestanden
Clone parse-docx-to-markdown-chunks-dotnet, herstel GroupDocs.Parser 26.10.0, en voer uit tegen documents/sample.docx. Richt vervolgens dezelfde vier methoden op een van je handleidingen en open de gegenereerde chunk-*.md bestanden.