Case 09 / PDF資料の情報抽出

精度を決めていたのは、文字コードだった

PowerPoint由来のPDFから同じ内容を4通りの渡し方で作り、抽出とサマリーをさせました。 正規化を1行入れるだけで、3モデルの人名誤生成が止まりました。

  • 実施 2026年8月
  • 試行 各モデル3回(n=3)
  • 対象 ローカル8モデル + Sonnet 5
  • 出力 全108件・欠損ゼロ

Conclusion

結論

PowerPointで作った会社紹介のPDF(17ページ)をAIに読ませ、会社名・設立日・代表者名・料金などを抜き出させました。 同じPDFを使い、テキストの取り出し方だけを4通りに変えて、9つのAIモデルで比べています。

  1. 01

    AIが、実在しない人名を作った

    代表者名は資料に「小松 夕祐」と書いてあります。ところが3つのAIモデルが 「小松祐介」「小松 祐」「小松 祐祐」という、存在しない名前を出しました。3回試して3回ともです。
    原因は、PDFから文字を取り出したときに「夕」が、見た目は同じでもコンピュータ上は別物として扱われる漢字に置き換わっていたことでした。 AIはその字を読めませんでしたが、空欄にせず、それらしい漢字で埋めました。 提案書や議事録の要約でこれが起きれば、相手先の担当者名を間違えたまま社外に出ることになります。

  2. 02

    対策は、AIに渡す前に文字を揃える1行だけだった

    取り出したテキストに、文字の表記を統一する処理(Unicode正規化)を1行かけてから渡したところ、 3モデルとも正しい名前を返すようになりました。
    文書の構造にも文章にも手を加えていません。13,568文字のうち344文字が別の文字コードに置き換わっただけです。 unicodedata.normalize("NFKC", text) ── 対策はこれだけでした。

  3. 03

    一方、レイアウトの崩れは、性能の高いモデルなら自力で読み解いた

    PDFから素直に文字を取り出すと、3列組みのメンバー紹介が縦一列に潰れ、 経歴が「別人の名前の直後」に並びます。人が読んでも取り違える並びです。
    それでも上位モデルは、並び順ではなく書かれている内容を手がかりに正しい人物へ結びつけました。 つまりレイアウトの崩れは人手で整形しなくても済む場合がある一方、文字コードのずれは誰も気づけないまま誤情報になります。 対策のコストは正反対で、前者は人手の作業、後者はライブラリ関数1回です。

Input

入力:何を渡したか

元になるPDFは1つです。同じPDFから4通りのテキストを作り、変数を「渡し方」だけに絞りました。

会社概要、育成プログラムの条件、コンサルティング支援の内容、メンバー紹介、導入事例が含まれます。資本金・決算期・従業員数の記載はありません。この「載っていない項目」が採点の中心になります。

4つの渡し方

条件作り方文字数この条件で分かること
Apdftotext7,831レイアウトを一切保たない。段組みが潰れたときに帰属を復元できるか
Bpdftotext -layout13,568列を空白で保つ。ただし康熙部首の異体字が混入する
C人手で構造化(Markdown)8,306見出し・表に整形済み。人が手を入れた場合の上限
D条件B + NFKC正規化13,568構造は条件Bと完全に同じ。差は文字コードだけ
条件Dは、変数を文字コードだけに絞るために追加しました。 条件Cは「レイアウト整形」と「文字の正規化」が同時に効いてしまい、どちらの効果か切り分けられません。 条件B → 条件D の差だけを見れば、正規化の効果を単独で測れます。

同じ箇所が、条件でどう変わるか

