Azure AI Document Intelligenceで1PDF内の複数請求書を自動分割するには?splitModeとprebuilt-invoiceの限界と実務解説

経理業務でよくある「1つのPDFに複数の請求書がまとめてスキャンされている」ケース。Azure AI Document Intelligence(旧 Form Recognizer)で完全自動処理したいとき、prebuilt-invoiceやsplitModeだけでどこまで出来るのか、どこから自前実装が必要なのかを整理します。

目次

Azure AI Document Intelligence と「1PDFに複数請求書」という現場課題

紙の請求書をスキャンしている現場では、以下のような運用が珍しくありません。

  • 月末に複数社ぶんの請求書をまとめてスキャンし、1つのPDFファイルに連結して保管している
  • 1PDFの中に「請求書A(1〜2ページ)」「請求書B(3〜4ページ)」「請求書C(5ページ)」…と同じ請求書レイアウトが繰り返し登場する
  • このPDFをAzure AI Document Intelligenceに渡して、請求書単位で金額や請求先などを自動抽出したい

ここで気になるのが、次のような疑問です。

  • Document Intelligenceに、「同一タイプの文書を自動的に切り出す」機能はあるのか?
  • splitMode (none / perPage / auto) を使えば、1つのPDFから複数の請求書を自動分割できるのか?
  • それとも、自前でPDFを分割してから prebuilt-invoice に投げるしかないのか?

この疑問に対して、最新の公式ドキュメントやMicrosoft Q&Aの情報を踏まえて、実務的な落としどころを整理していきます。

結論:prebuilt-invoice単体では「1ファイル=1請求書」が前提

まず、よく使われる prebuilt-invoice モデルの前提を押さえておきます。

  • 基本設計は「1ドキュメント=1請求書」です。
  • 1つのPDFに複数の請求書が含まれていても、そのファイル全体を1枚の請求書として扱う想定になっています。
  • Microsoft Q&Aでも「同じ請求書が複数含まれるPDFを prebuilt-invoice だけで自動分割する機能はない」と明言されています。

つまり、prebuilt-invoiceをそのまま使う限り、「請求書単位の自動分割」は提供されていません。この点をまず押さえたうえで、「ではどうするか?」を考えていきます。

やりたいことDocument Intelligenceの素の挙動現実的なアプローチ
1PDFの中にある複数の請求書を
自動で請求書単位に分割したい
prebuilt-invoice は
「1ファイル=1請求書」想定
前処理で分割し、
分割後のPDFを prebuilt-invoice へ
請求書+発注書+見積書など
複数種類の書類が混ざったPDFを分類・分割
splitMode付きの
カスタム分類モデルで対応可能
カスタム分類+モデル構成(model compose)で
文書タイプごとに自動ルーティング
同じレイアウトの請求書が1ファイル内に繰り返し登場し、
それぞれを自動で切り出したい
prebuilt単体での自動分割は不可①自前ロジックでPDF分割
②カスタム分類+モデル構成+splitModeで
「疑似的に」実現(後述)

splitMode の役割と限界を整理する

splitMode とは何か

splitMode は、カスタム分類モデルやモデル構成(model compose)で、1つのファイルをどのように「論理ドキュメント」に分割するかを制御するためのオプションです。

値挙動典型的な用途
noneファイル全体を1つのドキュメントとして扱う標準的な「1ファイル=1文書」シナリオ
perPageページ単位でドキュメントを分割する「1ページ=1伝票」がほぼ保証されている場合
auto分類モデルがページ構造や内容から
自動的に分割位置を推定する
複数タイプの文書が混在したPDFなど

v4.0(API バージョン 2024‑11‑30)では、デフォルトが none に変更され、auto での自動分割を使いたい場合は明示的に指定する必要があります。

公式ドキュメント上の「同一タイプ複数インスタンス」への言及

2024年末に更新された Microsoft Learn の モデル構成(model compose) のドキュメントでは、classifer+splitMode+model compose を組み合わせることで、同一タイプのドキュメントが1ファイル内に複数あるケースも扱えると明記されています。

要約すると、次のようなことが書かれています。

  • カスタム分類モデルでファイル内のページ群を分類する
  • splitMode を併用すると、1ファイル内に同じタイプの文書が複数あっても、それぞれを独立したドキュメントとして切り出せる
  • モデル構成(model compose)で、分類結果に応じて適切なカスタム抽出モデルへルーティングできる

