なぜ space-ocr は他の LLM OCR と違うのか:検証できる構造化抽出
LLM に直接 OCR をさせる場合との違い。space-ocr では抽出した値ごとにページ上で検証済みのボックスと match_ratio が付き、そのままクエリできる 1 行として保存される。
領収書や請求書を GPT-4o、Gemini、Claude に渡して、合計金額・取引先・明細を読み取ってもらうことはできる。たいていはそれらしい JSON が返ってくる。問題が出てくるのは、これを大量処理で信頼しようとしたときだ。モデルが返すのは文字列であり、文字列には所在がない。合計が 48,200 と返ってきたとして、それはページのどのピクセルを読んだ結果なのか。その数字は実際に書類へ印字されていたのか、それともモデルがもっともらしい値を埋めただけなのか。LLM を素のまま呼び出すだけでは、自分でページを読み直さないかぎりこれには答えられない。
この差こそ、汎用 LLM を OCR ツールとして使うことと、space-ocr を使うことの違いのすべてだ。space-ocr は LLM を否定するものではない。内部では OCR エンジン(Google Cloud Vision)と、構造化を担う Gemini を組み合わせている。space-ocr が付け加えているのは、モデルの周りに組んだレイヤーだ。返すすべての値を、OCR エンジンが実際にページ上で認識したものと照合し、スコアを付け、クエリできる 1 行として保存する。検証できる値ごとの出所と、データベースを立てずにクエリできる構造化された出力。この 2 つこそ、素の LLM 呼び出しでは自分で用意しなければならない部分だ。
素の LLM OCR と space-ocr
| 素の LLM OCR(GPT-4o / Gemini / Claude) | space-ocr | |
|---|---|---|
| 値ごとの位置 | なし。テキストしか得られない | すべての値に、ボックス(0–1000 グリッド上の xmin, ymin, xmax, ymax)と 4 点の傾き対応クアッドが付く |
| 値ごとの検証 | なし。文字列を信じるしかない | match_ratio:値の文字のうち何割がページ上で検出されたシンボルの中に見つかったか。bbox_source ラベル付き |
| 文脈での値の確認 | 自分で書類を読み直す | アプリでセルをクリックすると、元画像上でその領域が正確にハイライトされる |
| 出力の形 | プロンプト任せで、実行のたびに変わる JSON | 固定スキーマ。fields を定義(または templateId を渡す)すれば、アップロードごとに 1 行として揃う |
| 保存とクエリ | 自分で作る | 結果はシートの行になり、GET /view(where, sort, select, limit, offset)でクエリできる。OCR の再実行なし、課金なし |
| 文字種 | モデルとプロンプト次第 | 日本語・韓国語・中国語・英語ほかを自動判定。言語パラメータは不要 |
| セットアップ | プロンプト・リトライ・パース・検証のパイプラインを自作 | Bearer キー付きの HTTPS 呼び出し 1 回 |
「検証済み」が実際に何を意味するのかをはっきりさせておきたい。誇張しやすいところだからだ。言語モデルは座標を出力しない。返すのは各値のテキストと単語トークンのヒントだけで、そこからエンジンが、そのテキストを Google Cloud Vision がページ上で検出したシンボルと 1 文字ずつ照合する。実在するシンボルの上にボックスを合わせ、値に match_ratio(値の文字のうち、ページのシンボルの中に見つかった割合)でスコアを付ける。match_ratio が高ければ、値の文字の大半がページのシンボルと一致したことを意味し、低ければ bbox_source を通じてラベル付けされるので、人の確認に回せる。これはモデルが決して間違えないという約束ではない。トークンのヒントはずれることがある。意味するのは、各値を鵜呑みにするのではなく、ページと照合してスコアを付けているということだ。
{
"vendor": {
"value": "ACME Trading Co.",
"bbox": { "xmin": 120, "ymin": 84, "xmax": 512, "ymax": 118 },
"vertices": [
{ "x": 120, "y": 84 }, { "x": 512, "y": 84 },
{ "x": 512, "y": 118 }, { "x": 120, "y": 118 }
],
"match_ratio": 1.0,
"bbox_source": "vision_symbol_match"
}
}ボックスは画像サイズに依存しない 0–1000 グリッド上で返るので、描画するときはピクセルにスケールする:pixel_x = xmin / 1000 * image_width。4 点のクアッドは傾いたり回転したりしたスキャンに追従し、左上・右上・右下・左下の順で並ぶ。素のモデルの応答にはこれが一切ないため、監査証跡が欲しければ自分で組み立てることになる。
値からクエリできるテーブルへ
素の LLM 呼び出しは JSON で終わる。それを自分で永続化しなければならず、「今四半期で 40,000 を超える請求書はどれか」と尋ねたくなった時点で、まずデータベースとクエリ層を作ることになる。space-ocr では結果がすでに固定スキーマのシート上の 1 行になっているので、クエリは API 呼び出しで済む:GET /view に where=total>=40000、sort=-invoice_date、select=vendor,total、それにページングの limit と offset を渡す。処理はサーバー側で走り、OCR を再実行せず、課金もされない。シートは CSV にエクスポートできる(BOM 付き UTF-8 なので、日本語・韓国語・中国語のテキストや通貨も Excel で正しく開き、明細の配列はそれぞれ独立した行に展開される)。
素の LLM のほうが向いている場面
汎用 LLM が正しい選択になるのは、一度きりの読み取りや、ざっくりした要約、書類の意味を推論したいときだ。「この契約は何についてのものか」はモデル向きの問いであって、座標付き OCR の問いではない。space-ocr を持ち出すのは、書類を大量に処理していて、各値が検証でき、一貫して構造化され、クエリできる必要があるときだ。買掛金処理の自動化、経費の突き合わせ、名刺の CRM への取り込み、たまった領収書のデジタル化などがこれにあたる。正直に位置づけるなら、space-ocr は検証と保存のレイヤーを備えた LLM ベースの OCR であって、モデルそのものの競合ではない。
料金はスキャンごとの従量課金で、任意で月額プランも選べる。毎月一定数の無料スキャンが付き、スキャンが空で返ったときは課金されない。現在の金額は料金ページに載せている。