メンバー紹介ページ(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を開きます

Scoring

採点:どう測ったか

採点キーはモデル出力を1件も見ないうちに、元PDF全17ページを人が読んで確定させています。 実行後に基準を足したり緩めたりしていません。

仕掛けた罠何を見るか配点
資本金は記載がない会社概要の定番項目なのに載っていない。金額を書いたらハルシネーション1
2つの育成プログラムベーシックとプラクティカルで期間・人数・金額が異なる。取り違えないか8
指定した人物の経歴メンバー紹介は3列×2段。別人の経歴を当てないか2
会社名の語順「株式会社colabofact」は誤り。語順を入れ替えないか1
ページ数の表記フッタは「N/10」だが実際は全17ページ

照合方法は項目ごとに定義しています(完全一致/包含/空白除去して包含/「記載なし」を表す表現を含み、かつ金額を含まない、など)。採点スクリプトには当たり判定のセルフテストを用意し、正しい拒否回答を誤判定しないことを確認してから本採点にかけています。

配点の全文と照合ルールは、採点キーのJSONに書いてあります。 下の「資料一式」から確認できます。

Result

結果

33点満点(単純項目31個+メンバー経歴2項目が各2点)。n=3の中央値。

モデルA 素の抽出B レイアウト C 構造化D 正規化のみD−B
claude-sonnet-5クラウド 33333333±0
qwen3:32b 33333333±0
qwen3:14b 33333333±0
qwen3:8b 31333333±0
gemma3:12b 32323333+1
phi4:14b 31323233+1
Swallow-8B 0323333+1
llama3.1:8b 23212321±0
ELYZA-JP-8B 601616+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-53 / 3 / 3 / 33 / 3 / 3 / 3
qwen3:32b3 / 3 / 3 / 33 / 3 / 3 / 3
qwen3:14b3 / 3 / 3 / 33 / 3 / 3 / 3
gemma3:12b3 / 3 / 3 / 33 / 3 / 3 / 3
phi4:14b3 / 3 / 3 / 33 / 3 / 3 / 3
qwen3:8b0 / 3 / 3 / 33 / 3 / 3 / 3
Swallow-8B0 / 3 / 3 / 30 / 3 / 3 / 3
llama3.1:8b0 / 0 / 3 / 00 / 0 / 0 / 0
ELYZA-JP-8B0 / 0 / 0 / 00 / 0 / 0 / 0

なぜ上位モデルは崩れなかったか

出力を目視して分かった。順序ではなく内容を手がかりにしている。

  • 経歴文中の「取締役副社長COO」が、一覧の役職「最高執行責任者(COO)」と結びつく
  • 一部メンバーは個別経歴ページ(1カラムなので崩れない)にも記載がある
資料内の冗長性が、抽出崩れを吸収していた。 逆に言えば、1回しか出てこない情報は、崩れたら復元できない。 同じ情報が繰り返される資料ほど、雑な抽出に耐える。

8B級では、狙った罠が実際に発動した

qwen3:8b は条件Aで、あるメンバーの出身校を「別のメンバーの出身校」と回答した(3回とも)。 崩れた並びで直前に置かれている人物の経歴である。
条件B・C・Dでは3/3正解。渡し方を変えるだけで直った。 Swallow-8B も同じ挙動を示した。

誤読しかけたこと

Swallow-8B の条件A 0点は、レイアウト崩れではなかった

スコアだけ見れば「素の抽出で完全崩壊」に見える。実際は違った。

"サマリー": "…様々な分野の"タレント"とコラボレートすることで…"
資料原文の二重引用符をそのままJSON文字列に入れ、エスケープし損ねていた。 3回とも同じ壊れ方をした。中身自体は正しく取れている。
採点上は0点で正しい(JSONとして使えないので実務でも0点)が、 原因を「レイアウト崩れ」と読むのは誤りである。 対策として Ollama の format: "json" オプションを次回検討する。

ELYZA のコンテキスト長超過という推測は誤りだった

当初「入力が長すぎて読めていない」と考えたが、実測で否定された。 全108件が stop で正常終了しており、入力トークンも6,192〜9,966で num_ctx=32768 に十分収まっている。
単に指定された項目を埋め切れていない。推測で書かずに実測して確かめた。

Speed

速度

条件A/B/C・27件の中央値。最大値が突出しているのは初回のモデル読み込み。

モデル中央値最大平均出力tok所見
ELYZA-JP-8B3.2秒42.1秒300最速・最短だが項目を埋め切れない
llama3.1:8b5.2秒42.8秒450速いがサマリーが空
Swallow-8B7.2秒44.6秒684正規化すれば満点
gemma3:12b7.9秒83.2秒473速度と正確性のバランスが最良
qwen3:8b9.4秒12.9秒997ばらつきが最小
phi4:14b14.0秒86.2秒809正規化すれば満点
qwen3:14b17.8秒23.7秒1,076全条件で満点
qwen3:32b38.4秒200.9秒1,150全条件で満点だが最も遅い
出力量と正確性は相関していない。 ELYZAは最速・最短だが最低スコア。qwen3:14b は32Bと同点で応答が半分以下。 この用途では14Bで足りる。

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回しか出てこない情報は復元できない
このケースで最も重要なのは、対策のコストが問題によって桁違いに違うこと。 レイアウト崩れへの対策は「人手で構造化する」で高コスト。 しかし実際に精度を分けていた文字コードへの対策はライブラリ関数1回だった。
どちらが効いているかを切り分けないと、高いほうの対策を選んでしまう。

生成系AI活用、DX推進についてご相談ください

レビュー分析やインサイト生成、戦略支援など、生成系AIの活用やDX推進について幅広く支援いたします。

お問い合わせ