Azure AI Document IntelligenceでWord(.docx)解析が常に1ページになる理由と対処法|PDF変換・OpenXML・セマンティック分割まで徹底解説【2025年最新】

Azure AI Document Intelligence で Word(.docx)を解析すると、PDF ではページ単位の結果が得られるのに Word は「常に 1 ページ分」しか返ってこない――この戸惑いを、仕様の背景から整理し、検索インデックス設計まで踏み込んだ現実解(変換/自前分割/セマンティック分割)を具体的手順・コード例つきでまとめます。

目次

Azure AI Document Intelligence × Word(.docx)で起こる現象

Word 文書を prebuilt-documentprebuilt-layoutread などで解析すると、レスポンス内の pages が 1(または 3,000 文字ごと程度で増える論理ページ)に見えることがあります。PDF は物理ページ+座標(bounding box)が返るため、ページ単位の切り分けが容易ですが、Word は「リフロー」前提のフォーマットであり、物理的なページ区切りを持たないため、現在の API ではページ対応が制限されています。

要点(TL;DR)

  • 仕様どおり:Word は「約 3,000 文字 ≒ 1 ページ」の論理ページで扱われる。境界座標・pageRangelines は未対応。
  • PDF は別物:物理ページ+座標が返るのでページ分割しやすい。
  • 回避策:①PDF へ変換して解析、②OpenXML/Word API でページ推定、③文字数ベースの擬似ページ、④ページに依存しないセマンティック分割。
  • 2025年11月時点:Word の「ページ番号」を直接検出する機能の公式ロードマップは未公表。

PDF と Word(DOCX)の機能差まとめ

観点PDFWord(.docx)
ページ概念物理ページ論理ページ(約 3,000 文字単位)
座標(bounding box)取得可未対応
pageRange 指定利用可未対応
lines / 行オブジェクト取得可(モデルによる)未対応
テーブル抽出可(座標あり)可(ただし座標なし)
使うべきモデル例prebuilt-layout, prebuilt-documentprebuilt-document(論理ページ)

なぜ「1 ページ」なのか:原因・仕様の背景

Word はレイアウトエンジンにより表示時に改ページが決まる「リフロー」型です。これに対し、PDF はレンダリング済みなのでページと座標が固定です。現行の SDK/REST API(例:2024‑02‑29-preview2023‑10‑31-preview、v4.0 GA など)では、Word のページは 約 3,000 文字を 1 単位とする論理ページで扱われ、座標・pageRangelines は未対応です。そのため、複数ページの .docx でもレスポンス上は「1 ページ(または 3,000 文字ごとに増える程度)」に見えます。

レスポンスのイメージ(DOCX)

{
  "modelId": "prebuilt-document",
  "apiVersion": "2024-02-29-preview",
  "content": "(文書全文)",
  "pages": [
    {
      "pageNumber": 1,
      "spans": [{ "offset": 0, "length": 2987 }]
      // 座標情報はなし(Word は未対応)
    }
  ],
  "paragraphs": [
    { "spans": [{ "offset": 0, "length": 15 }], "role": "title" },
    { "spans": [{ "offset": 16, "length": 120 }], "role": "body" }
    // …
  ],
  "tables": [ /* 座標なしの表構造 */ ]
}

PDF 解析のイメージ(比較用)

{
  "modelId": "prebuilt-layout",
  "pages": [
    {
      "pageNumber": 1,
      "width": 8.5,
      "height": 11,
      "unit": "inch",
      "words": [ { "content": "Azure", "polygon": [/* 座標 */] } ],
      "lines": [ /* 行・座標あり */ ],
      "tables": [ /* セル座標あり */ ]
    },
    { "pageNumber": 2, /* … */ }
  ]
}

検索インデックス設計への影響

「ページ単位でチャンク化して索引化したい」ニーズは強いですが、Word の仕様を踏まえると次の方針になります。

  • 物理ページへの厳密なマッピングが必要:PDF へ事前変換が最も確実。
  • Word のまま処理:OpenXML/Word API でページ推定またはセマンティック分割に切り替える。
  • 暫定対応:3,000 文字 ≒ 1 ページの擬似ページで割り切る。

