社内文書をAIに読ませるPoCは、たいてい最初はうまくいく。手をつけやすい文書——テキスト中心の規程集、Q&A、議事録あたり——を何十件か食わせると、それらしい答えが返ってくる。関係者に見せれば「思ったより使えるね」という反応も返ってくる。
止まるのは、対象を実際の業務文書に広げたときだ。1シートに集計表が何枚も貼られたExcel。本文には「詳細は別紙2参照」とだけ書かれていて、肝心の中身は別ファイルにある報告書。文字が図形の中に入っていて、見た目にしか存在しないPowerPoint。人間なら数秒で読み取れる情報を、AIが平気で読み違える。しかも「分かりません」とは言わず、それらしい別の数字を答えてくる。
厄介なのは、一度上がった期待値が下がらないことだ。最初のデモで「使える」と思った人たちは、残りの文書でも同じように動くと思っている。こちらとしては、文書側をAIが読みやすい形に整えてもらえれば精度は上がると分かっている。でも「この文書、AI向けに直しておいて」とは言えない。作った人がもういなかったり、量が多すぎたり、そもそも直す権限がこちら側に無かったりする。
そして一番しんどいのは、開発者ではない人に「なぜ読めないのか」が伝わらないことだと思う。同じPDFに見えるのに、こっちは読めてあっちは読めない。その理由を説明する言葉が、こちら側にも用意できていない。
私は個人で、社内共有ドライブを模した文書群にAIで答えさせるコンペ(社内文書を模した文書群へのQA、100問)に参加した。ルールは「文書には一切手を入れない」。読ませ方と答えさせ方の工夫だけで、最終的に100問中89〜90問を完全一致(Perfect)まで持っていけた(この特定の文書群での数字で、どんな文書でも9割読めるという意味ではない)。
参加してすぐに気づいたのは、これはそのまま社内文書PoCの縮図だったということ。文書は直せない、精度は求められる、フォーマットは混在している——条件が驚くほど重なっていた。この記事は、そこで分かった「フォーマットごとの読ませ方」を、そのままPoCに持ち込める形でまとめたもの。
書いている人: 個人でこのAIコンペに参加し、文書のパーサーからAIのエージェント実装まで一通り自分で組みました。この記事の対象: PDF・Excel・PowerPointの読み込み精度で足踏みしているPM・SE・情シスの方。RAGの基礎(検索してから答える仕組み)やチャンク分割の説明はしません。
100問・4フォーマット混在、という条件
このコンペの配布ファイルは、PDF・Word(docx)・PowerPoint(pptx)・Excel(xlsx)の4形式が主体で、内訳はおおよそこう。
- PDF 約30件
- Word(docx) 40件超
- PowerPoint(pptx) 約25件
- Excel(xlsx) 約20件
ほかにもCSVやMarkdownなど、複数の形式が混在していた。
この文書群を読んで100問に自動で答えるシステムを作るのが課題で、ルール上、個別の設問の正解を人が調べてシステムに埋め込むのは禁止されていた。持ち込めるのは「読み方」の汎用ロジックだけ。つまりこの記事に書く工夫は、目の前の会社の文書にもそのまま持ち込める種類のものに絞られている。
初期の実装は、資料を画像としてAIに見せて読ませていた。そこから読ませ方だけを変えていき、最終的に「人が答えを1問も教えずに、コードと配布データだけで100問を解かせる」という検証で89〜90問が完全一致になった。降参(分からないと投げる)は0問、実行エラーによる欠番も0問。しかも100問すべてで、コードが実際に出力した文字列が正解キーと一字一句一致していることまで確認した。つまりこれは「人が答えを手で埋めた結果」ではなく、「同じコードをもう一度動かせば同じ答えが出る」状態でここまで来た、という意味を持つ数字だった。
89〜90と幅があるのにも理由がある。コードが出す答え自体は再現するが、その答えを正誤判定する側(採点に使うAI)には実行のたびの揺らぎがあり、同じ解答セットに対しても判定結果が±2問ぶれることがある。答えが変わっているのではなく、採点する側の判定が毎回同じとは限らない、ということ。
読ませ方を変える、と言っても中身は大きく3層に分かれる。この先に書くフォーマット別の話は主に①前処理の中身で、末尾の「一番効いた1手」は③集約の話になる。
なぜAIは「読めない」のか、非開発者に説明する2つの言い方
技術を知らない同僚に「なぜAIが社内文書を読めないのか」を説明しようとして、うまい言葉が見つからず困った経験がある人は多いと思う。実装しながら見えてきた、そのまま使える言い方が2つある。
1つ目:「ファイルの中身は写真じゃなくて設計図」
Word・Excel・PowerPointのファイルの実体は、ZIP圧縮された設計図(XML)の束。初期の実装のように資料を画像としてAIに見せると、AIは絵を見て数字を目で読むことになり、細かい桁を読み間違える。設計図を直接開けば、数字を「読み間違える」ことは原理的に起きない。目で見てコピーしているのではなく、元のデータをそのまま取り出しているだけだから。この方針転換が、今回のスコアを最も動かした要因の1つだった。
2つ目:「AIには『空欄』が本当に空にしか見えない」
Excelでフェーズ番号の列が1行目だけ「1」で、下5行が空欄になっている表がある。人間はこれを見て、罫線や表の並びから「6行ともフェーズ1」と自然に読む。AIには、そこに書いてある文字が無い、としか見えない。しかも実測してみると、この種の表は空欄の内側と外側でまったく同じ格子罫線が引かれていて、罫線だけを見て空欄の意味を判別することは原理的に不可能だった。
この2つを押さえておくと、この先のフォーマット別の対処がつながって見えてくる。
Excel: 座標と空欄、2つの崩れ方
① ピボット表がらみの設問が、6問中0問しか解けていなかった
最初につまずいたのは、集計表(ピボットテーブル)がらみの設問だった。たとえばこういう問いだ。
「工数管理表シートの G14 に入っている数字は、どの条件で絞り込んだ、何の合計ですか」
人間なら、そのセルの左に並ぶ行見出し(部署→担当者)と、上に並ぶ列見出し(フェーズ)を目で辿って「営業部・田中さんの、設計フェーズの工数合計」と答える。セルの位置そのものが答えの根拠になっているタイプの問いで、これが6問中0問しか正解できていなかった。
原因は読み方にあった。当時は表の各行を1本の文章のようにつなげてAIに渡していたので、「G列の14行目」という座標が消え、どの見出しに属する数字なのかが分からなくなっていた。
最初に文字列連結を選んだのは、行を|でつなげるだけの単純な作りで済み、AIに渡すプロンプトも短く書けたからだった。ただ実際に読ませてみると、ピボットのように複数の表が並ぶ資料でことごとく誤読し、この近道は早々に捨てることになった。
ピボットの表示形式には、行ラベルの各項目が独立した列に並ぶ「表形式」と、行ラベルが1列だけで階層を字下げ(インデント量)で表す「コンパクト形式」の2種類がある。直し方は、セル番地(B4など)を保ったまま読ませ、字下げ量まで見て階層(支店→商品カテゴリ→合計、のような構造)を自動で組み立て直すこと。方眼紙の升目の位置を保ったまま読む、という感覚に近い。これで該当タイプの設問が6問中0問から6問中6問に変わった。
② 空欄が「前の値を引き継ぐ」意味を持っていた
業務用のExcelでよく見る、こういう表があった。
| フェーズ | タスク | 担当 |
|---|---|---|
| 1 | 要件ヒアリング | 田中 |
| 業務フロー整理 | 田中 | |
| 課題の洗い出し | 佐藤 | |
| 2 | 基本設計 | 佐藤 |
| レビュー | 田中 |
人間はこれを見て、2行目から4行目も「フェーズ1」だと迷わず読む。書いていないのは、同じ値を繰り返すのが冗長だからで、空欄は「上と同じ」という意味を持っている。ところがAIには、そこに文字が無いとしか見えない。「フェーズ1のタスクは何件ですか」と聞くと、1件と答えてしまう。
最初は罫線で判別しようとした。「上と同じ」の範囲は罫線で囲われているはずだ、という読みだ。ところが実際に罫線の設定を調べてみると、空欄の内側と外側でまったく同じ格子線が引かれていた。見た目の区切りは人間が行の内容から補って感じているだけで、データとしては存在しない。罫線を手がかりにする方法は、この時点で捨てた。
そこで構造だけで判定する方式に切り替えた。「2列以上が同じ行範囲で同時に切り替わる」列だけを、上のセルの値で埋める。逆に、フラグが繰り返し並ぶ列や、備考欄のような単独列でしか固まりを作らない列は埋めない。実際、備考欄まで埋める設計にしていたら、「特定の項目に対応するタスクは何件か」型の設問で、本来1件しかない注記を複数行に共通する値と取り違え、1件を5件と答える誤りを新たに作り込むところだった。改善のつもりの変更が、誤答を生む側に回るところだった。
構造分類の結果、「上と同じ」として継承すべき空欄は、階層ラベル型のグループが59、セルの縦結合そのものが28件見つかった。この分類ロジックを実装する前に、代表的なパターンを1つずつ自分の目で開いて「ここは継承すべき/すべきでない」を判定し、それを期待値として固定した。実装後の回帰テストは9件中9件が想定どおりだった。
手元のExcelが「1シートに表が複数貼ってある」「フェーズ列が結合セルで省略されている」のどちらかに当てはまるなら、まずはここを疑うとよい。経費集計表や工数管理表でよく見かける形だった。
PDF: まず2種類を見分け、書式は文字ごとに読み直す
テキストPDFか、画像PDF(スキャン)か
画像PDFは文字データを1文字も持っていないので、そのまま抽出しようとしても何も取れない。配布されたPDFのうち、6割強が画像PDFだった。ここにOCRをかけずに済ませていたら、それだけで少なくとも9問が全滅する分量だった。
OCRのキャッシュ保存については、苦い経験がある。パーサーを作り直したとき、更新処理がOCRの結果まで上書きしてしまい、画像PDFがほとんど読めない状態になった。これに気づかず提出した回は、スコアがほぼ全滅した。
厄介なのは、この壊れ方が静かなことだ。パーサーはエラーを出さず、抽出結果も空にはならない。ページあたり数十文字の断片は返ってくるので、ログを眺めているだけでは正常に見える。同じことを後日もう一度やってしまったが、その時は提出前の検査で気づき、キャッシュから全件復元できたので実害はなかった。それ以来、パーサーを更新する前に、OCR結果が正しく反映されているかを機械的に検査してから次の工程に進むようにしている。同じ構造のバグを2回作った側から言うと、これは注意力ではなく検査で防ぐ種類の事故だと思う。
太字・下線は、文字ごとに書式フラグを直接読む
書式(太字・下線)を扱うには、文字を1文字単位で開き直し、各文字が持つ書式のフラグを直接読む必要がある。理由は単純で、「太字で強調されている項目は何か」のような設問に対してPDFから普通にテキストを抜くと、文字だけが残って書式の情報が消えてしまうからだ。
中には、斜体の字形を持たないフォントで見た目だけの斜体が作られている資料もあった(合成斜体と呼ばれる処理。詳しい仕組みは後述のWordセクションで扱う)。フォント名や斜体フラグでは検出できず、文字を描画する座標変換の中の「せん断」(四角形を平行四辺形に歪める変形)の量を直接見て初めて検出できた。下線も、実装当初は検出ロジック自体が存在せず、判定が常に「下線なし」で固定されていたことが後から分かった。この2つを直したうえで全ページを対象に、太字・下線・斜体が同時に重なっている箇所を機械的に洗い出したところ、該当は1箇所だけで、自分で目視して確認した結果と一致した。
契約書の重要条項の強調・注記の太字のように、「見た目の書式そのものが意味を持つ」PDFを扱っているなら、同じ落とし穴を踏みやすいので注意したほうがいい。
Word: 見た目だけの書式と、本文の丸め値
斜体の字形が無いフォントで、見た目だけの斜体が作られていた
Wordは、指定したフォントに斜体の字形が無い場合、文字を機械的に少し傾けて描画することで見た目だけの斜体を作る(合成斜体)。実際にあった資料では、使われていたフォントに斜体の字形が用意されておらず、文字が18度ほど傾けて描画されていた。この処理はフォント名にも、斜体かどうかを示す書式フラグにも痕跡を残さない。書式そのものが設問の手がかりになる資料でこの種のフォントが使われていると、通常の書式チェックでは検出そのものができない。
グラフは絵ではなく、データがそのまま埋め込まれている
Word文書に貼られたグラフも、実体は絵ではない。ファイルの中のword/charts/chart1.xmlという部分に、元になった数値がフル精度でそのまま入っている。画像として目で読むのではなく、このデータ部分を直接取り出せば、小数第5位まで正確な値が取れる。
表が画像として貼られていて、本文の数値が丸め値だった
一見表に見えても、実際には表が画像(EMFという古いWindows形式)として貼り付けられているだけで、セル自体には値が無いことがあった。しかも、その表のすぐそばの本文が、表の実値とは別に「約〜」付きの丸め値を書いていた。表には中央値と平均という2つの統計量が並んで書かれているのに、本文は片方だけを丸めて紹介しており、本文の丸め値だけを見て答えると、質問が求めている統計量と食い違う。
この食い違いに気づく前は、本文に書かれた数値をそのまま採用していた。本文の方が読みやすく、表が画像として埋め込まれている資料まで律儀に開きに行く優先順位が低かったからだ。ところがその数値は表の片方の統計量を丸めただけのもので、質問が求めていたのはもう片方だった。本文の言葉を信じたことが、遠回りして誤答を生んだ。以来、質問が統計量(平均・中央値など)を指定していたら、本文の要約値ではなく、表・グラフのラベルに厳密に一致する実数を使うようにしている。
契約書に添付された参考図表や、報告書の本文中に「概算では」「約」といった要約表現がある資料では、同じ食い違いが起きやすい。
PowerPoint: 座標と色は、数値で判定し直す
色コードの決め打ちは、3例続けて外れた
「赤い行を全部挙げて」に正しく答えようとして、最初は色コードを決め打ちで判定した。多くの資料が赤=FF0000のような定番色を使っているだろう、という見込みだった。ところが実際の資料では、赤字が真っ赤(FF0000)ちょうどではなかったり、資料ごとのブランドカラーが赤系の別の色だったりして、色コードの決め打ちは3例続けて外れた。文字色・セルの塗り色・条件付き書式のすべてを対象に、色を「コード」ではなく色相(Hue)の範囲で判定する方式に切り替えたところ、この種の設問を安定して拾えるようになった。色は資料ごとに違う数値が使われていて、名前や決め打ちのコードでは管理できない、というのが実感だった。
「どこにあるか」がそのまま答えになる
PowerPointのテキストをそのまま抜き出すと、「どの図形がどこに置かれているか」という位置の情報が消える。座席表(誰がどの席にいるか)やガントチャート(どのタスクが第何週か)のような設問は、図形の座標そのものでしか答えが決まらない。図形を座標付きで読み出す処理に切り替え、あわせて画面には出ないレイアウトの残骸のような図形が重なっていることもあるため、実際に描画される図形だけを対象にする絞り込みも入れた。
仕事での場面では、進捗管理のガントチャート、体制図の座席表、赤字ハイライトでの要注意項目の抽出など、「見た目の配置や色そのものが情報」というPowerPoint・Excelの資料でそのまま使える考え方。
一番効いた1手: 推測させず、元データを直接読ませる
ここまではフォーマットごとの個別の話だったが、スコアの上げ幅で見ると、これらを全部足したよりも効いた変更が1つある。
当初は、AIを何度も独立に走らせて、多数決で答えを決める方式を使っていた。多い意見を勝たせる、という考え方自体は自然に見える。ところが、AIが一次資料ではなく自分の知識や推測だけで答えを組み立て、多数決で「それらしい答え」に収束しているだけのケースが混ざっていた。多数決を積み重ねても、間違った推測が多数派になれば、間違ったまま安定してしまう。
確信度をどう扱うかも、最初から今の形だったわけではない。最初はAI自身に「この答えにどれくらい自信があるか」を数字で申告させ、閾値未満なら棄権させる設計にしていた。ところが間違えた問題でも、正解した問題と同じくらい高い確信度を申告しているケースが多く、この自己申告方式はすぐに見切りをつけることになった。申告された数字ではなく、「実際に一次資料を調べたか」という行動の記録を見る方式に切り替えた。
この4つは、思いつきで並べたものではなく、実際に誤答を出した型から逆算した。
| 検査する条件 | なぜその型を疑うのか |
|---|---|
| 一次資料を1度も調べていない | 調べずに答えられる問いは、そもそも資料を配る必要がない。手元の知識で作文した可能性が高い |
| 「該当なし」なのに調べた手段が1種類だけ | 「無い」の証明には網羅探索が要る。1つの探し方で見つからなかっただけの可能性が残る |
| 「すべて挙げよ」型なのに全件を列挙できる手段を使っていない | 部分的に見つけた分だけで答えると、列挙の抜けに気づけない |
| 答えに数字があるのに元データの確認が0回 | 数字は最も捏造されやすい。それらしい桁数の値が作られていても、読んだだけでは気づけない |
多数決に頼るのをやめ、AIには一次資料を直接調べさせてから答えさせる、という方向に設計を変えた形になる。
この1つの切り替えが、他のどの個別対応よりもスコアを動かした(+9.0点)。フォーマットごとの読み方をいくら磨いても、AIが結局「読まずに答えている」なら意味が無い、ということだった。
この設計はどれも、100問をまとめて解かせるバッチ処理の前提で磨いたものだった。1問ごとに何度もツールを呼び、時には調べ直す作りは、精度には効くが速くはない。チャット画面で会話しながら即座に答えを返したい場面では、この作り方のままだと待ち時間がそのままボトルネックになる。精度をバッチで先に詰め、チャット向けには「よく聞かれる型を先読みしてキャッシュしておく」といった速度側の工夫を別に積む、というのが現実的な順番だと思う。
読者が今日できる最小の一歩
いきなりパーサーを書き直す必要はない。今日できることは1つで十分。
自分のPoCで外れた回答を、もう一度見返して、「内容そのものを間違えている」のか「内容は合っているのに書き方(書式)がズレているだけ」なのかを仕分けてみてほしい。
「AIが文書を読めていない」と感じている失敗の何割かは、実は「読めているのに、答え方がズレているだけ」かもしれない。読み方を直す前に、まずここを仕分けるだけなら、今日中に終わる。
もう1つ、すぐに確認できることがある。いま使っている読み込み処理が、資料を「画像として見せている」のか「元のファイルの構造を直接読んでいる」のかを確かめてほしい。PDFを画像化してAIに渡している、Excelを画面キャプチャで読ませている、といった実装は今でも珍しくない。前者になっているなら、後者に切り替えるだけで、数字の読み間違いの多くはそもそも起きなくなるはずだ。
汎用化できる部分と、まだ解けていない部分
ここまでの工夫のうち、座標を保って読む・書式のフラグを直接読む・画像より先に構造化データを疑う、という基本方針はフォーマットが変わっても効くはずだと思っている。実際、この考え方1つでExcel・PDF・Word・PowerPointの4つに横展開できた。
一方で、そのまま真似できない部分もある。色相での判定は、文字色・塗り色・条件付き書式がそれぞれ独立して設定されている資料でうまく機能したが、影付きの図形やテーマの自動配色に頼っている資料まで含めて安定するかは、まだ十分に試せていない。空欄をどこまで前の値の継承として扱うかという構造分類のルールも、表の作り方の癖に強く依存する部分で、手元の文書で検証しないと同じ結果にはならないと思う。
さいごに
文書は直せなかった。直せたのは、AIにその文書をどう見せるか、どう答えさせるかという読み方の方だけだった。今日の一歩を仕分けてみて、もし外れた回答の多くが書式のズレだったら、それはPoCの限界ではなく、まだ直せる部分が残っている、というサインだと思う。
この記事の点検を自社の文書でやるための作業シートを置いています。フォーマット別のチェック項目、非開発者に説明するときの言い方、精度の測り方のテンプレをまとめたものです。
同じ壁にぶつかっている方の話を聞いてみたい。似たような詰まり方をした経験があれば、コメントで教えてもらえると嬉しい。