はじめに
GMOコネクトの永田です。
プロジェクトで受領したWord文書(要件定義書、設計書など数十ファイル)をLLMに読ませたくて、Markdownに変換しました。変換はPythonで自動化できますが、品質チェックが辛い。数十ページのWordと変換後MDを目視で突き合わせる作業です😇
Claude Codeに丸投げしてみたところ、手動で見つけた11件の問題のうち7件を事前知識なしで検出。人間が見落としていた問題も3件拾っています。
最初にまとめ
Word→Markdown変換で見つかった11件の品質問題を、Claude Codeでどこまで自動検出できるか試しました。
| チェック手法 | 検出率(手動比) | 特徴 |
|---|---|---|
| 手動チェック | 基準(11件) | 1ページずつ突き合わせて発見 |
| 品質チェックツール(手動の知見を組み込み) | 90% | 検出ルールを書けば高精度 |
| Claude Codeサブエージェント(知見ゼロ) | 64% | 何も教えなくても7件を検出。手動の見落としも3件発見 |
なぜWordをMarkdownにするのか
LLMにドキュメントの内容を理解させるとき、Wordのままでは扱いにくいです。
| 形式 | LLMでの扱いやすさ | 理由 |
|---|---|---|
| Word (.docx) | △ | バイナリ形式。テキスト抽出は可能だが構造(見出し階層、テーブル)が失われやすい |
| △ | テキスト抽出の品質がレイアウトに依存。マルチカラムやテーブルが崩れる | |
| Markdown | ◎ | プレーンテキストで構造が明示的。LLMのコンテキストにそのまま入る。Gitで差分管理もできる |
今回はdocx、pptx、xlsxなど数十ファイルを一括変換しています。
既存ツールでは何が足りないか
Pandoc、MarkItDown、Mammothの3ツールで同じdocxを変換して比較しました。
| 観点 | カスタムスクリプト | MarkItDown | Pandoc | Mammoth |
|---|---|---|---|---|
| 画像抽出 | ◎ (外部PNG) | △ (base64埋込) | ○ | ○ |
| 画像余白トリミング | ◎ | × | × | × |
| EMF/WMF→PNG変換 | ◎ | △ | × | × |
| テーブル(Markdown記法) | ◎ | ○ | × (HTML化) | △ (崩れ) |
| 縦ヘッダーテーブル | ◎ | △ | △ | × |
| 見出し階層 | ◎ | ○ | △ | ○ |
| 日本語番号書式(①②③等) | ◎ | ○ | ○ | ○ |
| フィールドコード(図表番号) | ◎ | △ | ○ | △ |
EMF/WMF画像が表示できない
一番困ったのがこれです。調査対象のdocxでは画像の約4割がEMF(Enhanced Metafile)形式で、既存3ツールはどれもPNGに変換できません。
- Pandoc、Mammothはそのまま.emfファイルとして抽出。ブラウザでもGitHubでも表示できない
- MarkItDownはbase64で
image/x-emfとして埋め込む。やはり表示できない
表示不能な画像が4割残る状態では困ってしまいます😇
テーブルがHTMLに逃げる、または崩れる
Pandocは複雑なテーブル(セル内改行、結合セルなど)をHTMLの<table>タグにフォールバックします。Markdownとしての可読性は失われ、LLMに渡すときにHTMLタグがノイズになります。
Mammothはセル内に改行が入るとテーブル構造が壊れ、行がバラバラになります。
また、この文書には「1列目がラベル、2列目が内容」という縦方向ヘッダーのテーブルが多数あります。Markdownテーブルでは1行目が必ずヘッダーになるため、そのまま変換すると1行目のデータがヘッダー扱いされてしまう。既存ツールはこれに対応していません。
図表番号のフィールドコードが取れない
Wordの図表番号はSTYLEREF+SEQフィールドコードで自動生成されていて、python-docxのpara.textでは取得できません。既存ツールでも図表番号が欠けたり不完全になるケースがあります。
そもそもWordの使い方が良くない問題
ここまでツール側の課題を挙げましたが、正直なところ元のWord文書にも問題があります。今回の文書では:
- Heading 1が未使用で、Heading 2から始まっている
- Heading 5、7が欠番(H2→H3→H4→H6→H8と飛んでいる)
- 標準の見出しスタイルの代わりに、丸数字用や括弧付き番号用の独自スタイルを定義して見出しとして使っている
標準の見出しスタイルを使っていればPandocでもMarkItDownでも十分に変換できます。独自スタイルは名前がHeading Nではないので、どのツールも見出しとして認識できずプレーンテキストに落ちる。
とはいえ、受領する設計書のスタイルが綺麗に管理されていることは、ほぼありません😇
見出しの階層が歯抜けだったり、独自スタイルが乱立していたり、番号書式がカスタム定義だったりするのは日常です。
(Word使うなら見出しをちゃんと管理して欲しいですが・・・)
「Wordの使い方が悪い」で片付けても変換はできないので、結局スクリプト側で吸収する必要があります。
Claudeでカスタムスクリプトを書いた
python-docxでの構造解析、LibreOfficeでのEMF→PNG変換、PILでの余白トリミングを組み合わせたカスタムスクリプト(約900行)をClaudeに書いてもらいました。
既存ツールをベースにしても、EMF変換パイプライン・縦ヘッダーテーブル検出・フィールドコード解析・画像トリミングは結局カスタム実装が要るので、最初からスクラッチで書くのと開発量はあまり変わりません。元Wordのスタイル問題への対応も含めると、なおさらです。
手動チェックで見つかった11件の問題
変換スクリプトを書いて動かし、元のWordと見比べて直す。このサイクルを繰り返して出てきた問題が11件です。
| # | 問題 | 原因 | 影響 |
|---|---|---|---|
| 1 | EMF/WMF画像が壊れている | PILがEMF形式を変換できず、生バイナリを.png拡張子で保存 | 画像の約4割が表示不能 |
| 2 | 画像に大きな余白 | LibreOfficeがA4全体をレンダリング | 図が小さく見づらい |
| 3 | 図キャプションが消える | Wordのフィールドコード(fldChar)内のテキストをpara.textで取得できない |
「図 1-1 現行システム概要図」等が欠落 |
| 4 | 図番号のハイフンが欠ける | STYLEREF+SEQフィールドの間にハイフンリテラルがない | 「図 1-1」が「図 11」に |
| 5 | 独自スタイル(丸数字用)が通常テキスト | カスタムスタイル名をスクリプトが認識しない | 見出しがフラットなテキストに |
| 6 | Heading 4〜9が見出しにならない | Heading 1/2/3のみ対応していた | 文書構造の大部分が失われる |
| 7 | 縦ヘッダーテーブルの誤認識 | 1列目がラベルの2列テーブルで、1行目がヘッダー扱いされる | テーブルの意味が変わる |
| 8 | ファイル名の括弧でリンク破損 |
) がMarkdownリンク構文の閉じ括弧と衝突 |
画像リンクが壊れる |
| 9 | 見出しレベルが深すぎる | Word Heading 2→###、Heading 8→######超え |
読みにくい |
| 10 | 独自スタイル(括弧番号用)未対応 | #5と同様、カスタムスタイルが通常テキスト化 | 見出し構造が欠ける |
| 11 | テキストボックスの内容が4重に出力 | mc:AlternateContent(Choice+Fallback)×テキストボックス重複 | テキストがゴミ化 |
数十ページのWord文書に対してこれらを見つけるのに、WordとMarkdownを何度も往復しました。
(設計書1つ分をやって、心が折れそうになりました。まだまだ設計書あるんですけどね😇)
しかも厄介なのは、問題の原因がWord XMLの深い部分にあることです。たとえば#3の図キャプション消失。Wordの図表番号はフィールドコード(fldCharのbegin/separate/end)で生成されていて、python-docxのpara.textではこの中のテキストを返してくれません。#11のテキスト4重出力もmc:AlternateContentのChoice/Fallback二重構造とテキストボックスの重複が組み合わさった結果です。問題を見つけるだけでなく、docxのXMLを読んで原因を特定し、スクリプトに修正を入れるところまでが1セット。これを手動で繰り返すのは、非常に手間がかかります。
Claudeはdocx(OOXML)の仕様を知っているので、このXML解析と原因特定を自律的にやれるのでは?と考えて、品質チェックの自動化に取り組みました。
Claudeで自律チェック
3つの診断手法
| 手法 | 内容 | 検出できるもの |
|---|---|---|
| docx構造分析 | python-docxで全スタイル・番号定義・フィールドコード・画像形式を走査 | 未対応スタイル、EMF/WMF画像、フィールドコードの存在 |
| MD構造検証 | 変換後MDの見出し・図キャプション・テーブル・テキスト異常を検査 | キャプション欠落、見出しレベルのギャップ、テキスト重複 |
| 元Word画像との比較 | 元docxをLibreOffice→PDF→PNGに変換してページ画像化し、Claudeが変換後MDと見比べる | レイアウトの大きな差異、画像の余白 |
知見ありのdiagnoseツール:90%検出
手動で見つけた問題の観点をPythonスクリプトに組み込み、自動検出できるか検証しました。
python quality_loop.py diagnose \
--docx source_document.docx \
--md converted_output.md
============================================================
DIAGNOSIS REPORT: source_document.docx
============================================================
Total issues: 18 (critical: 6, major: 10, minor: 2)
[CRITICAL] UNHANDLED_HEADING_STYLE
Heading style "Heading 4" (XX paragraphs) is not converted to Markdown headings
[CRITICAL] UNHANDLED_HEADING_STYLE
Heading style "Heading 6" ...
[MAJOR] EMF_WMF_IMAGES
XX images are EMF/WMF format. PIL may fail to convert these.
[MAJOR] MISSING_FIGURE_CAPTION
X figure(s) have no caption text following the image reference.
...
11件中9件を検出。検出できなかった1件は対象文書にその条件がなかっただけなので、実質的にはほぼ全件です。
知見を検出ルールに落とし込めれば、自動チェックで十分な精度が出ます。
ゼロ知識のサブエージェント:64%検出
ここからが本題です。過去の会話コンテキストを一切引き継がないClaude Codeのサブエージェントに、同じ監査をやらせました。
渡したのは4つだけ。
- 元のdocxファイル
- 変換スクリプトのソースコード
- 変換後のMarkdownファイル
- 元Wordのページ画像(LibreOfficeでPDF化してページごとにPNG化したもの)
指示は「変換品質の問題を見つけて全て報告せよ」だけです。どんな問題があるかは教えていません。
18件報告、手動発見の7件をカバー
| # | 手動で発見した問題 | サブエージェント | 検出方法 |
|---|---|---|---|
| 1 | EMF/WMF画像の破損 | 検出 |
fileコマンドで全画像をチェック |
| 3 | フィールドコード内テキスト欠落 | 検出 | 章番号・節番号の欠落として報告 |
| 5 | 独自スタイル(丸数字用)未対応 | 検出 | カスタムスタイルの未処理として報告 |
| 6 | Heading 4〜9未対応 | 検出 | docxスタイル一覧とスクリプトの対応漏れ |
| 7 | 縦ヘッダーテーブル | 部分検出 | vMergeの重複行として別角度で検出 |
| 10 | 独自スタイル(括弧番号用) | 検出 | #5と同様 |
| 2 | 画像の余白 | 未検出 | 壊れたEMF画像を読めず視覚比較不可 |
| 4 | 図番号のハイフン欠け | 未検出 | テキストレベルの細かい差異 |
| 11 | mc:AlternateContent重複 | 未検出 | MD出力のパターン分析に至らず |
人間が見落としていた問題も拾った
| 問題 | 重要度 | 内容 |
|---|---|---|
| vMergeセルのテーブル行重複 | HIGH | Wordの縦結合セルでpython-docxが結合元テキストを全行に返す。Markdownで同じ文字列が複数行に出る |
| 目次のページ番号出力 | MEDIUM | TOCスタイルの段落がページ番号付き生テキストとして出力される。Markdownでは意味がない |
| 画像代替テキストの不一致 | MEDIUM |
![図1]の連番が元文書の図番号(図 1-2等)と対応しない |
これらは実際に修正を入れました。
所感
得意なこと、苦手なこと
Claudeが強いのは構造の分析です。docxのXML構造解析(スタイル一覧、番号定義、フィールドコード)はClaudeの既有知識でカバーでき、変換スクリプトのコードリーディングと組み合わせると未処理パターンの特定まで自律的に進みます。
弱いのは細かい差異の検出です。ハイフン1文字の有無、画像余白の大小、4重に重複したテキストが「異常」であるという判断。こういうものはMD出力を注意深く読まないと見つからないです。
運用するならこの形
今回作ったのは2つのスクリプトです。変換用のconvert_to_markdown.pyと、品質チェック用のquality_loop.py。後者のdiagnoseコマンドは検出した問題をCritical(変換が壊れている)、Major(情報が欠ける)、Minor(体裁の問題)の3段階で報告します。
1回の診断で大半の問題が出てきます。Claudeが原因を調べてスクリプトを直し、再変換して再チェック。手動で介入するのは、診断ルールにまだない新しいパターンが出たときだけです。
数字で振り返り
| 指標 | 値 |
|---|---|
| 手動で発見した問題 | 11件 |
| Claude Code(知見ゼロ)で検出 | 7件 (64%) + 手動見落とし3件 |
| 品質チェックツール(知見あり)で検出 | 9件 (90%) |
完全自律は遠いですが、手動チェックの負荷を大きく減らせる手応えはあります。新しいWord文書を受け取ったとき、まずClaudeに走らせて大きな問題を潰し、残りを目視で確認する運用が現実的です。
最後に、GMOコネクトではサービス開発支援や技術支援をはじめ、幅広い支援を行っておりますので、何かありましたらお気軽にお問合せください。