解決策・回避策の比較

方法概要メリットデメリットおすすめ度
A. 事前に PDF へ変換して解析Word → PDF 変換後に Document Intelligence を実行物理ページ・座標が取れる/既存ロジック流用変換コスト/動的要素の喪失◎(ページ厳格要件)
B. Word API/OpenXML SDK でページ分割Word 組版に近い区切りを自前推定UI の「ページへジャンプ」を再現しやすい実装・運用が難しい/レンダラー必要○(オンプレ/社内ツール)
C. 文字数ベースで擬似ページ3,000 文字 ≒ 1 ページでチャンク化最短で実装可/コスト低実ページと一致しない△(暫定策)
D. セマンティック分割へ方針転換見出し・段落・表など構造でチャンク化検索品質向上が見込める/汎用ページジャンプ不可/UI設計要◎(長期安定)

実装レシピ A:PDF へ変換してから解析

もっとも堅い方法です。既存の PDF 向けパイプラインと同じ前提で組めます。

処理フロー

  1. DOCX を PDF に変換(Microsoft 365 / Graph の変換 API、もしくはサーバーサイドの Office レンダラーやヘッドレス LibreOffice 等)。
  2. 変換済み PDF を prebuilt-layout(または prebuilt-document)で解析。
  3. ページ座標に基づいてチャンク化し、インデックスに投入。

解析リクエスト(REST、概念例)

POST https://<resource-name>.cognitiveservices.azure.com/
documentintelligence/documentModels/prebuilt-layout:analyze?api-version=2024-02-29-preview
Content-Type: application/json
Ocp-Apim-Subscription-Key: <key>

{
"urlSource": "https:///converted.pdf"
} 

レスポンスの pages[n].words[].polygontables を利用すれば、座標ベースの正確なページ分割・ハイライトが可能です。

インデックス設計(例:Azure AI Search)

フィールド用途
docIdEdm.String(key)文書の一意キー(ハッシュ推奨)
chunkIdEdm.Stringページ番号+セクション情報など
pageNumberEdm.Int32物理ページ番号
contentEdm.Stringチャンク本文
contentVectorCollection(Edm.Single)埋め込みベクトル
bboxEdm.Stringハイライト用座標(JSON)
headingPathEdm.String見出し階層(パンくず)

実装レシピ B:OpenXML/Word API でページ境界を推定

Word の「手動改ページ(<w:br w:type="page"/>)」や「段落書式のページ前改ページ(w:pPr/w:pageBreakBefore)」、セクション区切り(w:sectPr)を利用し、自前で「擬似ページ」を算出します。サーバー側でレンダリング(Office/LibreOffice)を行う場合は、より実際のページに近い結果が得られます。

OpenXML でページ区切りを拾う C# 例(抜粋)

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

var breaks = new List(); // コンテンツ内オフセットのリスト
using (var doc = WordprocessingDocument.Open(path, false))
{
var main = doc.MainDocumentPart.Document.Body;
int offset = 0;
foreach (var p in main.Elements())
{
// ページ前改ページ(段落書式)
var pPr = p.ParagraphProperties;
if (pPr?.PageBreakBefore != null) breaks.Add(offset);


    foreach (var r in p.Elements<Run>())
    {
        foreach (var br in r.Elements<Break>())
        {
            if (br.Type?.Value == BreakValues.Page) breaks.Add(offset);
        }
        // テキスト長をオフセットに足す
        var txt = string.Concat(r.Elements<Text>().Select(t => t.Text));
        offset += txt.Length;
    }
    offset += Environment.NewLine.Length;
}


}
// breaks を基点に自前ページ配列を作成 

この方法は 「Word の実表示にかなり近い」擬似ページを作れますが、等幅でないフォント・余白設定・禁則などの組版差は残ります。高精度に寄せるならサーバー側レンダリング(Word/LibreOffice)で PDF 化 → A へ寄せるのが現実的です。

実装レシピ C:3,000 文字ごとの擬似ページ

仕様に合わせて、約 3,000 文字を 1 単位と見なす簡易アプローチです。段落の途中で切れないよう軽く工夫します。

Python(概念)

