💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar:
parse-docx-to-markdown-chunks-dotnet
Der alte Weg war schmerzhaft
Heading-basierte Markdown-Chunking ist ein .NET‑Retrieval‑Prep‑Pfad, der Office‑ und PDF‑Dateien in abschnittsweise Text umwandelt, bevor Sie einbetten. Ohne diesen Schritt legen Teams oft eine gesamte Word‑Datei in einen einzigen Vektor. Der Deckblatt‑Absatz und die Pflichtklausel teilen sich ein Embedding, sodass die Kosinus‑Suche den falschen Abschnitt rankt. Ich stieß darauf in einem internen Leitfaden mit drei Seiten: Jede Anfrage zog zuerst den Titelblock, weil der Index die bereits von den Autoren geschriebenen # / ##‑Strukturen nie sah.
Ein selbstgeschriebener DOCX‑zu‑Markdown‑Konverter bedeutet, Open‑XML‑Stile zu durchlaufen, Listenniveaus zuzuordnen und dennoch zu scheitern, wenn das nächste Upload eine PDF ist, deren formatierter Markdown‑Pfad null zurückgibt. Token‑Fenster fester Größe ignorieren diese Überschriften. Über Nacht mischen sich dann leere Hüllen mit riesigen Handbüchern, weil niemand zuerst nach Seitenzahl oder Größe filtert.
Die wirklichen Kosten von Ganzdatei‑Embeddings: falsche Abschnitts‑Antworten, verschwendete Extraktionsausgaben für Müll‑Uploads und keine
chunk-01.md‑Artefakte, die Operatoren bei einem fehlerhaften Batch öffnen können.
Es gibt einen besseren Weg
GroupDocs.Parser für .NET ersetzt dieses Durcheinander durch vier fokussierte Aufrufe: GetDocumentInfo, GetFormattedText(FormattedTextMode.Markdown), GetText wenn Markdown null ist, und einen kleinen Überschriften‑Walker, der chunk‑NN.md schreibt. Der gleiche Parser‑Typ deckt das Parsen von Word‑DOCX und den PDF‑Text‑Fallback ab, sodass Sie einen einzigen C#‑Service‑Pfad behalten.
Sie benötigen:
- .NET 8 SDK
- GroupDocs.Parser 26.10.0 (temporäre Lizenz)
- Ein Word‑DOCX (das Beispiel ist 3 Seiten / 14820 Bytes) und optional eine PDF
dotnet add package GroupDocs.Parser --version 26.10.0
Der neue Weg: Prüfen, Extrahieren, Fallback, Aufteilen
Hier ist dieselbe Retrieval‑Prep nach dem Wechsel. Das Konsolenbeispiel unter parse-docx-to-markdown-chunks-dotnet schreibt Infos, Markdown, PDF‑Text und vier Überschriften‑Chunks für das mitgelieferte DOCX.
Schritt 1 - Gate mit GetDocumentInfo
Fragen Sie nach Dateityp, Seitenzahl und Größe, bevor Sie für eine vollständige Extraktion bezahlen.
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());
Protokollieren Sie diese Fakten neben der Job‑ID. Beim Beispiel‑DOCX sollten Sie ein Microsoft Word Open XML Document, 3 Seiten und 14820 Bytes sehen.
Schritt 2 - Bevorzugen Sie Markdown von GetFormattedText
Für Word und Formate, die formatierte Extraktion unterstützen, behalten Sie ATX‑Überschriften für den Splitter bei.
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 ist ein Signal, den Pfad zu wechseln, nicht ein Absturz. Schreiben Sie UTF‑8, damit Chunk‑Tools und Git‑Diffs lesbar bleiben.
Schritt 3 - Fallback mit GetText für PDF
Wenn der Markdown‑Modus null zurückgibt, behalten Sie die Datei in der Pipeline mit einer reinen Textebene.
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;
Erwarten Sie .txt, nicht erfundene #‑Überschriften. Der Beispiel‑Host schreibt Result/sample-pdf.txt für den PDF‑Zweig.
Schritt 4 - Markdown bei # und ## aufteilen
Zeilen durchlaufen; flushen, wenn ein neues ATX # oder ## erscheint; chunk‑NN.md schreiben.
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);
}
}
Löschen Sie zuerst veraltete chunk-*.md‑Dateien. Tiefere ###‑Zeilen bleiben im aktuellen Abschnitt. Das Beispiel‑DOCX erzeugt vier Dateien unter Result/chunks/.
Wann sollte ich Ganzdatei‑Embeddings durch diesen Pfad ersetzen?
Ersetzen Sie Ganzdatei‑Embeddings, wenn die Aufnahme Word‑DOCX und PDF mischt und der Index Markdown in Abschnittsgröße benötigt. Führen Sie zuerst GetDocumentInfo aus, versuchen Sie Markdown für Office‑Dateien, rufen Sie GetText auf, wenn der formatierte Reader null ist, und splitten Sie nur, wenn # / ##‑Marker vorhanden sind. Lassen Sie unstrukturierten PDF‑Text unverändert, bis Sie eine separate Chunk‑Richtlinie hinzufügen. Diese Reihenfolge entspricht der Beispiel‑Konsolen‑App und hält Fehlermodi für Operatoren explizit.
Was sich für Operatoren geändert hat
| Vorher | Nachher |
|---|---|
| Ein Embedding pro Datei | Ein Embedding pro Überschriften‑Block |
| Benutzerdefiniertes Open XML Markdown | GetFormattedText Markdown‑Modus |
| PDF verworfen bei null formatiertem Reader | GetText schreibt ein Text‑Artefakt |
| Keine günstige Vorprüfung | Seitenzahl‑ und Größen‑Gate funktioniert |
Entsorgen Sie jedes Parser‑ und TextReader‑Objekt. Wenden Sie eine Lizenz oder temporäre Lizenz vor dem nächtlichen Volumen an. Behandeln Sie Uploads als nicht vertrauenswürdige Eingaben und parsen Sie in einem isolierten Worker.
Probieren Sie es mit Ihren eigenen Dateien
Klone parse-docx-to-markdown-chunks-dotnet, stelle GroupDocs.Parser 26.10.0 wieder her und führe es gegen documents/sample.docx aus. Zeige dann dieselben vier Methoden auf eines Ihrer Handbücher und öffnen Sie die erzeugten chunk-*.md‑Dateien.