space ocr
指南文章價格文件

space-ocr 與 LLM OCR 有何不同:可驗證、結構化的擷取

space-ocr 與直接請 LLM 做 OCR 的差別:欄位會連同頁面上的來源座標與 verified 判定一起回傳,需要複核的項目則列在 review 裡。

9 分鐘閱讀· 2026-08-31

你可以把一張收據或發票丟給 GPT-4o、Gemini 或 Claude,請它回報總金額、廠商與明細項目。多數情況下,你會拿到一份看起來合理的 JSON。麻煩是在你要大量信任這些結果時才開始:模型回傳的是一串字串,而字串沒有位址。如果總金額回來是 48,200,它讀的是頁面上哪些像素?那個數字究竟是印在文件上,還是模型自己補了一個看似合理的值?單靠一次原始的 LLM 呼叫,你沒辦法回答這些問題,除非自己再把整頁重讀一遍。

這道落差,就是「把通用 LLM 當 OCR 工具」和「用 space-ocr」之間的全部差別。space-ocr 並不反對 LLM。它目前在底層把一個 OCR 引擎(Google Cloud Vision)搭配 Gemini 來做結構化——這是實作細節,而不是 API 契約的一部分。它多出來的,是圍繞模型建立的那一層:回傳的值都會拿去和 OCR 實際在頁面上看到的內容核對、給出判定,上傳到試算表之後再存成一列你可以查詢的資料。這正是原始 LLM 呼叫留給你自己處理的兩件事:可以驗證的逐值來源,以及不必自建資料庫就能查詢的結構化輸出。

原始 LLM OCR 與 space-ocr 的對照

原始 LLM OCR (GPT-4o / Gemini / Claude)space-ocr
逐值位置一次 JSON 擷取呼叫回傳的是文字,與獨立 OCR 判讀交叉核對過的來源座標並不在契約裡凡是能定位的值都附一個位置框(xmin、ymin、xmax、ymax,建立在 0–1000 的格線上)與一個四點方向四邊形;定位不到的值會以 nobox 出現在 review.flagged 裡
逐值驗證沒有,你只能信任那串字串判定在 cells[path].verified,原因在 review.reasons;核對本身放在 evidence 裡——text_match、source,以及 match_ratio,也就是該值有多少字元能在頁面偵測到的符號中被找到
宣告的規則驗證邏輯要自己寫required、pattern、enum、min/max、near 與 not_near 隨請求一起宣告,由伺服器端判定,違規會落到 review.flagged
在情境中核對某個值自己把文件重讀一遍在應用程式裡點一格,它在原圖上的精確區域就會亮起
輸出形狀依提示而定、每次執行都不一樣的 JSON固定的結構:定義一次 fields,或讓 autoFields 提議一份,data.values 就照這個形狀回傳
儲存與查詢要自己建POST /ocr/fields 只在回應裡回傳,不保存影像;上傳到試算表後,每一頁落成一列,用 GET /view 查詢(where、sort、select、limit、offset),不重跑 OCR,也不收費
文字語系看模型與提示而定日文、韓文、中文、英文等自動偵測,沒有語言參數
建置自己的一整條提示、重試、解析與驗證流程一次 HTTPS 呼叫,帶一把 Bearer 金鑰
✓ Verified

「已驗證」到底是什麼意思,值得說清楚,因為這件事很容易被誇大。語言模型並不會產生座標。它回傳的是每個值的文字,加上一些字詞 token 的提示;引擎接著把這段文字逐字元和 Google Cloud Vision 在頁面上偵測到的符號比對。它把位置框落在那些真實的符號上,而核對的結果會寫進 cells[path].evidence:逐字元核對本身是 text_match,該值的字元有多少比例被找到是 match_ratio,位置框怎麼找到的是 source。verified 是疊在上面的判定:只要這一格帶上任何一條原因就是 false,核對跑過而沒有任何原因就是 true,根本沒有可核對的對象則是 null。比對偏弱會立起 low_ratio 之類的原因,那條 path 隨即出現在 review.flagged 裡,這就是你轉給人工處理的清單。values 是模型讀出來的字串,不是逐位元組的副本,所以當你需要拿去和主檔做精確比對時,請用 evidence.printed_text——OCR 在那組座標上讀到的字形。這並不是在保證模型永遠不會出錯:token 提示仍可能偏移,兩個引擎也可能在同一處誤讀上取得一致。它的意思是:每個值都會拿去和頁面核對並給出結論,而不是照單全收。

POST /ocr/fields — 回應中的一個欄位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
  "status": "success",
  "data": {
    "values": {
      "vendor": "ACME Trading Co."
    },
    "cells": {
      "vendor": {
        "box": { "xmin": 120, "ymin": 84, "xmax": 512, "ymax": 118 },
        "quad": [
          { "x": 120, "y": 84 }, { "x": 512, "y": 84 },
          { "x": 512, "y": 118 }, { "x": 120, "y": 118 }
        ],
        "verified": true,
        "review": null,
        "evidence": {
          "text_match": true,
          "source": "vision_symbol_match",
          "match_ratio": 1.0,
          "ocr_confidence": 0.97
        }
      }
    },
    "review": {
      "unit": "field",
      "declared": 1,
      "returned": 1,
      "boxed": 1,
      "verified": 1,
      "flagged": []
    },
    "image": { "width": 1654, "height": 2339 }
  }
}

