はじめに
業務資料で重要なデータは、たいてい表に入っています。しかも PDF です。
単純な表なら、PDF のまま入れても引けます。ただ、セル結合が入り組んだ表になると、文書は検索で見つかるのに、中の数値だけが出てこない。
表なら、行と列のある形に直せばいい。CSV にすることにしました。
問題は、その CSV を誰が作るのかです。
この記事は、IBM watsonx Orchestrate(wxO)のナレッジベースに表データを持ち込む方法を、IBM Bob を活用して3通り試した記録です。結論から書くと、表を画像のまま AI に見せて読み取ってもらった CSV が、いちばん良い結果でした。
Bob は IBM の AI 駆動型の統合開発環境(IDE)です。この記事では、CSV を作るのも、wxO への登録も、動作確認も、Bob と一緒にやっています。
1. 題材にした表
気象庁が公開している「特別警報の指標及び危険警報・警報・注意報発表基準一覧表(東京管区気象台管内)」を使いました。
簡単な表で試しても仕方がないので、難しそうなものを選んでいます。
1-1. 表の構造
列方向は2段になっています。上段が一次細分区域(東京地方、伊豆諸島北部、伊豆諸島南部、小笠原諸島)、下段がそれをさらに分けた市町村等をまとめた地域で、23区西部から小笠原諸島まで10列あります。
行方向も2段です。左端に特別警報・危険警報・警報・注意報という区分があり、その内側に暴風(平均風速)、強風(平均風速)、大雪といった要素の行が並びます。
値の入るセルは、1列だけのものもあれば、複数列にまたがるものもあります。行方向にまたがるセルもあります。
さらに、値の脇に「※1」「※2」のような注釈記号が付いていて、その中身は表の外、いちばん下に書かれています。
1-2. 人がこの表を読むとき
たとえば「多摩西部の強風注意報の基準は何 m/s か」を、この表から人が読み取るとします。
- 左端の区分から「注意報」のブロックを見つける
- そのブロックの中で「強風(平均風速)」の行を見つける
- 上の列見出しから「多摩西部」の列を見つける
- 行と列が交わるセルを読む
- そのセルは多摩北部・多摩南部・多摩西部の3列にまたがった結合セルで、「13m/s ※2」と書かれている
- 「※2」が何かを、表のいちばん下の注記で確かめる
縦に2回、横に2回たどって、交点を見て、結合の範囲を確かめて、最後に表の外を見に行く。
値そのものは「13m/s」の5文字ですが、そこに至るまでに位置関係を何度も使っています。
RAG に渡すというのは、この位置関係を持ったまま渡せるかどうか、という話になります。
2. 試したデータ形式
2-1. 全体像
3種類のデータ形式を用意し、2つの質問の結果を比較しました。
用意したもの
- A(PDF): PDF をそのまま
- B(テキスト→CSV): PDF から抽出したテキストをもとに作った CSV
- C(画像→CSV): PDF を画像にして読み取り、作った CSV
B は内容を確認してテスト対象から外しました。
投げた質問と期待する結果
- Q1: 多摩西部の強風注意報の基準は何m/sですか → 13 m/s
- Q2: 東京23区の暴風警報の基準は何m/sですか → 陸上・海上とも 25 m/s
検証結果
| 質問 | A の回答 | C の回答 |
|---|---|---|
| Q1 | × 25 m/s | ○ 13 m/s |
| Q2 | × 記載がありません | ○ 陸上・海上ともに 25 m/s |
2-2. A(PDF)について
まずは PDF をそのまま wxO に渡してみました。
Q1 への回答
多摩西部の「強風注意報」
基準となる平均風速は 25 m/s(陸上・海上ともに)です。
出典: 「tokyo_warning_standards.pdf」の表中に「暴風(平均風速) 陸上 25 m/s,海上 25 m/s」と記載されています。
誤答。25m/s は暴風警報の値です。聞いたのは強風注意報です。
出典として引用している文字列は、文書に実在します。暴風の行から取ってきたものです。
文書は合っている。引用も合っている。行だけが違う。
Q2 への回答
東京23区における暴風警報の発表基準(風速)は、現在のナレッジベースに収録されている「特別警報の指標及び危険警報・警報・注意報発表基準一覧表(東京管区気象台管内)」の本文中には具体的な数値が記載されていませんでした。
誤答。原典には書いてあります。
2-3. B(テキスト→CSV)について
A が駄目だったので、CSV にすることにしました。
PDF からテキストを抽出し、それを材料に CSV を作る。ここからは Bob に頼んでいます。
出てきた CSV は、列の割り当てが合っていませんでした。項目名が固まって並んだ後に、数値が固まって並ぶ形で、両者の間には列の情報がありません。
テスト対象から外しました。
2-4. C(画像→CSV)について
テキストからでは復元できないので、材料を替えました。画像です。
注釈記号(※1〜※3)が判別できるレベルまで PC で拡大し、スクリーンショットで保存しました。
その画像を Bob に見せて、表のセルを読み取って CSV にしてもらいました。
画像には、テキストに無かったものが残っています。セルの位置関係です。
どのセルがどの列の下にあるか、どこからどこまでが1つのセルか。人が表を読むときに使っているものが、そのまま写っています。
最近の AI の能力を使えば、それを読み取りデータに変換してくれます。
出てきた CSV は、列がずれていませんでした。
Q1 への回答
多摩西部における強風注意報の基準は、平均風速 13 m/s です。
正解。ただし基準値のみで、「※2」の注釈の内容は反映されていません。
Q2 への回答
暴風(平均風速)/警報: 陸上 25 m/s、海上 25 m/s(23区西部・23区東部共通)
正解。
素晴らしい。正直なところ、あの入れ子のセル結合が読める形になるとは思っていませんでした。
3. チャンクの中身
wxO のチャットでは、回答の上にある「理由の表示(REASONING)」を開くと、エージェントがナレッジベースをどんなクエリで検索し、どのチャンクが返ってきたかを見られます。これを見てみます。
3-1. A(PDF)のチャンク
引けたチャンクは、どれも似た壊れ方をしていました。3種類あります。
項目名はあるが、値がない
| 注 強風(平均風速) 風雪(平均風速) | 水位(高潮予報区間に限る)又は潮位が別紙4のレベル4高潮 危険警報の基準値に到達することが予想される場合 ...
「強風(平均風速)」という項目名は入っています。隣に並んでいるのは高潮の基準文で、風速の値はどこにもありません。
値はあるが、聞かれた項目のものではない
| 報 暴風(平均風速) | 前までに発表) 陸上 25m/s,海上 25m/s 陸上 25m/s 雪を伴う,海上 25m/s 雪を伴う | 25m/s 25m/s 雪を伴う
暴風の行です。項目名と値が揃っています。これが引けたのは、強風注意報を聞いた Q1 のときでした。
そもそも中身がない
| 警 |
| 令和8年5月28日現在 気象庁 東京都 | 令和8年5月28日現在 気象庁 東京都
「警」の1文字だけのもの、日付とヘッダーだけのもの。Q2 の2回目の検索では、5件のうち4件がこのヘッダーの断片でした。実質1件しか引けていません。
共通しているのは
聞いた項目と、引けたチャンクの中身が対応していないことです。
1-2 で人が読んだときは、「多摩西部」の列を見て、その下の「強風」の行との交点を読んでいました。
チャンクの中では、この交点が保たれていません。強風の項目名は引けても値がなく、値が引けたときは強風ではなく暴風の行でした。
Q1 では、たまたま値のある断片が引けたので、それを使って回答が組み立てられました。Q2 では、値のある断片が引けなかったので「記載がありません」になりました。どちらも、聞いた項目の値には届いていません。
そして、回答だけを見ても分かりません。「記載がありません」は正直な回答に見えます。「25 m/s」には出典が付いています。ここを開いて初めて、何が起きていたかが見えました。
3-2. C(画像→CSV)のチャンク
同じように、C(画像→CSV)の側も見てみます。
種別: 暴風(平均風速) | 区分: 警報 | 23区西部: 陸上 25m/s 海上 25m/s | 23区東部: 陸上 25m/s 海上 25m/s | 多摩北部: 25m/s | 多摩南部: 25m/s | 多摩西部: 25m/s | 大島: 25m/s(※1) | 新島: 25m/s(※1) | 八丈島: 30m/s | 三宅島: 30m/s | 小笠原諸島: 25m/s
これが、一番の驚きでした。
CSV の1行は、本来は値だけの並びでした。7項目めの「25m/s」は、それだけを見ても何のことか分かりません。1行目(ヘッダー行)の7項目め、「多摩西部」を見て初めて分かる情報です。
それが、列名: 値 のペアの並びに展開されています。7項目めの値に、ヘッダー行の7項目めの列名が付け加えられて、「多摩西部: 25m/s」になっている。
しかも行の境界が守られています。暴風の行と強風の行が、混ざっていない。
「多摩西部の強風注意報」を聞かれたときに、暴風の 25m/s を拾ってくる余地がありません。
空のセルは nan で埋まっていました。
種別: 暴風雪(平均風速) | 区分: 警報 | ... | 大島: nan | 新島: nan | 八丈島: nan | 三宅島: nan | 小笠原諸島: nan
見た目は良くありませんが、「この区域には値が無い」という情報が残っています。空欄が詰められて隣の値がずれる、ということが起きない。
なお、今回の CSV は4つのチャンクに分かれました。分かれ方は質問を変えても同じだったので、投入した時点で決まっているようです。ただし何行ずつまとめるかの規則については、公開されている情報からは説明がつきませんでした。ここは分かっていません。
3-3. 公式ドキュメントに書いてありました
ここまで分かってから、ADK の公式ドキュメントを検索しました。
Knowledge and document processing considerations というページに、「Ignoring file format implications」というアンチパターンがあります。
そこに、表形式のデータには XLSX より CSV が推奨される、と書かれていました。
理由も書いてあります。各行のデータが同じコンテキストに保持されるから。XLSX だとセルが個別にインデックスされて、行としての関係が壊れる。
チャンクを見て気づいたことが、そのまま書いてありました。
さらに同じ箇所には、複雑なレイアウトの PDF では表・段組み・埋め込み画像が崩れる、という記述もあります。対処としてはレイアウトを認識できるパーサーを使うこと、と。
A(PDF)で起きたことも、C(画像→CSV)で解決したことも、同じ1ページに先に書いてあったわけです。
3-4.(参考)ナレッジベースの設定
A(PDF)と C(画像→CSV)で条件が揃っていることは、list_knowledge_bases --verbose で実測して確認しました。
- ビルトイン Milvus(ファイルアップロード)
- 埋め込みモデル:
ibm/granite-embedding-278m-multilingual
既定は英語向けのibm/slate-125m-english-rtrvr-v2ですが、日本語の文書を日本語で検索するので多言語モデルを指定しています -
chunk_size: 400 /chunk_overlap: 50 /extraction_strategy: standard
YAML では未指定で、サーバー側で既定値が入って返ってきます -
conversational_search_tool:query_sourceは Agent(dynamic モード)、generation.enabledは false、query_rewrite.enabledは true
extraction_strategy には high_quality という選択肢があります。これは試していません。この記事の結果は、すべて既定値のままのものです。
4. CSV は精査が必要
ここからは、うまくいった話ではありません。
画像から CSV を作る方法は、要するに読み取りです。読み取りは間違えます。
今回も間違えました。
強風(平均風速)の行に「※2」という注釈記号が付いています。八王子(アメダス)の観測値は16m/sを目安とする、という但し書きです。
原典では、この値のセルは多摩北部・多摩南部・多摩西部の3列にまたがる1つの結合セルです。つまり※2は3区域すべてにかかります。
生成された CSV では、多摩西部だけに付いていました。
指摘して直してもらったのですが、直ったのは指摘した行だけでした。
同じ構造の誤りは他の行にもあり、同様の修正を繰り返す必要があります。
4-1. 先に決めておくこと: 結合セルをどう展開するか
3列にまたがったセルに「10」と書いてあるとき、読み方は2通りあります。
3列それぞれが 10 なのか、3列を合わせて 10 なのか。
前者なら各列に 10 を並べるのが正しく、後者なら並べた時点で意味が変わります。
今回の表は前者でした。区域をまとめたセルの風速は、どの区域にもそのまま当てはまります。
ただし表によっては後者もあります。金額や件数を扱う表なら、まとめたセルが合計を意味していることは珍しくありません。
これは読み取りが正確かどうかの話ではなく、表の意味の話です。
出てきた CSV を見てから決めようとすると、どちらの解釈でも辻褄が合ってしまいます。読み取りを頼む前に決めておくものだと思っています。
4-2. CSV で確認する視点
全セルを原典と突き合わせるのかというと、そこまでは要らないと思っています。
今回踏んだ範囲では、3つでした。
注釈の係り先
※1、※2 のような記号が、どのセルにかかっているか。結合セルに付いている注釈は、そのセルがまたがる列すべてにかかります。ここが今回間違えた箇所です。
空欄の分布
CSV の中で空欄がどこに出ているか。結合セルを見落とすと、本来値が入るべき列が空欄になります。逆に、原典で本当に空のはずのセルに値が入っていることもある。空欄の位置は原典と見比べやすいので、結合の見落としを見つける手がかりになります。
代表セルの原典突き合わせ
各行から1〜2個だけ選んで、原典と照合します。列が丸ごとずれていれば、どのセルを選んでも合いません。1個合えば、その行の列の割り当ては信用してよい。
今回の誤りは、この3つで全部見つかりました。全セル照合はしていません。
5. おわりに
セル結合だらけの表を CSV にする。どう解決すればいいのか分からなかったこの作業に、選択肢がひとつ増えていました。
表なら CSV がいい、というのは公式ドキュメントにも書かれています。書かれていないのは、その CSV をどう用意するかです。今回いちばん時間を使ったのは、そこでした。
B(テキスト→CSV)を採らなかったのは、材料に情報が無かったからです。
こういう表を構造ごと機械に読ませる試みは以前からありますが、手元で気軽に試せるものではありませんでした。
C(画像→CSV)でやったのは、セルの位置関係を保ったまま渡しただけです。スクリーンショットを撮って、貼って、指示した。それだけです。
AI に画像を読み取らせるという使い方が、今回の検証で新しい道具になり得ることを確認できました。
もちろん読み取りは間違えるので、出てきたものは自分で確認する必要があります。それでも、手が出せない状態とは違いました。
うまく答えないとき、検索の設定を疑いたくなります。でも直すべきだったのは検索ではなく、渡す形でした。
なお、例外規定や但し書きの扱いには、これとは別の課題があります。
「※2」のような注釈は、CSV に載せることはできても、回答のときに適用されるとは限りませんでした。ここはまだ試している最中です。
本記事は、一連の検証と執筆にあたり Anthropic Claude を利用し、その出力を参考にしています。