def chunk_by_chars(text: str, size: int = 3000, overlap: int = 200):
    chunks = []
    start = 0
    n = len(text)
    while start &lt; n:
        end = min(start + size, n)
        # できれば段落単位の区切りを後方探索
        sep = text.rfind("\n\n", start, end)
        if sep != -1 and sep - start &gt; size * 0.6:
            end = sep
        chunks.append(text[start:end])
        start = max(end - overlap, 0)
    return chunks

「厳密なページ一致」は捨てる代わりに、実装の速さ・安定性を取ります。UI は「ページ」ではなく「チャンク番号+見出し」でナビゲーションするのが相性良しです。

実装レシピ D:セマンティック分割(おすすめ)

ページ番号に依存せず、見出し・段落・表・箇条書きなどの論理構造やトピックでチャンク化します。長文の再検索(RAG, QA)ではページよりも意味単位の方が精度が高いケースが多いです。Word でも paragraphs 情報(役割・スタイル)を利用して分割可能です。

セマンティック分割のアルゴリズム例

  1. paragraphs を走査し、heading/title ロールを境にセクション単位へ分割。
  2. 各セクション内で tables/箇条書きを保持しつつ、3,000〜4,000 文字未満へ再分割(オーバーラップ 200〜300 文字)。
  3. 見出し階層(H1 > H2 > …)を headingPath としてメタデータ化。
  4. ベクトル埋め込みとキーワード索引を併用し、UI でハイライト。

擬似コード(TypeScript 風)

type Para = { text: string; role?: string };
function semanticChunks(paragraphs: Para[], max = 3200, overlap = 256) {
  const chunks = [];
  let buf = "", headingPath: string[] = [];
  const flush = () =&gt; {
    if (!buf.trim()) return;
    chunks.push({ content: buf, headingPath: headingPath.join(" &gt; ") });
    buf = "";
  };
  for (const p of paragraphs) {
    if (p.role === "title" || p.role === "heading") {
      if (buf.length &gt; 0) flush();
      headingPath.push(p.text.trim());
      continue;
    }
    if ((buf + "\n\n" + p.text).length &gt; max) {
      const tail = buf.slice(-overlap);
      flush();
      buf = tail + p.text;
    } else {
      buf += (buf ? "\n\n" : "") + p.text;
    }
  }
  flush();
  return chunks;
}

UI/UX 設計のヒント

  • ページジャンプが必須:A(PDF 変換)で座標を取り、ビューワ(PDF.js 等)と連携。
  • ページに依存しないナビ:D(セマンティック分割)で「見出しツリー+チャンク」ナビを提供。
  • ハイブリッド:Word は D、PDF は A で処理し、同一 UI に統合。
  • ハイライトcontent のオフセット(spans)を保存し、ヒット箇所の前後 N 文字を抜き出してプレビュー。

実務アーキテクチャ例

  1. 取り込み:Blob ストレージへ DOCX アップロード(メタに contentType を保持)。
  2. 前処理:DOCX の場合は
    • 方針 A:コンテナ(LibreOffice)または SaaS 変換で PDF 化。
    • 方針 B:OpenXML で改ページ・見出し抽出。
    • 方針 D:Document Intelligence の paragraphs を主軸に分割。
  3. 解析:Document Intelligence(prebuilt-documentprebuilt-layout)。
  4. チャンク化:A はページ、B/C/D は擬似ページ・セマンティック。
  5. 索引:キーワード+ベクトルの二段構え(Azure AI Search)。
  6. 配信:ビューワと連携(PDF はページ、Word は見出しでジャンプ)。

運用チェックリスト

  • DOCX は座標・pageRange 非対応であることを前提に設計しているか。
  • ページ厳格要件のユースケースでは PDF 変換の SLA とコストを見積もったか。
  • セマンティック分割時の「最大長」「オーバーラップ」「見出し階層」を明示的に管理しているか。
  • UI に「ページ」依存のないナビゲーション(見出しツリー/検索ハイライト/文脈プレビュー)があるか。
  • エラー時のフォールバック(変換失敗→C で暫定索引など)を用意したか。

よくある落とし穴と対処

