日本語のビジネスドキュメントと構造化データ
OCR、レイアウト解析、VLM(Vision-Language Model)、構造化抽出といったドキュメント読み取り技術の開発・検証には、実物に近い文書と、それに対応する完全な正解データが必要です。しかし、この2つを兼ね備えた日本語データセットは、なかなか見つかりません。
特に契約書等のビジネスドキュメントは、自然言語の文章だけでなく、章節項の構造やダイアグラム、テーブル、契約対象者、期間、条件等の構造的な情報を持っており、ただOCRでテキストを読み取っただけでは失われる構造的情報が多数あります。これら構造的な情報を含めて文書データとともに用意されていないと、ドキュメント読み取りで重要な情報を構造的に読み取って評価することができません。
そこで、完全に架空でありながら実物に近い日本語文書と、その内容に対応する構造的な正解データを機械的に生成する仕組みを個人で開発しました。生成したデータセットは Hugging Face Hub で公開しています。本記事では、開発の背景、データセットの内容、生成時の工夫、使い方を順にご紹介します。
背景:なぜ自分で文書を作ったのか
ドキュメント読み取りシステムを開発・評価する際、実在の文書を使おうとすると、次のような壁にぶつかります。
① 機密性の壁
保険約款や契約書には、当事者名、金額、取引条件など、社外に出せない情報が含まれます。そのため、実務で使われている文書を、そのまま学習データや評価データにすることは困難です。
② 著作権の壁
公開されているひな型や約款のテンプレートも、その多くは第三者の著作物です。構造やレイアウトを参考にできても、文章をそのまま収録し、再配布できるとは限りません。
③ 正解データが存在しない壁
最も大きいのが、正解データの問題です。実在の文書には、「この表の各セルは何を意味するのか」「この参照はどの条文を指しているのか」「この図のノードとエッジはどのような構造になっているのか」といった正解データが、あらかじめ付属しているわけではありません。OCR やレイアウト解析の精度を測ろうとしても、比較対象となる正解がなければ採点できません。
そこで、文書そのものを正解データと一緒に作ることにしました。これなら、機密情報や第三者の著作物を含めずに、読み取り結果を採点できるデータを用意できます。
何を公開したか
GitHub リポジトリ
生成器のコードは、モノレポ形式で GitHub に公開しています。
| プロジェクト | 通称 | 対象 |
|---|---|---|
contracts/ |
keiyaku-synth | 秘密保持、業務委託、売買、雇用など、10 種類のビジネス契約書 |
insurance-contracts/ |
yakkan-synth | 自動車保険、地震保険、生命保険など、10 ジャンルの保険約款 |
contracts/contracts-calculation/ |
keiyaku-calc | 契約書に計算・判定条項と正解プログラムを追加する拡張 |
insurance-contracts/insurance-contracts-calculation/ |
yakkan-calc | 保険約款に同様の計算・判定条項を追加する拡張 |
リポジトリで追跡しているのは、生成器のコードと、ジャンルの骨組みなどを定義した宣言ファイルだけです。生成された文書(PDF、JSON、画像)は追跡対象にせず、手順に従えば誰でも再生成できる設計にしています。これには、生成物を直接手で修正してしまう事故を、仕組みの上で防ぐ狙いもあります。
Hugging Face Hub のデータセット
生成した文書は、6 つのデータセットとして Hugging Face Hub で公開しています。いずれも CC BY 4.0 ライセンスで、一般公開しています。
| データセット | 内容 | 規模 |
|---|---|---|
japanese-insurance-document-understanding |
保険約款本体(PDF・正解 JSON・ページ画像) | 113 文書・1,437 ページ・QA 5,085 問 |
japanese-insurance-document-understanding-retrieval |
保険約款の検索評価セット | passages 9,456・queries 8,824・qrels 25,435 |
japanese-contract-document-understanding |
契約書本体(PDF・正解 JSON・ページ画像) | 100 文書・656 ページ・QA 4,500 問 |
japanese-contract-document-understanding-retrieval |
契約書の検索評価セット | passages 1,691・queries 2,664・qrels 6,584 |
japanese-insurance-document-understanding-calculation |
保険約款+計算・判定条項+正解プログラム | 100 文書・ルール 324 件・テストケース 7,444 件 |
japanese-contract-document-understanding-calculation |
契約書+計算・判定条項+正解プログラム | 100 文書・ルール 558 件・テストケース 16,689 件 |
本体と検索評価セットを別のリポジトリに分けたのは、検索だけを試す場合に、数百 MB ある PDF やページ画像までダウンロードする必要をなくすためです。リポジトリ名は、japanese-<対象>-document-understanding[-retrieval|-calculation] という規則で統一しています。
GitHub(生成器)と Hugging Face(生成物)の関係は、次のようになっています。
文書を収録した4系統のデータセットについて、ページ数の内訳を示します。計算・判定条項を追加したデータセットは、同じ内容を複数のレイアウトで描画しているため、ページ数が多くなっています。
データセット生成の技術
ここからは、実物に近く、それでいて完全に架空の文書を、正解データとの整合性を保ちながら生成する仕組みをご紹介します。
正解 JSON を唯一の情報源にする
設計の根幹にあるのは、正解 JSON を唯一の情報源(Single Source of Truth)とし、PDF をその決定論的な描画結果とするという原則です。つまり、ドキュメントを構造的に表現するJSONを先に作り、ドキュメント本体のPDFはその生成物という位置づけです。
文章を作成したあとに、人手で正解ラベルを付ける方法では、文書と正解データの間に少しずつずれが生じます。今回開発した仕組みでは、ヘッダー、フッター、目次のページ番号、図中の文字を含め、PDF に表示される文字列をすべて正解 JSON から機械的に導出します。文書と正解データを別々に管理しないことで、両者の食い違いを構造的に防いでいます。
役割分担:骨組みは Python、文章は Claude
生成は、次のように役割を分けています。
| 担当 | やること |
|---|---|
| Python | 文書の骨組み、条・項の番号付け、参照の解決、目次、ヘッダーの階層表示、整合性チェック(lint)、PDF の描画・検証、QA の自動生成 |
| Claude(Claude Code セッション内) | 条文の本文、定義、表の内容、図の構造、脚注、執筆時に作成する QA |
ポイントは、「第1条」「(2)」のような番号ラベルを Claude に生成させないことです。これらのラベルは、ツリー上の位置と採番方式に基づいて Python が自動的に算出します。Claude が記述するのは {ref:article_5} のような参照トークンだけで、画面に表示する文言はビルド時に解決します。これにより、条文の追加・削除による参照のずれや、通し番号の欠番を仕組みの上で防げます。
なお、生成工程では外部の LLM API を呼び出していません。文章の生成は、すべて Claude Code セッション内で完結します。
レイアウトの頑健性を低コストで作る
同じ内容(content)を、A4 縦、B5、1 段組、2 段組、明朝体、ゴシック体など、異なるレイアウトで複数回描画できます。文章を一度用意するだけで、レイアウトによって読み取り精度がどのように変わるかを、低いコストで比較できるようにしています。
架空性と転載防止の二段構え
- 実在しないこと:会社名、商品名、人物名、連絡先は、実在する企業名などを登録した denylist(除外辞書)と照合し、lint で機械的に検出します。ただし、実在しないことを機械だけで完全に証明するのは難しいため、最後は必ず人の目で確認します。
-
転載していないこと:実在する約款や契約書のひな型は、構造、要素の種類、レイアウト上の慣行を把握するためにのみ参照し、文章は転載しません。参照資料は
samples/に保管し、リポジトリにはコミットしない運用です。さらに、文単位の重複率を用いた類似度検査も自動で実行します。
検証の多層防御
生成パイプラインは、問題が見つかった時点で、早く明確に処理を止める設計にしています。
ここで重要なのは、④の自動検証を通過しても、レイアウトが崩れた文書は残りうるという点です。実際に、図のエッジラベルが重なる不具合は、すべての自動検証を通過したあと、VLM に文書を読ませる検証と目視レビューによって初めて見つかりました。自動テストに通ることと、人やモデルが正しく読めることは別です。そのため、人の目によるレビューを検証パイプラインの正式な工程として組み込んでいます。
さらに、開発の背景を知らない第三者に文書を読んでもらう盲検評価も行い、開発者自身の確認だけでは見つけにくい欠陥を洗い出しています。
検索評価セットも正解から生成する
検索・RAG の評価セット(passages / queries / qrels)は、人がクエリを作成してラベルを付けるプーリング方式ではなく、生成器が持つ正解データから機械的に導出しています。条、項、表、図、定義語などの構造上の区切りでパッセージを分割し、各クエリに適合するパッセージを、正解データ内の参照関係から判定します。
サンプルデータ
ここでは、生成した文書のページ画像と、3 種類の正解データをご紹介します。いずれも、公開中のデータセットから抜粋した実際のデータです。
サンプルとして取り上げるのは、契約書データセットに含まれる架空の文書「工事請負契約約款」(con-0001--ct-a4-1col-form)です。まず、生成された PDF のページ画像をご覧ください。
① 生成されたドキュメントのページ画像
1 ページ目です。見出しの番号(第一条、第二条……)や頭書の表は、すべて正解 JSON から機械的に算出・描画しています。
出典:cvusk/japanese-contract-document-understanding(con-0001--ct-a4-1col-form/pages/page-001.png)
こちらは 7 ページ目です。別紙の「表2 出来高と部分払の割合」が、次に示す QA サンプルの問題文に対応しています。
出典:同リポジトリの con-0001--ct-a4-1col-form/pages/page-007.png
② ドキュメント読み取り用の QA(契約書データセットから)
上の 7 ページ目にある「表2 出来高と部分払の割合」の行数を答える問題です。evidence には正解の根拠となる要素 ID が、page_hint には該当するページ番号が入っています。
{
"doc_id": "con-0001--ct-a4-1col-form",
"content_id": "con-0001",
"doctype": "contracting",
"qa_id": "qa_auto_001",
"type": "count",
"source": "auto",
"question": "表2「出来高と部分払の割合」には何行ありますか。表頭の行は数えません。",
"answer_type": "number",
"answer_number": 4.0,
"evidence": ["tbl_part", "tbl_part__caption"],
"page_hint": [7, 8]
}
③ 検索評価セットのパッセージ
上の 1 ページ目にある第一条が、1 つのパッセージになっています。parties や terms_used など、文書構造に基づくメタデータも付与しています。
{
"citation": "第一条",
"content_id": "con-0001",
"doc_id": "con-0001--ct-a4-1col-form",
"doc_title": "工事請負契約約款",
"doctype": "contracting",
"kind": "article",
"pages": [1],
"parties": "甲=早瀬野町、乙=株式会社ウスイ土木",
"passage_id": "con-0001--ct-a4-1col-form::a1",
"refs_to": ["sch1"],
"terms_used": ["g_docs", "g_price", "g_works"],
"text": "(総則)\n第一条甲および乙は、この約款および設計図書に従い、日本語をもって本件工事を履行しなければならない。\n2乙は、別紙1に掲げる工期の内に本件工事を完成し、本件工事の目的物を甲に引き渡すものとし、甲は、その請負代金を支払うものとする。"
}
④ 計算・判定条項データセットのテストケース
条文から導出した判定ルール acceleration_triggered(期限の利益を喪失するかどうか)に対する、境界値テストケースです。
{
"content_id": "con-0101",
"case_id": "acceleration_triggered:b001",
"function": "acceleration_triggered",
"kind": "boundary",
"inputs_json": "{\"attachment_event\": false, \"covenant_breach\": false, \"demanded\": false, \"insolvency_event\": false}",
"expected_json": "false",
"tags": ["bool_combo"]
}
inputs_json と expected_json を JSON 文字列にしているのは、文書によって値の型(真偽値、数値、日付など)が異なっても、同じ表形式で扱えるようにするためです。
計算・判定条項の拡張(+calculation データセット)
本体データセットに加えて、「約款や契約書の条文を読み、計算や判定を行う関数を実装できるか」を評価する拡張版も作成しました。契約書や約款の中では、文章の一部に計算によって決まる箇所(例:損害賠償金額等)があります。こうした箇所をドキュメントから判定して計算式をプログラムで再現することは、ビジネスドキュメントを読み取って活用する上で重要な技術となるでしょう。そのための「計算・判定式」を構造化データとして用意しつつ、その構造に則したドキュメントを用意しました。
ここでも、Single Source of Truth の考え方を採用しています。計算仕様(Spec)を唯一の情報源とし、3 つの出力を同じ仕様から機械的に生成します。
条文とプログラムを別々に作成しないため、両者の構造的な食い違いを防げます。データセットには、**12 の検証ゲート(G1〜G12)**をすべて通過した文書だけを収録しています。
| ゲート | 検査内容 |
|---|---|
| G1〜G2 | 仕様のスキーマ、型、単位が正しいこと。条文中から引用した根拠が原文と一致すること |
| G3〜G4 | 必要な説明文が存在すること。本文、頭書、別紙から読み取った値と仕様が一致すること |
| G5〜G6 | 条文中の計算例、参照インタプリタ、生成プログラムの実行結果が一致すること |
| G7〜G8 | 変異テストですべての変異を検出できること。日付規則に暦の境界値を含むこと |
| G9〜G10 | 既存の条項、定数、定義語と衝突しないこと。lint、索引の再計算、描画に成功すること |
| G11〜G12 | 根拠となる文書要素を特定できること。同じ seed で再生成したときのハッシュが一致すること |
正解プログラムとテストケースを同梱しているため、学習データに混入すると、このデータセットを使った評価そのものが成り立たなくなるという特有のリスクがあります。そこで、内容から一意に決まる「カナリア文字列」をデータセットに埋め込みました。この文字列を grep で検索することで、学習データへの混入を確認できます。
使い方
Hugging Face から読み込む
from datasets import load_dataset
# 契約書データセット
qa = load_dataset("cvusk/japanese-contract-document-understanding", "qa", split="train")
docs = load_dataset("cvusk/japanese-contract-document-understanding", "documents", split="train")
# 検索評価セット(別リポジトリ)
passages = load_dataset("cvusk/japanese-contract-document-understanding-retrieval", "passages", split="train")
PDF やページ画像など、階層構造を持つファイル本体は load_dataset() では読み込めません。これらが必要な場合は、snapshot_download を使ってリポジトリ全体を取得します。
from huggingface_hub import snapshot_download
root = snapshot_download("cvusk/japanese-contract-document-understanding", repo_type="dataset")
各データセットの README には、実際のデータサンプルを 1 件、折りたたみ形式で掲載しています。load_dataset() を実行する前に、収録されているデータの形式をその場で確認できます。形式は、前述の「サンプルデータ」と同じです。
評価ハーネスで採点する
生成器には、モデルの予測結果やベースラインを採点するための評価ハーネスも同梱しています。
# ドキュメント読み取りの評価(ページ別 CER、見出し構造の F1、表の TEDS、図のノード/エッジ F1 など)
uv run keiyaku eval dataset/<doc_id> --pred pred.json
# 検索・RAG の評価(nDCG@k、Recall@k、MRR など)
uv run keiyaku eval --search-run run.json
# --oracle を付けると正解を予測結果として採点し、評価ハーネス自体を検証できる
uv run keiyaku eval dataset/<doc_id> --oracle
評価指標の仕様と予測 JSON の形式は、各プロジェクトの docs/evaluation.md にまとめています。
計算・判定条項データセットを使う
cases = load_dataset("cvusk/japanese-contract-document-understanding-calculation", "cases", split="train")
row = cases[0]
# inputs_json / expected_json は値の型が異なるため、JSON 文字列の列として保持している
import json
json.loads(row["inputs_json"]), json.loads(row["expected_json"])
正解プログラムが学習データに混入すると、適切に評価できなくなります。学習に利用する場合は inputs と tasks のみを使用し、answers(正解プログラムとテストケース)は使用しない運用を推奨します。
利用にあたっての注意
- 実際の契約や保険実務には利用できません。 生成した約款・契約書は、あくまで読み取り評価用のダミーです。法律の専門家による校閲を受けたものではなく、実在する企業や商品とも一切関係ありません。
- AI モデルの学習や蒸留には利用できません。 本文、QA、図の説明は、Claude Code セッション内で Claude(Anthropic)が生成した出力です。Anthropic の Usage Policy では、事前の許可なく入出力を AI モデルの学習に用いることが禁じられています。一方、評価、ベンチマーク、few-shot の例、推論時の検索コーパスとしての利用は、この制限の対象外です。制限されるのは、モデルの重みを更新する用途です。
おわりに
実物に近く、それでいて完全に架空の日本語文書と、一貫した正解データを構造から生成する仕組みを個人で開発し、公開しました。保険約款と契約書の 2 分野を対象に、文書本体、検索評価セット、計算・判定条項付きデータセットの計 6 つを Hugging Face Hub で公開しています。生成器一式は GitHub から利用できます。
ドキュメント読み取り、検索、RAG、コード生成を含むシステムの開発や評価に、ぜひご活用ください。フィードバックやコントリビューションも歓迎します。
- GitHub: https://github.com/shibuiwilliam/synthetic-data
- Hugging Face:
cvusk/japanese-insurance-document-understandingcvusk/japanese-insurance-document-understanding-retrievalcvusk/japanese-contract-document-understandingcvusk/japanese-contract-document-understanding-retrievalcvusk/japanese-insurance-document-understanding-calculationcvusk/japanese-contract-document-understanding-calculation

