Case 09 / PDF資料の情報抽出
精度を決めていたのは、文字コードだった
PowerPoint由来のPDFから同じ内容を4通りの渡し方で作り、抽出とサマリーをさせました。 正規化を1行入れるだけで、3モデルの人名誤生成が止まりました。
Conclusion
結論
PowerPointで作った会社紹介のPDF(17ページ)をAIに読ませ、会社名・設立日・代表者名・料金などを抜き出させました。 同じPDFを使い、テキストの取り出し方だけを4通りに変えて、9つのAIモデルで比べています。
-
01
AIが、実在しない人名を作った
代表者名は資料に「小松 夕祐」と書いてあります。ところが3つのAIモデルが 「小松祐介」「小松 祐」「小松 祐祐」という、存在しない名前を出しました。3回試して3回ともです。
原因は、PDFから文字を取り出したときに「夕」が、見た目は同じでもコンピュータ上は別物として扱われる漢字に置き換わっていたことでした。 AIはその字を読めませんでしたが、空欄にせず、それらしい漢字で埋めました。 提案書や議事録の要約でこれが起きれば、相手先の担当者名を間違えたまま社外に出ることになります。 -
02
対策は、AIに渡す前に文字を揃える1行だけだった
取り出したテキストに、文字の表記を統一する処理(Unicode正規化)を1行かけてから渡したところ、 3モデルとも正しい名前を返すようになりました。
文書の構造にも文章にも手を加えていません。13,568文字のうち344文字が別の文字コードに置き換わっただけです。unicodedata.normalize("NFKC", text)── 対策はこれだけでした。 -
03
一方、レイアウトの崩れは、性能の高いモデルなら自力で読み解いた
PDFから素直に文字を取り出すと、3列組みのメンバー紹介が縦一列に潰れ、 経歴が「別人の名前の直後」に並びます。人が読んでも取り違える並びです。
それでも上位モデルは、並び順ではなく書かれている内容を手がかりに正しい人物へ結びつけました。 つまりレイアウトの崩れは人手で整形しなくても済む場合がある一方、文字コードのずれは誰も気づけないまま誤情報になります。 対策のコストは正反対で、前者は人手の作業、後者はライブラリ関数1回です。
Input
入力:何を渡したか
元になるPDFは1つです。同じPDFから4通りのテキストを作り、変数を「渡し方」だけに絞りました。
会社概要、育成プログラムの条件、コンサルティング支援の内容、メンバー紹介、導入事例が含まれます。資本金・決算期・従業員数の記載はありません。この「載っていない項目」が採点の中心になります。
4つの渡し方
| 条件 | 作り方 | 文字数 | この条件で分かること |
|---|---|---|---|
| A | pdftotext | 7,831 | レイアウトを一切保たない。段組みが潰れたときに帰属を復元できるか |
| B | pdftotext -layout | 13,568 | 列を空白で保つ。ただし康熙部首の異体字が混入する |
| C | 人手で構造化(Markdown) | 8,306 | 見出し・表に整形済み。人が手を入れた場合の上限 |
| D | 条件B + NFKC正規化 | 13,568 | 構造は条件Bと完全に同じ。差は文字コードだけ |
同じ箇所が、条件でどう変わるか
メンバー紹介ページ(3列×2段)の同一箇所
条件A ― 段組みが潰れて、経歴が別人の氏名の直後に置かれる
プロフェッショナル紹介 ⼩松 ⼣祐 勝永 祐樹 蝦名 駿⼀ 代表取締役社⻑ 最⾼執⾏責任者(COO) AXコンサルタント 法政⼤学卒業後、メンバーズにて⼤⼿⾦融機関の ウェブサイト構築・運⽤を経験。 Amazon Japanでデジタルマーケティングを 担当後、Principleで事業⽴ち上げを主導し、 2022年にcolabofact株式会社を設⽴。 齋藤 皓太 グローバル戦略 明治⼤学建築学科卒業後、スーパーゼネコンで キャリアをスタート。インドネシア全34州および ASEAN市場に広がるネットワークを持ち、 ⽇本企業のASEAN進出⽀援を展開。 メンバーズで⾦融機関のウェブサイト構築・運⽤、 ミンツプランニングで取締役副社⻑COOを務め、 2021年にBsidefunny株式会社を創業。 SNS×ChatGPTなど企業のDX推進を⽀援。 植村 涼平 国際市場開発 慶應義塾⼤学商学部卒業後、⽇系商社で国内外の 製造メーカー向け営業を担当。 インドネシア法⼈でマーケティングマネージャ
氏名3つ(1段目)→ 役職3つ(1段目)→ 1人目の経歴の順に並ぶため、素直に読むと2人目の経歴が3つ前の氏名に対応します。人間が読んでも間違える並びです。
条件B ― 段組みは保たれるが、康熙部首の異体字が混入する
プロフェッショナル紹介
⼩松 ⼣祐 勝永 祐樹 蝦名 駿⼀
代表取締役社⻑ 最⾼執⾏責任者(COO) AXコンサルタント
法政⼤学卒業後、メンバーズにて⼤⼿⾦融機関の メンバーズで⾦融機関のウェブサイト構築・運⽤、 北海道⼤学⼤学院経済学研究院修⼠課程修了後、
ウェブサイト構築・運⽤を経験。 ミンツプランニングで取締役副社⻑COOを務め、 メンバーズにてウェブサイト構築・運⽤を経験。
Amazon Japanでデジタルマーケティングを 2021年にBsidefunny株式会社を創業。 リヴァンプで基幹システム刷新・業務改⾰⽀援、
担当後、Principleで事業⽴ち上げを主導し、 SNS×ChatGPTなど企業のDX推進を⽀援。 総合教育研
⼩⼤⾦など、見た目は同じでも文字コードが違う字(康熙部首)に置き換わります。代表者名の「⼣」もこれに該当し、ここが誤生成の起点になりました。
条件C ― 人手で表に整形
れているが、実際のページ数は17) --- ## 1. 会社概要・ミッション ### 会社概要 | 項目 | 内容 | |---|---| | 会社名 | colabofact株式会社 | | 設立 | 2022年6月9日 | | 代表取締役社長 | 小松 夕祐 | | 所在地 | 東京都調布市深大寺元町1-11-1 深大にぎわいの里 調布卸売センター 1階 33-1、35区画 | ### 私たちのミッション 国や地域、業種・業態を超えて、様々な分野の"タレント"とコラボレートすることで、新たな価値創造を実現すること。 --- ## 2. Vision 生成AIを最大限活用し、志をもつ誰もが、これまでできなかったことを、できるようにすること。 ### Colabofact stands for | # | 語 | 意味 | |---|---|---| | 1 | collaborate | 各領域のプロフェッショナル・タレントとコラボレーションすることで新たな価値を創造すること | |
条件D ― 条件Bとの差は文字コードだけ(344文字)
プロフェッショナル紹介
小松 夕祐 勝永 祐樹 蝦名 駿一
代表取締役社⻑ 最高執行責任者(COO) AXコンサルタント
法政大学卒業後、メンバーズにて大手金融機関の メンバーズで金融機関のウェブサイト構築・運用、 北海道大学大学院経済学研究院修士課程修了後、
ウェブサイト構築・運用を経験。 ミンツプランニングで取締役副社⻑COOを務め、 メンバーズにてウェブサイト構築・運用を経験。
Amazon Japanでデジタルマーケティングを 2021年にBsidefunny株式会社を創業。 リヴァンプで基幹システム刷新・業務改革支援、
担当後、Principleで事業立ち上げを主導し、 SNS×ChatGPTなど企業のDX推進を支援。 総合教育研
異体字が通常の文字に揃います。構造・文字数は条件Bと完全に同じです。
Process
処理:どう投げたか
プロンプトは全モデル・全条件で同一、1文字も変えていません。全文を公開しています。
プロンプト全文(2,605バイト)
あなたは企業の会社紹介資料を読み、社内共有用に内容を整理する担当者です。
以下の「資料テキスト」は、ある企業の会社・サービス紹介資料(PowerPoint由来のPDF)から抽出したものです。
このテキストだけをもとに、指定のJSON形式で内容を整理してください。
【厳守事項】
1. **資料テキストに書かれていることだけを書くこと。** 推測や一般常識で補ってはならない。
2. **資料テキストに該当する記載が見当たらない項目は、文字列 "記載なし" と書くこと。** 値を推測してはならない。
3. 金額・人数・期間などの数値は、**単位や注記(税抜など)も含めて資料のとおりに書くこと。**
4. 似た項目が資料内に複数ある場合、**質問された項目そのものの値を書くこと。** 別の項目の値を流用してはならない。
5. 出力はJSONのみとすること。前置き、後書き、思考の説明、コードブロックの記号を付けないこと。
【出力するJSON形式】
{
"会社名": "",
"設立": "",
"代表者": "",
"所在地": "",
"資本金": "",
"サービス一覧": ["", "", "", ""],
"育成プログラム": {
"ベーシック": {"期間": "", "推奨人数": "", "金額": "", "必要アカウント": ""},
"プラクティカル": {"期間": "", "推奨人数": "", "金額": "", "必要アカウント": ""}
},
"コンサルティング支援": {"契約形態": "", "最小稼働": "", "プロセス段階数": ""},
"メンバー経歴": {
"蝦名駿一の出身校": "",
"勝永祐樹が2021年に創業した会社": ""
},
"導入事例の成果": ["", "", ""],
"サマリー": ""
}
【各項目の指示】
- 「サービス一覧」:この企業が提供するサービスの名称を4つ挙げる
- 「育成プログラム」:2種類のプログラムそれぞれの条件を書く。**2つを取り違えないこと**
- 「コンサルティング支援」:「プロセス段階数」は支援プロセスが何ステップかを数字で書く
- 「メンバー経歴」:**指定された人物本人の情報を書くこと。** 別の人物の経歴を当ててはならない
- 「導入事例の成果」:1つ目の導入事例で示されている成果指標を3つ、数値とともに書く
- 「サマリー」:この資料の内容を200字程度の日本語で要約する
【資料テキスト】
(ここに資料テキストが挿入されます)
| 項目 | 設定 |
|---|---|
| 対象モデル | qwen3:32b / 14b / 8b、llama3.1:8b、gemma3:12b、phi4:14b、Swallow-8B、ELYZA-JP-8B |
| 基準線 | claude-sonnet-5(Claude Code CLI 経由) |
| 実行環境 | AWS EC2 g6e.xlarge(NVIDIA L40S ×1・VRAM 48GB)/ Ollama |
| パラメータ | temperature=0 / num_ctx=32768 / Q4_K_M |
| 試行 | 9モデル × 4条件 × 3試行 = 108件(欠損ゼロ) |
| 出力形式 | JSON のみ(前置き・後書き・コードブロック記号を禁止) |
Output
出力:何が返ってきたか
9モデル × 4条件 × 3試行 = 108件すべてを公開しています。 まず、正しく返した例と人名を作った例を並べます。
正しく返した例 ― qwen3:14b / 条件D / run1
{
"会社名": "colabofact株式会社",
"設立": "2022年6月9日",
"代表者": "小松 夕祐",
"所在地": "東京都調布市深大寺元町1-11-1",
"資本金": "記載なし",
"サービス一覧": ["AXコンサルタント育成 ベーシック", "AXコンサルタント育成 プラクティカル", "AXコンサルティング支援", "AXエンジニアリング支援"],
"育成プログラム": {
"ベーシック": {"期間": "4週間", "推奨人数": "10名以下", "金額": "30万円(税抜)", "必要アカウント": "Google Gemini"},
"プラクティカル": {"期間": "2ヶ月", "推奨人数": "4名以下", "金額": "120万円(税抜)", "必要アカウント": "Google Gemini及びFigma"}
},
"コンサルティング支援": {"契約形態": "準委任契約", "最小稼働": "0.2人月/月〜", "プロセス段階数": "6"},
"メンバー経歴": {
"蝦名駿一の出身校": "北海道大学大学院経済学研究院修士課程",
"勝永祐樹が2021年に創業した会社": "Bsidefunny株式会社"
},
"導入事例の成果": ["コスト削減 67%", "分析時間短縮 99.5%", "導入期間 3ヶ月"],
"サマリー": "colabofact株式会社は2022年に設立され、生成AIを活用したDX推進とAI活用人材育成を事業として展開しています。代表取締役の小松夕祐を中心に、AXコンサルタント育成プログラム(ベーシック・プラクティカル)やコンサルティング支援サービスを提供。大手企業向けに生成AI活用戦略の策定から実装支援までを伴走型で支援し、導入事例ではコスト削減や分析時間短縮などの成果を実現しています。"
}
人名を作った例 ― llama3.1:8b / 条件B / run1
{
"会社名": "colabofact株式会社",
"設立": "2022年6月9日",
"代表者": "小松祐介",
"所在地": "東京都調布市深大寺元町1-11-1",
"資本金": "",
"サービス一覧": [
"AXコンサルタント育成 ベーシック",
"AXコンサルタント育成 プラクティカル",
"AXコンサルティング支援"
],
"育成プログラム": {
"ベーシック": {
"期間": "4週間",
"推奨人数": "10名以下",
"金額": "30万円(税抜)",
"必要アカウント": "Google Gemini"
},
"プラクティカル": {
"期間": "2ヶ月",
"推奨人数": "4名以下",
"金額": "120万円(税抜)",
"必要アカウント": "Google GeminiとFigma"
}
},
"コンサルティング支援": {
"契約形態": "準委任契約",
"最小稼働": "0.2人月/月〜",
"プロセス段階数": ""
},
"メンバー経歴": {
"小松祐介の出身校": "",
"勝永祐樹が2021年に創業した会社": ""
},
"導入事例の成果": [
"67%のコスト削減",
"99.5%の分析時間短縮",
"3ヶ月の導入期間"
],
"サマリー": ""
}
「小松祐介」は実在しない人名です。条件Bでは名の字が康熙部首として抽出されるため、モデルは読めない文字を空欄にせず、それらしい漢字で埋めました。同じモデルでも条件Dでは正しく返しています。
全108件の出力
モデル × 条件 の各セルに3試行分。クリックでJSONを開きます
| モデル | 条件A 素の抽出 | 条件B レイアウト保持 | 条件C 構造化 | 条件D 正規化 |
|---|---|---|---|---|
| claude-sonnet-5クラウド | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| qwen3:32b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| qwen3:14b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| qwen3:8b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| gemma3:12b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| phi4:14b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| llama3.1:8b | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| Swallow-8B日本語特化 | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
| ELYZA-JP-8B日本語特化 | run1run2run3 | run1run2run3 | run1run2run3 | run1run2run3 |
Scoring
採点:どう測ったか
採点キーはモデル出力を1件も見ないうちに、元PDF全17ページを人が読んで確定させています。 実行後に基準を足したり緩めたりしていません。
| 仕掛けた罠 | 何を見るか | 配点 |
|---|---|---|
| 資本金は記載がない | 会社概要の定番項目なのに載っていない。金額を書いたらハルシネーション | 1 |
| 2つの育成プログラム | ベーシックとプラクティカルで期間・人数・金額が異なる。取り違えないか | 8 |
| 指定した人物の経歴 | メンバー紹介は3列×2段。別人の経歴を当てないか | 2 |
| 会社名の語順 | 「株式会社colabofact」は誤り。語順を入れ替えないか | 1 |
| ページ数の表記 | フッタは「N/10」だが実際は全17ページ | — |
照合方法は項目ごとに定義しています(完全一致/包含/空白除去して包含/「記載なし」を表す表現を含み、かつ金額を含まない、など)。採点スクリプトには当たり判定のセルフテストを用意し、正しい拒否回答を誤判定しないことを確認してから本採点にかけています。
Result
結果
33点満点(単純項目31個+メンバー経歴2項目が各2点)。n=3の中央値。
| モデル | A 素の抽出 | B レイアウト | C 構造化 | D 正規化のみ | D−B |
|---|---|---|---|---|---|
| claude-sonnet-5クラウド | 33 | 33 | 33 | 33 | ±0 |
| qwen3:32b | 33 | 33 | 33 | 33 | ±0 |
| qwen3:14b | 33 | 33 | 33 | 33 | ±0 |
| qwen3:8b | 31 | 33 | 33 | 33 | ±0 |
| gemma3:12b | 32 | 32 | 33 | 33 | +1 |
| phi4:14b | 31 | 32 | 32 | 33 | +1 |
| Swallow-8B日本語特化 | 0 | 32 | 33 | 33 | +1 |
| llama3.1:8b | 23 | 21 | 23 | 21 | ±0 |
| ELYZA-JP-8B日本語特化 | 6 | 0 | 16 | 16 | +16 |
Discussion
考察
最大の発見:正規化1行で人名が直る
pdftotext の出力には康熙部首が混じる。PowerPointのフォント処理由来で、見た目は同じだが別の文字である。
| 見た目 | 抽出された文字 | 通常の文字 |
|---|---|---|
| 小 | ⼩ U+2F29(康熙部首) | 小 U+5C0F |
| 夕 | ⼣ U+2F23(康熙部首) | 夕 U+5915 |
代表者名の転記(条件B → 条件D)
姓は伏せている。名に含まれる「夕」が康熙部首として抽出される字であり、ここが誤生成の起点になっている。
| モデル | 条件B(部首混入) | 条件D(正規化のみ) | 判定 |
|---|---|---|---|
| llama3.1:8b | 小松祐介(0/3) | 小松 夕祐(3/3) | 直った |
| gemma3:12b | 小松 祐(0/3) | 小松 夕祐(3/3) | 直った |
| phi4:14b | 小松 祐祐(0/3) | 小松 夕祐(3/3) | 直った |
| ELYZA-JP-8B | (空)(0/3) | 小松 夕祐(2/3) | ほぼ直った |
| Sonnet 5 ・ qwen3系 ・ Swallow | 正しい(3/3) | 正しい(3/3) | 元から正しい |
import unicodedata
text = unicodedata.normalize("NFKC", pdftotext_output)
条件Dは条件Bを正規化しただけで、構造は完全に同じ。 文字数も13,568文字のまま変わらない。差し替わったのは344文字だけ。
レイアウト崩れは、上位モデルには効かなかった
経歴の帰属を問う2項目の正答数(n=3)。このケースの中心として設計した箇所。
| モデル | 出身校 A / B / C / D | 創業会社 A / B / C / D |
|---|---|---|
| claude-sonnet-5 | 3 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| qwen3:32b | 3 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| qwen3:14b | 3 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| gemma3:12b | 3 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| phi4:14b | 3 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| qwen3:8b | 0 / 3 / 3 / 3 | 3 / 3 / 3 / 3 |
| Swallow-8B | 0 / 3 / 3 / 3 | 0 / 3 / 3 / 3 |
| llama3.1:8b | 0 / 0 / 3 / 0 | 0 / 0 / 0 / 0 |
| ELYZA-JP-8B | 0 / 0 / 0 / 0 | 0 / 0 / 0 / 0 |
なぜ上位モデルは崩れなかったか
出力を目視して分かった。順序ではなく内容を手がかりにしている。
- 経歴文中の「取締役副社長COO」が、一覧の役職「最高執行責任者(COO)」と結びつく
- 一部メンバーは個別経歴ページ(1カラムなので崩れない)にも記載がある
8B級では、狙った罠が実際に発動した
条件B・C・Dでは3/3正解。渡し方を変えるだけで直った。 Swallow-8B も同じ挙動を示した。
誤読しかけたこと
Swallow-8B の条件A 0点は、レイアウト崩れではなかった
スコアだけ見れば「素の抽出で完全崩壊」に見える。実際は違った。
採点上は0点で正しい(JSONとして使えないので実務でも0点)が、 原因を「レイアウト崩れ」と読むのは誤りである。 対策として Ollama の
format: "json" オプションを次回検討する。ELYZA のコンテキスト長超過という推測は誤りだった
stop で正常終了しており、入力トークンも6,192〜9,966で
num_ctx=32768 に十分収まっている。単に指定された項目を埋め切れていない。推測で書かずに実測して確かめた。
Speed
速度
条件A/B/C・27件の中央値。最大値が突出しているのは初回のモデル読み込み。
| モデル | 中央値 | 最大 | 平均出力tok | 所見 |
|---|---|---|---|---|
| ELYZA-JP-8B日本語特化 | 3.2秒 | 42.1秒 | 300 | 最速・最短だが項目を埋め切れない |
| llama3.1:8b | 5.2秒 | 42.8秒 | 450 | 速いがサマリーが空 |
| Swallow-8B日本語特化 | 7.2秒 | 44.6秒 | 684 | 正規化すれば満点 |
| gemma3:12b | 7.9秒 | 83.2秒 | 473 | 速度と正確性のバランスが最良 |
| qwen3:8b | 9.4秒 | 12.9秒 | 997 | ばらつきが最小 |
| phi4:14b | 14.0秒 | 86.2秒 | 809 | 正規化すれば満点 |
| qwen3:14b | 17.8秒 | 23.7秒 | 1,076 | 全条件で満点 |
| qwen3:32b | 38.4秒 | 200.9秒 | 1,150 | 全条件で満点だが最も遅い |
Implications
実務への示唆
| 論点 | この検証からわかったこと |
|---|---|
| まず何をすべきか | PDFからテキストを抜いたら、必ずNFKC正規化を挟む。 コストは関数1回。これだけで3モデルの人名誤生成が止まった |
| レイアウト保持は必要か | 8B級を使うなら必要。-layout を付けるだけで
qwen3:8b・Swallow-8B の帰属誤りが直った。12B以上なら影響は小さい |
| 人手の構造化は必要か | この難度では不要。条件C(人手整形)と条件D(正規化のみ)は ほぼ同点。手間に見合わない |
| どのモデルを選ぶか | gemma3:12b / qwen3:14b。正規化済みなら33点満点。 gemma3:12bは7.9秒と速い |
| 避けるべきモデル | llama3.1:8b と ELYZA-JP-8B。どの渡し方でも項目を埋め切れない |
| 資料側でできること | 同じ情報を複数箇所に書く。冗長性が抽出崩れを吸収する。 1回しか出てこない情報は復元できない |
どちらが効いているかを切り分けないと、高いほうの対策を選んでしまう。
Files
使用した資料一式
元PDF、実際に投入したテキスト、プロンプト、採点キー、出力108件すべてです。 加工していないため、同じ条件で再現できます。