保険約款PDFをMarkdown化するために、PDF分割・暗号化解除・AIでの全文抽出を試す
自動車保険の保険証券PDFをMarkdown化できたので、次は普通保険約款・特約PDFもMarkdown化します。
保険証券は数ページで、契約内容の一覧表に近い文書でした。
一方、保険約款はページ数が多く、条文・特約・免責条件などが大量に含まれます。
そのため、保険証券と同じプロンプトで一括変換するのではなく、PDFを小さく分割し、1ファイルずつMarkdown化する方針にしました。
この記事では、保険約款PDFをMarkdown化するための技術的な手順のみを扱います。実際の約款本文、契約者情報、証券番号、住所、電話番号などは掲載しません。また、変換後のMarkdown全文も公開せず、ローカル環境でRAG用データとして利用します。
- 保険証券用プロンプトは約款PDFには向かない
- 約款PDFは分割して処理する
- 章ごとに分割する必要はない
- 最終的な処理方針
- 注意:個人情報を含むPDFは扱いに注意する
- Popplerをインストールする
- 最初のPDF分割スクリプト
- 原因:PDFが暗号化扱いだった
- qpdfをインストールする
- 復号PDFを対象にして再分割する
- 10ページでは文字量が多すぎた
- 10ページ分割結果を退避する
- 3ページごとに分割する
- 約款全文Markdown化用プロンプト
- AIでMarkdown化を試す
- Gemini CLIのクォータエラー
- Web版AIで手動処理する方法
- 途中で出力が止まった場合
- 当面の運用
- 今回分かったこと
- ハマりどころ
- 公開時の注意
- 次にやること
- まとめ
保険証券用プロンプトは約款PDFには向かない
保険証券は、以下のような特徴があります。
- 数ページ程度
- 表形式が中心
- 証券番号、保険期間、補償内容など項目が決まっている
- 構造化しやすい
そのため、「PDF全体を読み取って、決まった形式でMarkdownにする」という方法が成立しました。
しかし、保険約款は違います。
- ページ数が多い
- 条文が長い
- 特約が多い
- 画像PDFの場合、OCR負荷が大きい
- 全文抽出ではトークン上限に引っかかりやすい
そのままAIに投げると、途中で省略されたり、出力が切れたり、「以下略」のような文言が混ざる可能性があります。
約款PDFは分割して処理する
約款PDFのMarkdown化では、以下の方針にします。
- PDFを小さく分割する
- 1回あたりの出力量を制限する
- 構造はできるだけ維持する
- 要約は禁止する
- 原文に存在しない説明は追加しない
最終的には、3ページ程度ずつ分割してMarkdown化する方針にしました。
章ごとに分割する必要はない
最初は、約款を章ごとに分割した方がよいのではないかと考えました。
しかし、RAGで使うことを考えると、章ごとに分割する必要はありません。
重要なのは章ではなく、以下です。
- 条文そのもの
- 条文番号
- 支払条件
- 免責条件
- 特約名
- 参照関係
RAG検索では、「第3章」などの章タイトルよりも、「人身傷害」「通院」「後遺障害」「支払われない場合」などの本文中の意味情報の方が重要です。
そのため、PDF分割は意味構造のためではなく、AI処理の物理制限を避けるために行います。
最終的な処理方針
今回の処理方針は以下です。
- 元PDFをバックアップする
- 暗号化されている場合は復号する
- PDFを3ページごとに分割する
- 分割PDFごとにMarkdown化する
- 条文番号を保持する
- RAG投入時に条文単位でチャンク化する
この流れなら、ページ数の多い約款PDFでも少しずつ処理できます。
注意:個人情報を含むPDFは扱いに注意する
保険証券や契約内容を含むPDFには、氏名、住所、電話番号、証券番号、車両情報などの個人情報が含まれる場合があります。
外部AIサービスへアップロードする場合は、個人情報や契約者情報が含まれていないか確認し、必要に応じてマスキングしたうえで利用します。
今回の記事では、実際の契約情報を含むPDF名や本文は掲載せず、汎用的なファイル名に置き換えています。
Popplerをインストールする
PDF分割にはPopplerを使います。
MacではHomebrewでインストールできます。
brew install poppler
すでにインストール済みの場合は、以下のように表示されます。
Warning: poppler 26.01.0 is already installed and up-to-date.
pdfseparate のバージョンを確認します。
pdfseparate -v
実行例は以下です。
pdfseparate version 26.01.0
最初のPDF分割スクリプト
まず、10ページずつ分割するスクリプトを作成しました。
vim split_pdf.sh
内容は以下です。
#!/usr/bin/env bash
set -e
INPUT="auto_insurance_policy_terms.pdf"
PAGES_PER_FILE=10
mkdir -p backup pages out
# 元PDFをバックアップ
cp "$INPUT" backup/
# 1ページずつ分割
pdfseparate "$INPUT" pages/page_%03d.pdf
# まとめ直し
count=1
files=()
for f in pages/page_*.pdf; do
files+=("$f")
if (( ${#files[@]} == PAGES_PER_FILE )); then
printf -v num "%03d" "$count"
pdfunite "${files[@]}" "out/policy_${num}.pdf"
files=()
((count++))
fi
done
# 余りページ
if (( ${#files[@]} > 0 )); then
printf -v num "%03d" "$count"
pdfunite "${files[@]}" "out/policy_${num}.pdf"
fi
ここでは、実際の保険会社名や商品名が入ったPDFファイル名は使わず、公開用に auto_insurance_policy_terms.pdf という汎用名にしています。
実行権限を付けます。
chmod +x split_pdf.sh
実行します。
./split_pdf.sh
しかし、以下のエラーが出ました。
Unimplemented Feature: Could not merge encrypted files ('pages/page_001.pdf')
原因:PDFが暗号化扱いだった
エラーを見ると、分割後のPDFが暗号化ファイルとして扱われていました。
元PDFの状態を確認します。
pdfinfo "auto_insurance_policy_terms.pdf" | grep -i encrypted
結果は以下です。
Encrypted: yes (print:yes copy:yes change:yes addNotes:yes algorithm:RC4)
Encrypted: yes になっているため、PDFは暗号化扱いです。
パスワードが不要なPDFでも、内部的に暗号化扱いになっていることがあります。
Popplerの pdfunite は暗号化PDFの結合に対応していないため、先に復号する必要があります。
qpdfをインストールする
PDFを復号するため、qpdf を使います。
brew install qpdf
インストール後、以下のコマンドで復号します。
qpdf --decrypt "auto_insurance_policy_terms.pdf" "policy_decrypted.pdf"
エラーが出なければ成功です。
復号できたか確認します。
pdfinfo "policy_decrypted.pdf" | grep -i encrypted
結果は以下です。
Encrypted: no
Encrypted: no になったので、復号できています。
復号PDFを対象にして再分割する
split_pdf.sh の対象ファイルを、復号済みPDFに変更します。
INPUT="policy_decrypted.pdf"
再度実行します。
./split_pdf.sh
今度はエラーなく終了しました。
出力先の out フォルダに、分割されたPDFが作成されます。
10ページでは文字量が多すぎた
10ページごとに分割できましたが、保険約款は文字量が多く、Markdown化するにはまだ重そうでした。
画像PDFの場合、OCR負荷や入力トークン量が大きくなります。
そこで、10ページ単位ではなく、3ページ単位に減らすことにしました。
10ページ分割結果を退避する
まず、すでに作成した10ページ単位の出力を退避します。
TS=$(date +"%Y%m%d_%H%M%S") mv out "out_10p_$TS" mv pages "pages_10p_$TS"
TS には現在日時が YYYYMMDD_HHMMSS 形式で入ります。
これにより、既存の out と pages を日時付きフォルダとして退避できます。
3ページごとに分割する
split_pdf.sh を編集し、分割ページ数を3に変更します。
PAGES_PER_FILE=3
再度実行します。
./split_pdf.sh
作成されたPDFのページ数を確認します。
pdfinfo out/policy_001.pdf | grep Pages
結果は以下です。
Pages: 3
これで、3ページごとの分割PDFが作成できました。
約款全文Markdown化用プロンプト
次に、約款PDFをMarkdown化するためのプロンプトを作成します。
vim prompt_policy_fulltext.txt
内容は以下です。
あなたは文書アーカイブ作成専用アシスタントです。 以下のPDFは保険約款(画像PDF)です。 目的は「全文を欠落なくMarkdown化すること」です。 【最重要ルール】 - 要約は禁止 - 表現の言い換え禁止 - 文言の統合・省略禁止 - 解釈・補足・説明の追加禁止 - 原文に存在する文章のみを使用すること 【出力制御】 - 今回はPDF全体ではなく「このPDFファイルに含まれる範囲のみ」を処理してください - トークン上限に達しそうな場合は、文章を途中で省略せず、 必ず次の行のみを出力して終了してください ---CONTINUE--- 【Markdownルール】 - 見出し(章・節・条)をMarkdown見出しに変換 - 第○条/第○項/第○号 の階層を保持 - 箇条書きはMarkdown形式に変換 - ページ番号や脚注はそのまま記載 【出力形式】 - Markdown本文のみを出力してください - 前後の説明文・注意文・挨拶文は禁止です
ポイントは、要約や補足を禁止し、原文の保持を最優先にしていることです。
ただし、変換後の約款全文Markdownは公開せず、ローカル環境でRAG用データとして利用します。
AIでMarkdown化を試す
出力先ディレクトリを作成します。
mkdir -p md
まず、分割した policy_001.pdf をAIで処理します。
Gemini CLIなどのコマンドラインツールを使う場合は、以下のような形で実行します。
gemini -y " 次の指示書を必ず最初に読み、その内容に厳密に従ってください。 指示書ファイル: - prompt_policy_fulltext.txt 次のPDFファイルを処理してください。 - out/policy_001.pdf 指示書のルールに従い、 Markdown本文のみを出力してください。 " > md/policy_001.md
このように標準出力をリダイレクトすれば、結果を md/policy_001.md に保存できます。
Gemini CLIのクォータエラー
大量のPDFを処理する場合、APIの無料枠や利用上限に引っかかることがあります。
今回も、以下のようなエラーが出ました。
TerminalQuotaError: You have exhausted your daily quota on this model.
エラーの要点は以下です。
Quota exceeded for metric: generate_content_free_tier_input_token_count Quota exceeded for metric: generate_content_free_tier_requests
これはプロンプトやPDF分割の問題ではなく、API側の無料枠クォータを使い切ったことが原因です。
この場合、少し待ってもすぐには回復しないことがあります。翌日以降に再実行するか、処理対象をさらに小さくするか、別の処理方法を検討します。
Web版AIで手動処理する方法
コマンドラインでの処理が難しい場合は、Web版のAIサービスに分割PDFをアップロードしてMarkdown化する方法もあります。
その場合も、個人情報や契約者情報を含むPDFはアップロードしない、または必要に応じてマスキングしてから使います。
プロンプトは以下のような形にします。
あなたは文書アーカイブ作成専用アシスタントです。 このPDFは保険約款の一部(約3ページ)です。 目的は「全文を欠落なくMarkdown化すること」です。 【ルール】 - 要約しない - 言い換えしない - 解釈・補足を加えない - 原文に存在する文章のみを使用する - 文の順序を変更しない 【出力】 - 見出し(章・条)はMarkdown見出しに変換 - 第○条/第○項/第○号の構造を保持 - 箇条書きはMarkdownに変換 - ページ番号があれば残す Markdown本文のみを出力してください。 説明文は禁止です。
出力されたMarkdownを、手元でファイルに保存します。
cd md vim policy_001.md
途中で出力が止まった場合
Web版AIで処理する場合、出力が途中で止まることがあります。
その場合は、出力されたところまでコピーして保存し、続きが必要な場合は以下のように聞きます。
この続きの文章は?
続きが表示されたら、それをさらにコピーしてMarkdownファイルへ追記します。
この方法は手作業ですが、約款の中身を確認しながら進められるため、RAG用データの品質確認にもなります。
当面の運用
当面は、以下の手順で進めます。
- 約款PDFを3ページごとに分割する
- 分割PDFをAIに読み込ませる
- 全文Markdown化プロンプトを実行する
- 途中で止まった場合は続きを出してもらう
- Markdownを
md/policy_001.mdのように保存する - 内容を確認しながら次のPDFへ進む
手作業は残りますが、約款の中身を確認しながら進められるため、変換結果の品質確認には向いています。
今回分かったこと
今回の作業で分かったことは以下です。
- 保険証券用プロンプトは、約款のような大容量PDFには向かない
- 約款PDFは3ページ程度に分割した方が処理しやすい
- 章ごとではなくページ数で分割してよい
- RAGでは最終的に条文単位でチャンク化するのが重要
- 暗号化PDFは
pdfuniteで結合できないことがある qpdf --decryptで復号してから分割するとよい- AI APIには無料枠や利用上限がある
- Web版AIで手動抽出する方法も使える
- 画像PDFはOCR品質の確認が重要
ハマりどころ
pdfuniteが暗号化PDFを結合できない
Popplerの pdfunite は、暗号化PDFの結合に対応していない場合があります。
以下のようなエラーが出た場合は、暗号化PDFが原因です。
Unimplemented Feature: Could not merge encrypted files
pdfinfo で確認し、Encrypted: yes なら qpdf --decrypt を使います。
10ページ分割でも大きすぎる場合がある
画像PDFの約款は、1ページあたりの文字量が多いです。
10ページ単位ではまだ重く、途中で止まる可能性があります。
今回は3ページ単位にしました。
AI APIの無料枠に注意
CLIツール経由で大量のPDFを処理すると、無料枠のクォータに引っかかることがあります。
特に画像PDFは入力トークン量が大きくなりやすいため、短時間で上限に達しやすいです。
Markdownファイル保存は手作業になる場合がある
Web版AIを使う場合、出力を直接ファイル保存する仕組みがない場合があります。
その場合は、出力をコピーし、vim などで md/policy_001.md に保存します。
公開時の注意
この記事では、PDF分割やMarkdown化の技術手順のみを公開します。
以下は公開しない方針です。
- 実際の約款本文全文
- 変換後のMarkdown全文
- 契約者情報
- 証券番号
- 住所・電話番号
- 車両情報など個人に紐づく情報
- 実際のPDFファイル名
技術手順とサンプルコマンドだけを公開し、実データはローカル環境で扱います。
次にやること
次は、分割PDFごとにMarkdown化を進めていきます。
policy_001.pdfから順番にMarkdown化するmd/policy_001.mdのように保存する- 途中で止まった場合は続きを取得する
- 条文番号が欠落していないか確認する
- Markdownを結合する
- RAG投入用に条文単位でチャンク化する
保険証券と約款Markdownを組み合わせることで、「自分の契約で使える補償」と「約款上の支払条件・免責条件」をつなげられるようにしたいです。
まとめ
保険約款PDFをMarkdown化するために、PopplerによるPDF分割、qpdfによる暗号化解除、AIでのMarkdown抽出を試しました。
保険約款のような大容量PDFは、一括処理ではなく3ページ程度に分割して処理する方が安定しそうです。
また、PDFが暗号化扱いになっている場合は、pdfunite で結合できないことがあるため、先に qpdf --decrypt で復号する必要があります。
CLI経由ではAPIの無料枠に引っかかる場合があるため、必要に応じてWeb版AIで手動抽出する方法も併用します。
当面はこの方法で約款本文をMarkdown化し、最終的には条文単位でRAGに投入できる形へ整理していきます。

コメント