ただし、ここで言及されているのはあくまで「カスタムモデル」です。prebuilt-invoiceのようなprebuiltモデルは、model compose の構成対象としては明示されていません。

なぜ「prebuilt-invoiceだけでは自動分割できない」と言えるのか

まとめると、次のような整理になります。

  • prebuilt-invoice 単体:
    • 1ファイルを1請求書として扱う設計
    • splitMode を直接指定するインターフェースはなし
    • Microsoft Q&A でも「同一タイプ複数インスタンスの自動分割は未対応」と回答されている
  • カスタム分類+model compose+カスタム抽出:
    • splitMode=auto により、同じタイプの文書が複数含まれているファイルを自動で分割する機構が用意されている
    • ただし、prebuilt-invoiceはこの構成にそのまま載せられない
    • 結果的に、prebuilt-invoiceで請求書を解析したいなら、PDF分割は自前で実装した方がシンプル

このため、本記事では「前処理で請求書単位に分割 → prebuilt-invoice またはカスタム抽出で解析」という構成を現実解として解説します。

最小構成ワークフロー:前処理で分割してから prebuilt-invoice へ

実務で実装しやすく、かつ堅牢なのは次のような5ステップ構成です。

  1. レイアウト抽出(Layout / Read)
  2. 境界検出(ルール or 軽量ML)
  3. PDF分割
  4. 請求書解析(prebuilt-invoice or カスタム抽出)
  5. 例外処理(人手レビュー)
ステップ目的使用する主なサービス / ライブラリ例
1. レイアウト抽出各ページのテキスト・座標・テーブル構造を取得Document Intelligence の prebuilt-layout / Read モデル
2. 境界検出どのページからどのページまでが1件の請求書かを判定独自ロジック(ルールベース/ML)、正規表現、座標計算
3. PDF分割検出したページ範囲ごとにPDFを切り出すPyPDF2, iText, PDFSharp, .NET の PdfDocument など
4. 請求書解析分割済みPDFを請求書単位で解析Document Intelligence prebuilt-invoice or カスタム抽出
5. 例外処理スコアが低い/境界が曖昧なケースを人手で確認Power Automate, Logic Apps, Teams Approvals など

1. レイアウト抽出(Layout / Read)

まずは軽量な prebuilt-layout(または Read) で、全ページのテキストと座標を取得します。

  • 1ページごとに
    • テキスト行
    • ワードとバウンディングボックス
    • テーブルの行・列
    が取得できるため、「どの位置に何のテキストがあるか」を元に境界検出できます。

Python SDKを使ったプレーンな呼び出しイメージは次のようなものです(実際には適宜エラー処理や認証を追加)。

<pre><code class="language-python">
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.core.credentials import AzureKeyCredential

endpoint = "<YOUR_ENDPOINT>"
key = "<YOUR_KEY>"

client = DocumentIntelligenceClient(endpoint, AzureKeyCredential(key))

with open("invoices.pdf", "rb") as f:
    poller = client.begin_analyze_document(
        model_id="prebuilt-layout",
        body=f
    )
result = poller.result()

for page in result.pages:
    print("page number:", page.page_number)
    for line in page.lines:
        print(line.content, line.polygon)
</code></pre>

2. 境界検出(ルール or 軽量ML)

レイアウト情報が取れたら、「どこからどこまでが1枚の請求書か」という境界を決めます。ここがこの問題の肝です。

シンプルなところから始めるなら、まずはルールベースで設計します。

  • ページ上部の大きなテキストに「請求書」「INVOICE」「御請求書」が含まれている
  • 近くに「請求書番号 / Invoice No.」「発行日 / Issue Date」「取引先 / Bill To」などがまとまっている
  • ページ下部に「小計」「消費税」「合計」「お支払期日」などの金額ブロックがある

このような特徴が揃っているページを「請求書の先頭ページ候補」として扱い、次の先頭候補が出てくる手前までを1請求書とみなします(詳しいロジックは後述)。

3. PDF分割

境界検出の結果、「請求書1:1〜2ページ」「請求書2:3ページ」「請求書3:4〜5ページ」…のようにページ範囲のリストが得られたら、任意のPDFライブラリで分割します。

例えば Python なら PyPDF2 を使って次のような処理が可能です。

