具備稽核軌跡的文件 OCR
大多數 OCR 只丟給你一堆得照單全收的文字。space-ocr 會為每個數值附上出處:data.cells[path] 中的 box 與 quad 座標、支撐這次比對的 evidence,而需要人工過目的路徑則彙整在 data.review.flagged 裡。
從文件中擷取資料,做個展示很容易,要讓人信得過卻很難。一個模型讀了一張發票,回傳 total: 2,045,於是你面對一個任何信心分數都無法真正回答的問題:這到底是頁面上實際印著的數字,還是模型自己生出來的? 如果只是臨時查一筆資料,這倒無所謂。但換成會計、理賠處理、法遵合規,或任何你日後得接受稽核的場景,「相信模型就對了」根本稱不上是一種控管。
稽核軌跡正好補上這一塊。每個欄位回傳的不再只是一個光禿禿的數值,而是附帶一個已驗證的頁面位置——這樣一來,人(或另一套系統)就能直接跳到該數值被讀取出來的那組像素加以確認。這正是「一個答案」與「一個你能站得住腳的答案」之間的差別。
親眼看看:每個數值都回溯得到源頭
把游標移到下方任一欄位上。收據上那個框,就是該數值被讀取出來的位置——每個欄位也都帶著與這處位置對應的查核狀態。