落とし穴症状対処
DOCX の pageRange が効かない指定しても 1 ページ分しか返らない仕様。PDF に変換するか、擬似ページで対応
行オブジェクトが取れないlines が空/未出力仕様。段落(paragraphs)を利用し論理単位で扱う
表のセル座標がないセルの位置ハイライト不可PDF 解析へ切替、または表全体をテキスト化し文脈でハイライト
手動改ページを復元したいページ表示とズレるOpenXML で改ページ要素(w:br)等を解析して擬似ページ化

コード断片(解析の呼び出しとポーリング)

解析は非同期です。operation-location をポーリングして結果を取得します。

// JavaScript(概念)
// 1) analyze リクエスト
const res = await fetch(
  "https://&lt;resource&gt;.cognitiveservices.azure.com/documentintelligence/documentModels/prebuilt-document:analyze?api-version=2024-02-29-preview",
  {
    method: "POST",
    headers: {
      "Ocp-Apim-Subscription-Key": process.env.DI_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ urlSource: "https://&lt;blob&gt;/file.docx" })
  }
);
// 2) operation-location を取得してポーリング
const op = res.headers.get("operation-location");
let result;
for (;;) {
  await new Promise(r =&gt; setTimeout(r, 1500));
  const r2 = await fetch(op, { headers: { "Ocp-Apim-Subscription-Key": process.env.DI_KEY }});
  result = await r2.json();
  if (result.status === "succeeded" || result.status === "failed") break;
}
// 3) result.pages / paragraphs / tables を用いて分割

品質向上の実戦テクニック

  • 見出しスコアリング:文字サイズ・太字・全角カナ・番号(1. / 1.1 / 第○章)を特徴量に、区切り点を強化。
  • 表の扱い:1 セルを 1 チャンクにせず、表全体を 1 チャンクにして見出しと関連付ける。
  • オーバーラップ:300〜500 文字の重ねを推奨。検索ヒット後の文脈欠落を防止。
  • 重複排除:ヘッダー/フッター/署名ブロックを取り除く(特に契約書)。
  • 監査性docIdchunkIdsourceOffsetspans.offset)を保存し、誰でも再現できるように。

パフォーマンスとコストの観点

  • バッチ変換:A 案(PDF 化)はキューイングでスループットを確保。小容量ファイルは複数束ねてスループット最適化。
  • サイズ閾値:Word のみ・短文は D(セマンティック)で即索引、長文・図表が多い文書は A(PDF)へ。
  • 差分更新:ハッシュでコンテンツ差分を検出し、変更チャンクだけ再解析。

セキュリティとコンプライアンス

  • 機微情報(個人・機密条項)は前処理でマスキング(正規表現+ルール+辞書)。
  • インデックス化前にアクセスレベル(部署・ラベル)をメタデータで紐づけ、検索時フィルタリング。
  • 変換サービングのログに原文を残さない。監査はハッシュとメタのみ。

今後の見通し(2025年11月時点)

Word 文書の物理ページ番号検出・座標付与について、現時点で公開ロードマップはありません。Issue/フィードバックで要望を挙げつつ、A(PDF 化)・D(セマンティック分割)中心の設計にしておくのが堅実です。将来 Word のページ対応が拡充された場合も、セマンティック分割はそのまま価値を持つため、後方互換の高い投資になります。

まとめ

  • 現状仕様:Word はページを返さない(論理ページのみ)。PDF は物理ページ+座標が返る。
  • 要件が「ページ厳格」なら、まずはA:PDF 変換→解析が最短で確実。
  • Word のままなら、B:OpenXML 推定D:セマンティック分割を選択。C(3,000 文字)は暫定策に。
  • 検索 UXはページ番号依存から脱却し、見出し・段落・表を軸に再設計すると成果が出やすい。

付録:推奨フローの具体化

  1. 変換コストを許容できる:A(PDF 変換)が最も確実。既存の PDF パイプラインがそのまま生きる。
  2. 変換が難しい:業務・運用制約に合わせて B(OpenXML)または D(セマンティック)。
  3. 短期リリース:C(3,000 文字擬似ページ)で立ち上げ、後から A/D へ移行。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次