💡 完全に動作する例はGitHubで入手できます: parse-docx-to-markdown-chunks-dotnet
古い方法は苦痛だった
ヘッダーに基づくMarkdownチャンク化は、.NET の検索前処理パスで、Office と PDF ファイルを埋め込み前にセクションサイズのテキストに変換します。これがないと、チームはしばしば Word ファイル全体を単一のベクトルに投げ込んでしまいます。表紙の段落と義務条項が同じ埋め込みを共有するため、コサイン検索は誤ったセクションをランク付けします。3 ページの社内ガイドでこの問題に直面しました:すべてのクエリがタイトルブロックを最初に取得したのは、インデックスが著者がすでに書いた # / ## 構造を見ていなかったからです。
DOCX から Markdown へのコンバータを自前で作るということは、Open XML のスタイルを歩き、リストレベルをマッピングし、次にアップロードされる PDF のフォーマット済み Markdown パスが null を返すとまだ失敗する、ということです。固定サイズのトークンウィンドウはそれらの見出しを無視します。一晩でコーパスが空のシェルと巨大なマニュアルを混在させてしまうのは、誰もページ数やサイズで事前にゲートしなかったからです。
全ファイル埋め込みの実際のコスト:誤ったセクションの回答、ジャンクアップロードへの無駄な抽出コスト、そしてバッチが悪いときにオペレーターが開くことのできる
chunk-01.mdアーティファクトがないこと。
より良い方法があります
GroupDocs.Parser for .NET は、その混乱を 4 つの焦点を絞った呼び出しに置き換えます:GetDocumentInfo、Markdown が null のとき GetFormattedText(FormattedTextMode.Markdown)、GetText、そして chunk-NN.md を書き出す小さな見出しウォーカーです。同じ Parser 型が Word DOCX の解析と PDF テキストのフォールバックをカバーするので、1 つの C# サービスパスで済みます。
You will need:
- .NET 8 SDK
- GroupDocs.Parser 26.10.0 (一時ライセンス)
- Word DOCX(サンプルは 3 ページ / 14820 バイト)とオプションで PDF
dotnet add package GroupDocs.Parser --version 26.10.0
新しい方法:検査、抽出、フォールバック、分割
以下は切り替え後の同じ検索前処理です。parse-docx-to-markdown-chunks-dotnet のコンソールサンプルは、情報、Markdown、PDF テキスト、そして出荷された DOCX の 4 つの見出しチャンクを書き出します。
ステップ 1 - GetDocumentInfo でゲート
完全な抽出に費用をかける前に、ファイルタイプ、ページ数、サイズを問い合わせます。
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());
ジョブ ID の横にこれらの事実を記録します。サンプル DOCX では、Microsoft Word Open XML Document、3 ページ、14820 バイトが表示されるはずです。
ステップ 2 - GetFormattedText から Markdown を優先
Word およびフォーマット抽出をサポートする形式では、分割用に ATX 見出しを保持します。
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 はパスを切り替えるシグナルであり、クラッシュではありません。UTF-8 で書き込むことで、チャンクツールや git diff が読みやすくなります。
ステップ 3 - PDF 用に GetText でフォールバック
Markdown モードが null を返したときは、プレーンテキスト層でパイプラインにファイルを保持します。
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;
.txt が期待され、作り出された # 見出しはありません。サンプルホストは PDF ブランチ用に Result/sample-pdf.txt を書き出します。
ステップ 4 - # と ## で Markdown を分割
行を走査し、新しい ATX # または ## が現れたらフラッシュし、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);
}
}
まず古い chunk-*.md ファイルを削除します。より深い ### 行は現在のセクション内に残ります。サンプル DOCX は Result/chunks/ 配下に 4 つのファイルを生成します。
いつ全ファイル埋め込みをこのパスに置き換えるべきか?
入力が Word DOCX と PDF を混在させ、インデックスがセクションサイズの Markdown を必要とする場合に全ファイル埋め込みを置き換えます。まず GetDocumentInfo を実行し、Office ファイルには Markdown を試し、フォーマットされたリーダーが null のときは GetText を呼び出し、# / ## マーカーが存在する場合にのみ分割します。別個のチャンクポリシーを追加するまで、構造化されていない PDF テキストはそのままにしておきます。この順序はサンプルコンソールアプリと一致し、オペレーター向けに失敗モードを明示的に保ちます。
オペレーターにとって何が変わったか
| 変更前 | 変更後 |
|---|---|
| ファイルごとに1つの埋め込み | 見出しブロックごとに1つの埋め込み |
| カスタム Open XML Markdown | GetFormattedText Markdown モード |
| フォーマットされたリーダーが null のとき PDF が除外される | GetText がテキストアーティファクトを書き出す |
| 安価な事前チェックがない | ページ数とサイズのゲートが機能する |
すべての Parser と TextReader を破棄します。大量処理の前にライセンスまたは 一時ライセンス を適用してください。アップロードは信頼できない入力として扱い、隔離されたワーカーで解析します。
自分のファイルで試す
リポジトリ parse-docx-to-markdown-chunks-dotnet をクローンし、GroupDocs.Parser 26.10.0 を復元して documents/sample.docx に対して実行します。その後、同じ4つのメソッドをあなたのマニュアルのいずれかに向け、生成された chunk-*.md ファイルを開きます。