<pre><code class="language-python">
from PyPDF2 import PdfReader, PdfWriter

def split_pdf_by_ranges(src_path, ranges):
    reader = PdfReader(src_path)
    outputs = []

    for idx, (start, end) in enumerate(ranges, start=1):
        writer = PdfWriter()
        for page in range(start - 1, end):  # 1始まりを0始まりに変換
            writer.add_page(reader.pages[page])

        out_path = f"invoice_{idx}.pdf"
        with open(out_path, "wb") as fp:
            writer.write(fp)
        outputs.append(out_path)

    return outputs
</code></pre>

4. 請求書解析(prebuilt-invoice or カスタム抽出)

ここまでくれば、分割されたPDFはそれぞれ「1請求書=1ファイル」の状態なので、prebuilt-invoiceモデルにそのまま投げるだけです。

  • 請求書番号、発行日、支払期日、請求先、明細行、税額、合計金額…などを高精度で抽出可能
  • もし請求書レイアウトが独特で prebuilt-invoice だけでは精度が足りなければ、カスタム抽出モデルと併用する

5. 例外処理(人手レビュー)

完全自動を目指しつつも、現場で重要なのは「怪しいものは人に見せる」ラインを決めることです。

  • 境界検出スコアが低い(開始/終了条件が片方しか満たされていないなど)
  • 請求書番号や合計金額が抽出できない/重複している
  • prebuilt-invoiceのフィールド信頼度が総じて低い

このようなケースは、Power Automate や Logic Apps の承認フローに回し、担当者が PDF と抽出結果を見比べて修正できるようにしておくと安全です。

境界検出ロジックの設計パターン

境界検出ロジックをもう少し具体的に見ていきます。

請求書の「先頭ページ」を見つける

先頭ページ候補は、一般に次の条件を組み合わせて判定します。

  • ページ上部の大きなフォントのテキストが「請求書」「INVOICE」「御請求書」などを含む
  • その近く(上部1/3領域)に
    • 「請求書番号」「伝票番号」「Invoice No.」
    • 「発行日」「請求日」「Issue Date」
    • 「御中」「様」で終わる取引先名
    がセットで現れる
  • ベンダーロゴ+住所ブロックが毎回同じ座標近辺に現れる

これらの条件を満たしたページを「スコア化」し、一定以上のスコアを持つページを請求書の先頭として採用します。

特徴例スコアのつけ方例
見出しキーワード請求書, INVOICE, 御請求書含まれていたら +3
番号系ラベル請求書番号, Invoice No.含まれていたら +2
日付ラベル発行日, 請求日, Date含まれていたら +1
取引先名パターン「株式会社〜御中」「〜株式会社 御中」正規表現一致で +2
ロゴ位置の安定性同じベンダーで座標が±数ピクセル以内既存請求書との類似度に応じて +1〜+3

請求書の「終端」を見つける

先頭だけでは多ページ請求書を扱えないので、終端条件も組み合わせます。

  • ページ下部に「小計」「消費税」「合計」「総計」「お支払期日」などのラベルがセットで現れる
  • 明細テーブルが終わり、金額サマリが現れた直後に余白が広く開く
  • ページフッターに「Page 1 of 2」「Page 2 of 2」などのページ情報がある

例えば次のようなロジックが考えられます。

  1. 先頭ページ候補のうち、スコアが閾値以上のページをすべて列挙する
  2. それぞれの先頭候補ページから、「次の先頭候補ページの直前」までを同一請求書とみなす
  3. もし終端条件(合計・支払期日など)が途中ページで満たされていれば、そのページを終端として優先的に採用する

多ページ請求書への対応

多ページにわたる請求書では、2ページ目以降にヘッダが無いことも多く、単純な先頭検出だけでは対応しきれません。そこで、「先頭条件+終端条件+明細テーブルの連続性」という複合条件で判断します。

  • 明細テーブルのカラム構成(品名・数量・単価・金額)が続いている限り、同一請求書とみなす
  • ページフッターの「Page X of Y」で Y > 1 かつ X < Y の場合、まだ請求書が続くとみなす
  • 次ページの上部に再度「請求書」「INVOICE」が現れた場合は、新しい請求書の開始とみなす

バーコードやQRコードを活用する