位置框是建立在與影像尺寸無關的 0–1000 格線上,所以繪製時你要把它換算回像素:pixel_x = xmin / 1000 * image_width。寬與高請取自同一份回應裡的 data.image——那是實際被判讀的那一頁(EXIF 旋轉已經固化,大張照片也已經縮小過),而不是你送出的那個檔案。那個四點四邊形會跟著傾斜或旋轉的掃描一起走,順序是左上、右上、右下、左下。一旦有對不上的地方,同一條 path 就會以 { "path": "total", "reasons": ["text_mismatch"] } 的形式出現在 data.review.flagged 裡,交給複核的人是一份可以逐條走完的清單,而不是一個還要自己決定門檻的分數。對通用模型發出的 JSON 擷取呼叫不會附帶這些,所以你想要的任何稽核軌跡,都得自己手動拼出來。

從一個個值,到一張可查詢的表

原始的 LLM 呼叫到 JSON 就結束了。你還得自己把它存下來,而當你想問「這一季有哪些發票超過 40,000?」的那一刻,你得先蓋一座資料庫和一層查詢介面。POST /ocr/fields 同樣到 JSON 為止——它只在回應裡回傳,不保留影像。不同的是,同一次擷取可以走儲存層:先照你的欄位建一張試算表(POST /create),再把頁面傳進去(POST /upload),每一頁就成為一列,帶著同樣的 values、cells 與 review。之後查詢就是一次 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,而不是要跟模型本身較勁的對手。

計費是按掃描張數的用多少付多少,另有可選的月方案與每月一定額度的免費掃描,而且失敗的掃描不收費。目前的實際金額請見定價頁。

space-ocr 會取代 GPT-4o 或 Gemini 來做 OCR 嗎?
不會。space-ocr 底層本來就用 LLM,目前是以 Google Cloud Vision 負責 OCR、Gemini 負責結構化——這是實作細節,不是 API 契約。差別在圍繞模型的那一層:每個值都會被對回頁面上的符號並給出判定,上傳到試算表後,結果再存成一列可查詢的資料。它是一套 LLM 驅動的 OCR,而不是模型的替代品。
我怎麼知道某個值不是模型憑空捏造的?
看三個地方。cells[path].verified 是判定:帶上任何一條原因就是 false,核對跑過且沒有任何原因是 true,沒有可核對的對象是 null。data.review.flagged 是值得複核的 path 清單,每一條都帶 reasons。cells[path].evidence 裡放的是核對本身(text_match、match_ratio、source),但這些鍵只會出現在核對真的跑過的值上;鍵不在,意思是「無法判斷」,而不是「沒問題」。這是拿真實頁面來核對,而不是保證模型永遠不會出錯——兩個引擎也可能在同一處誤讀上取得一致。
space-ocr 回傳的座標是什麼格式?
一個由四個整數構成的位置框(xmin、ymin、xmax、ymax),建立在正規化的 0–1000 格線上,另外對傾斜或旋轉的頁面附一個四點方向四邊形(quad),頂點順序是左上、右上、右下、左下。換算成像素時以同一份回應裡的 data.image 為準,例如 pixel_x = xmin / 1000 * image_width。
不用自己的資料庫,也能查詢擷取出的資料嗎?
可以,前提是頁面已經進到試算表裡。POST /ocr/fields 只回傳 JSON,什麼都不保存;先建一張試算表(POST /create),再把頁面傳上去(POST /upload),每一頁就成為一列。之後 GET /view 會在伺服器端用 where、sort、select、limit 與 offset 篩選(例如 where=total>=40000)。它不會重跑 OCR,也不收費,而且你可以把試算表匯出成 CSV。
我需要告訴 space-ocr 文件是什麼語言嗎?
不用。語言會在日文、韓文、中文、英文等之間自動偵測,沒有任何語言參數需要設定。
它能讀 PDF 嗎?
網頁應用程式會把每一頁 PDF 點陣化成影像後再做 OCR。API 本身接受點陣影像(JPEG、PNG、GIF、BMP、TIFF、WebP),一次呼叫一張影像,所以送出前請先把 PDF 各頁轉成影像。
我要怎麼呼叫它?
對 POST /ocr/fields 送出一次 HTTPS 請求,帶上一把 Bearer spocr_ 金鑰,同時傳入影像與一份 fields 結構(也可以用 autoFields: true 讓它提議一份)。回應把兩件事分開:data.values 裡只有你宣告的結構,data.cells[path] 裡放著每個值的 box、quad、verified、review 與 evidence,data.review 則彙整了需要複核的項目。