前回は、8GB の GPU で日本語の縦書き資料を OCR し、AWS の Knowledge Base に載せるところまで書いた。今回はその続きで、図と表をどう扱うかである。結論を先に書くと、図を VLM に読ませるのはやめた。代わりに「図のキャプションと図中の文字を本文に埋める」だけにして、図の意味は Claude 側で本文から再構成させる。その判断に至るまでの試験と、途中で見つけたバグの話をする。
問題: OCR は本文しか取らない
OCR エンジン(YomiToku)は段落のほかに図の領域と表の構造も検出しているのだが、パイプラインは段落しか使っていなかった。しかも完了時に原本のページ画像を削除する設計なので、図は文字どおり消えていた。
横書きの技術資料を登録してみると、これが痛い。通信プロトコルの資料はシーケンス図と状態遷移図が本体で、本文はその説明である。「閉塞信号を送って確認信号が返らなければどうなるか」という質問に答えるには、図の中の矢印の向きが要る、と思っていた。
選択肢は 3 つあった。
| 案 | やること | 検索 | 解釈 | 保持する著作物 |
|---|---|---|---|---|
| A. 表だけ | 表を Markdown 表に変換して本文へ | ○ | ○ | 増えない |
| B. 図を切り出して保持 | 図の画像を残し、検索時に MCP で Claude に見せる | キャプション経由 | ◎ | 図の画像が残る |
| C. 図を文章化 | 切り出した図をローカル VLM で説明文にする | ○ | △ | 増えない |
A は迷う余地がない。B と C の分かれ目は「図の画像をサーバーに残すか」で、これは「原本は残さない」という方針とぶつかる。そこで出てきたのが「図を AA(テキストの図)にすれば、文字しか残さない方針を崩さずに済む」という発想だった。AA と言っても罫線の AA ではなく、シーケンス図なら Mermaid の sequenceDiagram を狙う。VLM が出しやすく、Claude Code がそのまま描画できる。
試験: Qwen3-VL 4B / 8B に 7 枚の図を渡す
試験は次のように組んだ。
- 資料からシーケンス図を中心に 7 図を選ぶ(2 本の矢印しかない易しい図から、20 本以上の矢印に番号が付いた難しい図まで)
- YomiToku の図検出で枠を取り、切り抜く
- Ollama の Qwen3-VL 4B-instruct と 8B-instruct に、Mermaid を出すプロンプトと罫線 AA を出すプロンプトの 2 種類を投げる
- 出力を原図と目視で突き合わせ、矢印の向きと構造を採点する
- 打ち切り条件を先に決める: 矢印の向きが 9 割を超えなければ見送り
8GB の GPU では、OCR ワーカーが VRAM を 3.7GB 押さえていると 8B(5.8GB)は載らず、73% が CPU に落ちて事実上止まった。4B で先に結果を出し、8B はコンテナを再起動して VRAM を空けてから GPU 100% で流した。
結果はこうなった。
| 図 | 難易度 | 4B | 8B | 8B の内容 |
|---|---|---|---|---|
| 衝突時の初期設定シーケンス(矢印 4 本) | 易 | × | ◎ | 向きまで完全に一致。唯一の正解 |
| 閉塞シーケンス(矢印 2 本 × 2 図) | 易 | × | × | 信号名を「参加者」にしてしまう。失敗側の図が丸ごと欠落 |
| 4 局を経由する呼接続(矢印 24 本) | 中 | △ | △ | 6 種の信号連鎖のうち 4 つは正しい。導通試験音と呼出音の向きが逆 |
| 導通試験の 4 場面(小図 4 つ) | 中 | × | × | 最初の 1 つだけ出して残りを落とす |
| 誤り制御手順(番号付き矢印 20 本超) | 難 | × | × | 注釈文を矢印に読み替える。8B は同じ行を出力上限まで繰り返した |
| 通知シーケンス(概念図と併記) | 中 | × | × | 参加者は合うが矢印はでたらめ |
| 回線構成図 | 対象外 | × | × | シーケンス図ではない |
8B は 4B より速く(1 図 3〜25 秒)、構造もそれらしいのだが、矢印 4 本の図を完璧に描いた同じモデルが、矢印 2 本の図を落とす。全体の正解率が低いだけでなく、どの出力を信じてよいか事後に判別できないのが致命的だった。罫線 AA のプロンプトはさらに悪く、出力上限まで空行や罫線を吐き続ける暴走が 2 回あった。
打ち切り条件に照らして、図の Mermaid 化は見送りにした。事前に条件を決めておいたおかげで、「もう少しプロンプトを工夫すれば」という誘惑を切れた。
表はそのまま使える
一方で表は拍子抜けするほど素直だった。YomiToku はセルの行・列・結合情報を返すので、結合セルを左上に寄せたグリッドにして Markdown 表にするだけである。技術資料の 64 表が、2〜6 列・最大 15 行まで、列も行も原本どおりになった。長い文がセルの途中で切れる(セル検出側の問題)のと、図と表が隣接すると一つの図領域にまとめられて表にならない、という限界は残ったが、頻度は低い。
図については、VLM を使わない確実な部分だけ実装した。図の枠に一番近い「図 N」で始まる段落をキャプションとみなし、図中の文字(これは YomiToku が漏れなく拾う)と一緒に > 図: 図2.45 閉塞シーケンス > 図中の文字: ¥MKB / BLO / BLA / … という形で本文の後ろに置く。これで「閉塞の確認信号は何か」のような質問は該当ページに当たる。図の意味は引けないが、場所と語彙は確実に引ける。
転機: 文字だけから図が描けた
ここで予想外のことが起きた。別の Claude Code セッションで、この索引を MCP 経由で検索し、「呼接続の基本シーケンスを図で描いて」と頼んだところ、4 局を経由する 24 本の矢印のシーケンス図が、ほぼ原図どおりに出てきたのである。画像は一切渡していない。材料は図中の文字(参加者 4 局と信号名の並び)と、隣のページの本文(「アナログ回線に対して呼設定の前に導通試験を行う」「COT の送出条件は前位局からの COT と後位局からの試験音の AND」)だけだ。
8B の VLM が原図の画像を見て 6 割しか当てられなかった矢印を、Claude は画像なしで復元した。しかも「導通試験音はアナログ回線区間だけにあり、ディジタル区間では省略」という、図中の文字には書かれていない構造を本文から推論していた。
念のため、Claude Code(Anthropic API)ではなく Bedrock 上の Claude Sonnet 4.6 でも同じことを試した。Knowledge Base の検索結果 4 件をそのままプロンプトに入れて「抜粋だけを根拠に Mermaid で再構成せよ」と頼むと、10 秒、約 $0.03 で同じ図が出た。判断に迷った箇所はコメントで理由を書いてくる。