もしベンダーの請求書にバーコードやQRコードに請求書番号が埋め込まれているなら、それを読みに行くのは非常に有効です。

  • Azure AI Vision のバーコード認識や、別途ライブラリでコードを読み取る
  • 各ページに現れるバーコード値が変わったタイミングを請求書境界とみなす

バーコードは印字位置やフォントに左右されにくく、境界検出の強いシグナルになります。

軽量MLによる補強

ルールベースに加えて、ページを次の4クラスに分類するような軽量MLモデルを併用すると、ロジックが安定します。

  • 開始ページ(START)
  • 中間ページ(MIDDLE)
  • 終端ページ(END)
  • 非請求ページ(OTHER)

特徴量の例:

  • ページ上部1/3のキーワード出現数
  • 金額系ラベルの有無と位置
  • 明細テーブルの行数・列数
  • ベンダーロゴの有無

まずは少量の教師データで学習し、ルールベースの結果と組み合わせてスコアリングすることで、誤分割の抑制が期待できます。

高度な構成:Document Intelligenceだけで「疑似自動分割」する

ここまでの構成では、PDFの分割を外部ライブラリで行っていました。しかし、カスタム分類+モデル構成+splitModeを駆使すると、Document Intelligenceの機能だけで「疑似的な自動分割」に近いことも可能です。

構成のイメージ

  1. カスタム分類モデルを作成し、「invoice」という1種類のクラスで学習する
    • 学習データとして、1ファイルに複数請求書を含むPDFも含める
    • API呼び出し時に splitMode=auto を指定する
  2. カスタム抽出モデル(invoice用)を作成する
  3. model compose で、分類モデル+抽出モデルを1つのモデル構成として定義する
  4. 構成モデルに対してPDFを投げると、classifer+splitMode によって
    • 1ファイル内の複数のinvoiceインスタンスを検出
    • それぞれをカスタム抽出モデルで処理

公式ドキュメントでは、「splitMode と併用した model compose により、同一タイプの複数インスタンスを1ファイル内から分割して処理できる」と説明されています。

この構成の注意点

  • 対象はあくまでカスタムモデルであり、prebuilt-invoiceをこの構成に直接組み込むことはできません。
  • 請求書レイアウトが頻繁に変わる場合、カスタム抽出モデルの再学習コストが増大します。
  • 課金は「分類+抽出」の両方に対して発生し、ページ数が多いとコストが嵩みやすくなります。

したがって、Document Intelligenceだけで完結させたい場合の選択肢としては有力ですが、多くの現場では「前処理でPDF分割+prebuilt-invoice」の方が実装・運用コストが低くなるケースが多いです。

Azure上での実装アーキテクチャ例

ここでは、前処理+prebuilt-invoice構成を、Azureのマネージドサービスでどう組むかの例を示します。

  • Blob Storage:スキャン済みPDFの格納場所
  • Event Grid:ファイルアップロード通知
  • Azure Functions:境界検出ロジックとPDF分割、Document Intelligence呼び出し
  • Document Intelligence(Foundry Tools 内の Document Intelligence):レイアウト抽出・請求書抽出
  • Cosmos DB / SQL Database:抽出結果の永続化
  • Power BI / 既存基幹システム:後段処理・可視化
コンポーネント役割
Azure Blob Storageスキャン済みPDFの格納。監査ログの意味も兼ねる。
Event GridBlob へのアップロードイベントをトリガーとして Functions を起動。
Azure Functionsレイアウト抽出→境界検出→PDF分割→prebuilt-invoice呼び出し→結果保存の一連の処理を実装。
Document Intelligenceレイアウト抽出(prebuilt-layout)、請求書解析(prebuilt-invoice)を提供。
Cosmos DB / SQL請求書単位の構造化データ(請求先・金額・明細など)を格納。
Power BI / ERP 連携ダッシュボード表示や既存経理システムへの連携を実現。

Microsoft 公式サンプルの Doc Intelligence in-a-Box のようなリファレンスソリューションも公開されており、これをベースにアダプトすると設計のベースラインとして有用です。

コスト・パフォーマンス設計のポイント

Document Intelligenceはページ単位の従量課金です。レイアウト抽出+請求書解析+分類などを組み合わせると、処理パイプラインによってコストが大きく変わります。