Each value with a box carries a verified on-page location — in data.cells[path], that is box + 4-point quad + evidence.match_ratio — on a 0–1000 normalized grid (0,0 top-left → 1000,1000 bottom-right), the same shape the live API returns. Hover a field to trace it back to the pixels it came from.
一個「已驗證的數值」實際帶著什麼
站得住腳的結果,不是一個數值外加一個分數。POST /ocr/fields 會把答案拆成幾個層次,每一層都能分別儲存、查詢與引用:
data.values— 讀到了什麼。就是你請求的那套結構,不摻任何保留鍵,可以直接寫進資料庫。data.cells[path].box與.quad— 從哪裡讀到的。box是與座標軸對齊的矩形{ xmin, ymin, xmax, ymax },落在 0–1000 正規化的網格上(0,0 = 左上角,1000,1000 = 右下角);quad是四個有序頂點,由於系統從不做傾斜校正,它會一路跟著頁面的傾斜角度。路徑語法全程一致:total、items[0].price。data.cells[path].evidence— 憑什麼這樣認定。text_match就是字元比對本身,source說明座標是怎麼定出來的,match_ratio是該數值的字元中在頁面上定位到的比例(≥ 0.85 視為高信心比對成功),printed_text則是 OCR 在那組座標上讀到的字元,可用來與values做精確字串比對。data.cells[path].verified與.review— 能不能不經人工就直接採用。verified是一個判定,而不是字元分數:只要review帶著任何理由就是false;跑過查核且沒有任何標記時是true;沒有任何標記、但根本沒有可比對的對象時則是null— 例如整列的合併框只有幾何資訊。review.reasons一律是陣列,依排序給出,第 0 項是主要理由。data.review.flagged— 留給人看的待辦清單。每一項都是一組{ path, reasons },要覆核的筆數就是flagged.length。data.normalized— 把頁面上的寫法和拿來計算的值分開。只有在某個欄位宣告了純量型別(或帶pattern/enum的 string)時才會出現,它把同一份讀值解析成該型別,而完全不動到values。
因為位置與證據是跟著數值一起回傳的,結果就不再是個黑盒子。你可以把框畫出來、引用路徑與座標,或重新查核某個被標記的欄位,完全不必再跑一次 OCR。
這些座標並不是直接採信模型講的。 語言模型會回傳每個數值的文字——以及它用到了哪些 word token 的提示——但從不回傳框本身。接著引擎會拿那段文字,去和視覺 OCR 在頁面上實際偵測到的符號做字元比對,讓框落在那些字元真正被找到的像素上,並為每個數值算出一個比對比例:它的字元中實際被定位到的占比。模型給的 token 提示可能有雜訊——在重複的列之間有時還會張冠李戴——所以引擎會用欄與列的一致性檢查去驗證它們,而不是盲目採信。重點不在於 AI 不會出錯,而在於與頁面對不上的數值會被擺到待覆核清單上,而不是悄悄放行。遇到根本沒有比對對象的項目——整列的合併框只有幾何資訊——該格會如實回報 verified: null,而不是謊稱通過。
點一下數值,直接落在像素上
在 App 裡,這變成了一種互動:點任一格,原始圖片就會把該數值所來自的那個框精準地標亮出來,還附上放大裁切與一條連接線。要抽查一整批資料,這是最快的方式——你的視線直接落到那個點上,而不必把整份文件從頭掃一遍。
修改紀錄同樣可稽核
稽核軌跡不只關乎機器的輸出——也關乎人到底改了什麼。當你編輯一格時,space-ocr 會把你的修改與原始 OCR 數值分開保存。Original 提示框永遠會顯示引擎一開始讀到的內容,讓審核者能把機器的數值與人工覆寫的值並排對照。
它就在 API 裡,附在每個數值上
這並不是只有 UI 才有的功能。POST /ocr/fields 回傳的 data.cells 是一份以路徑為鍵的扁平對照表(total、items[0].price),每一項都帶著 box、quad、verified、review 與 evidence。data.review.flagged[].path 用的是同一套路徑語法,所以從一筆待覆核的項目可以直接查到它自己的座標。當你用 GET /view 查詢已儲存的試算表時,這份對照表預設就會一起帶回來——加上 boxes=0 只會拿掉列裡的 cells 對照表,values、review 與 image 照樣回傳。
{
"status": "success",
"data": {
"values": {
"total": "2,045",
"items": [
{ "qty": "2", "price": "780" }
]
},
"cells": {
"total": {
"box": { "xmin": 595, "ymin": 974, "xmax": 781, "ymax": 1000 },
"quad": [
{ "x": 594, "y": 975 }, { "x": 781, "y": 972 },
{ "x": 781, "y": 998 }, { "x": 595, "y": 1000 }
],
"verified": true,
"review": null,
"evidence": {
"text_match": true,
"source": "vision_symbol_match",
"match_ratio": 1.0,
"printed_text": "2,045"
}
},
"items[0].price": {
"box": { "xmin": 693, "ymin": 640, "xmax": 781, "ymax": 668 },
"quad": [
{ "x": 693, "y": 641 }, { "x": 781, "y": 640 },
{ "x": 781, "y": 667 }, { "x": 693, "y": 668 }
],
"verified": false,
"review": { "reasons": ["text_mismatch"] },
"evidence": {
"text_match": false,
"source": "vision_symbol_match",
"match_ratio": 0.67,
"printed_text": "180"
}
}
},
"review": {
"unit": "field",
"flagged": [
{ "path": "items[0].price", "reasons": ["text_mismatch"] }
],
"by_reason": { "text_mismatch": 1 }
},
"normalized": { "total": 2045 },
"image": { "width": 1654, "height": 2339 }
}
}evidence.source 會告訴你每個座標是怎麼定出來的——vision_symbol_match 是常見的字元比對路徑(會帶著它真正的 match_ratio),token_id 表示用到了 word-token 提示。這是一份你可以記錄、過濾,或呈現給審核者看的中繼資料(metadata)。比對薄弱的情況並不會藏在這個鍵裡:它會以 low_ratio、weak_source、low_ocr_confidence 這類代碼出現在 review.reasons 中,同一條路徑也會列在 data.review.flagged 裡。這些理由代碼屬於 API 合約詞彙——請依代碼分支處理,並為目前還不認得的代碼留一則通用訊息。
實務上如何驗證一個數值
- 打開擷取結果打開試算表,或呼叫 GET /view——每個數值都由一條路徑定址,data.cells[path] 帶著它的 box、quad、review 與 evidence。
- 點一下該數值點該格,就能在原始圖片上標亮它被讀取出來的確切區域。
- 讀取證據與待覆核清單match_ratio 為 1.0 表示每個字元都被定位到了,≥ 0.85 即視為高信心比對成功。引擎無法定論的數值,會連同 low_ratio、text_mismatch 之類的理由一起列在 data.review.flagged 裡。
- 必要時加以修正編輯該格即可覆寫它——為了稽核軌跡,原始的 OCR 數值會被保留在 Original 提示框底下。