Bedrock 上の Claude Sonnet 4.6 が、Knowledge Base の検索結果(図中の文字と本文)だけから描いた呼接続シーケンス
気づいたのは単純なことで、技術資料の図は本文が説明している。図と本文は対になっていて、図の意味は本文が持っている。だから索引に要るのは「どの図がどこにあり、何が書かれているか」までで、描くのは Claude 側の仕事でよい。VLM に図を読ませようとしていたのは、この構造を見落としていたからだった。
逆に言えば、本文に説明のない図(フォーマット図や仕様書の構成図)は今の方法では復元できない。そこは「図だけが情報源」の資料が出てきたときに、切り抜きを MCP で見せる案 B を検討すればよい。
落とし穴: 図だけのページ
実装を配置して資料を登録し直したところ、OCR が 222 ページ終わった直後に Markdown の組み立てで落ちた。
IndexError: list index out of range
File "/app/core/ocr_worker.py", line 324, in build_markdown
if is_continuation(paras[0]):
原因はこうだ。パイプラインには「前のページが文の途中で終わっていたら、次のページの最初の段落と連結する」という処理がある。以前は本文のないページを丸ごと飛ばしていたので、連結相手の段落は必ず存在した。今回「図・表だけのページも通す」ようにしたことで、「文の途中で終わるページ」の次に「図だけのページ」が来る組み合わせが生まれ、空のリストの先頭を参照した。テストには図だけのページを入れていたが、その直前が文の途中で終わる組み合わせを見落としていた。
修正は 1 行で、先頭段落があるときだけ連結し、なければ保留していた段落をそのまま出す。再現テストを足して、OCR 済みの結果を再利用して組み立てだけやり直したところ、図 112・表 64 が入った Markdown が生成され、Knowledge Base に索引された。
まとめ
- ローカル VLM(Qwen3-VL 4B / 8B)で図を Mermaid 化する試験は、矢印の向きの正解率が打ち切り条件に届かず見送った。試験の前に打ち切り条件を決めておくと判断が早い
- 表は YomiToku の構造情報から Markdown 表にするだけで実用になる。図はキャプションと図中の文字を本文に埋め、場所と語彙を引けるようにする
- 図の意味は本文が持っている。Claude は文字だけから原図を再構成でき、それは Bedrock 上の Claude でも同じだった
- 「図・表だけのページ」を通すようにしたら、ページをまたぐ文の連結が空のリストを踏んだ。前提を変えたら、その前提に依存していた処理を疑う
次回は、この構成を Claude Code なしで Bedrock だけで回す「閉域構成」の話を書く。


コメント