安く・速く・精度よくを両立させるコツ

  • まずは prebuilt-layout で全ページを一度だけ解析し、その結果を境界検出に使う
    • 境界検出に失敗したPDFや、人手確認が必要なPDFには prebuilt-invoice を回さないことでコスト削減
  • 請求書と判定できないページ(広告・白紙など)は prebuilt-invoice に送らない
  • バッチ処理で夜間にまとめて処理することで、Functions のスケール制御をシンプルに

splitMode=perPage を暫定的に使うケース

もし運用上「1ページ=必ず1枚の請求書」というルールが厳守できるのであれば、暫定策として次のような構成もあり得ます。

  • カスタム分類モデルに splitMode=perPage を指定し、ページごとに独立したドキュメントとして扱う
  • 各ページをそのまま prebuilt-invoice に投げる

ただしこの方法は、

  • 多ページ請求書が出た瞬間に破綻する
  • ページの前後関係を利用したチェックができない

といったデメリットがあり、長期的には推奨しづらいという点に注意が必要です。

よくある落とし穴と対策

落とし穴1:スキャン品質が低く、ヘッダや金額が読めない

スキャン解像度が低かったり、傾きが大きかったりすると、OCR結果が乱れ、境界検出も請求書の抽出精度も落ちます。

対策:

  • 解像度 300dpi 以上でのスキャンをルール化
  • 画像前処理(傾き補正・回転補正・二値化)を Functions 内で実施
  • FAX紙や写真撮影画像など品質が著しく低いものは、最初から人手レビュー対象にする

落とし穴2:ベンダーごとにレイアウトがバラバラ

取引先が増えるほど、請求書のレイアウトは多様化し、単一のルールセットでは境界検出が難しくなっていきます。

対策:

  • ベンダー別のルール辞書を持つ
    • ベンダー名やロゴからベンダーIDを推定
    • ベンダーIDごとにヘッダ語彙・フッタパターン・レイアウト特徴を定義
  • 主要ベンダー(請求書枚数が多い上位20社程度)に絞ってチューニング

落とし穴3:境界判定の誤りに気づきにくい

境界検出が静かに失敗していても、金額だけは一見正しそうに見えるため、現場が気づくのに時間がかかることがあります。

対策:

  • 請求書番号の重複チェック(同じ番号が短期間に複数回登場していないか)
  • 合計金額と明細行の合計の突合(差分が一定以上あればアラート)
  • ベンダー単位の月次合計金額を手作業の集計と突き合わせる

落とし穴4:モデルやロジックのバージョン管理が曖昧

カスタム抽出モデルや境界検出ロジックを頻繁に改修すると、「どの請求書がどのロジックで処理されたのか」が分からなくなりがちです。

対策:

  • 処理時に 「モデルバージョン」「境界検出ロジックのバージョン」「設定ハッシュ」 を結果に記録
  • 問題が発生したときに、どのバージョンのロジックが原因かをすぐに特定できるようにする

まとめ:当面は「前処理ありき」で設計するのが現実的

本記事で見てきたように、Azure AI Document Intelligence には splitMode やモデル構成(model compose)など、複雑なドキュメント構造に対応する仕組みが用意されています。ただし、prebuilt-invoice単体で「1PDFに含まれる同一タイプの請求書を自動分割」する機能は、2025年時点でも提供されていません。

現実的な実装パターンは、次のように整理できます。

  • コスパ重視・短期導入なら: Layout/Read+自前の境界検出ロジックで請求書単位に分割 → prebuilt-invoice で解析
  • 精度追求・高度な自動化なら: カスタム分類+モデル構成+splitMode=auto+カスタム抽出モデルでDocument Intelligence内にロジックを寄せる(ただし運用コストが上がる)

どちらのルートを選ぶにしても、

  • スキャン品質の標準化
  • 境界検出のスコアリングと人手レビューの仕組み
  • ベンダー別ルールや軽量MLによる補強
  • ログ・バージョン管理・再処理のしやすさ

といった運用面の設計が成功の鍵になります。

「1PDFに複数の請求書が含まれる」という現場ではよくある前提を変えずに、Azure AI Document Intelligence を最大限活用するには、前処理のロジックをきちんと設計することが本質的な解決策です。まずは小さな範囲(特定ベンダー・特定部署)からパイロットを始め、ロジックと運用の両面を磨き込んでいくことをおすすめします。

この記事を書いた人

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

コメント

コメントする

目次