space ocr
指南文章價格文件

用 space ocr Claude 技能把文件轉成可查詢的結構化資料

一份從零寫起的繁體中文指南:安裝 space ocr 這個 Claude Code 外掛技能,設定 API 金鑰,批次處理整個資料夾的文件,並從存好的資料列查詢結果。

space ocr 是一個 Claude 技能(skill),以 Claude Code 外掛的形式發佈。安裝之後,Claude Code 就多了一條把文件影像轉成結構化欄位的途徑——發票、收據、名片、證件、表單,都會被讀成一組組帶有明確語意的鍵值,而不是一段你還得自己解析的純文字。

它和兩種常見做法不同。第一種是「貼一張圖、期待模型自己看懂」:模型確實能描述影像,但它讀出來的數字沒有來源、沒有座標,也無從查證,整批跑起來既不可重現也難以稽核。第二種是「自己架一套 OCR 加資料庫」:你得選 OCR 引擎、定義資料表、寫上傳與查詢的程式、再維護一個檢索層。space ocr 把這兩端都收斂掉——它在自己的伺服器上做辨識,回傳的每個值都帶頁面上的確切位置,而且 API 本身就是儲存層:資料夾、工作表、資料列都在 API 後面,你不必另外建資料庫或向量庫。

執行層面刻意保持簡單。整個技能只透過一支僅依賴標準函式庫的 Python 腳本 scripts/space_ocr.py 運作——沒有 pip 套件、沒有 MCP server、沒有 SDK,只需要系統裡有 python3。所有指令的輸出都是 stdout 上的 JSON,方便程式串接或讓 Claude 接著處理。

安裝是一次性的。在 Claude Code 裡用兩個 slash 指令把 marketplace 加進來、再安裝這個技能即可;之後 Claude Code 在需要時就會自行呼叫它。

1
2
/plugin marketplace add oisidonut/claude-space-ocr-skill
/plugin install space-ocr@space-ocr

安裝完成後要做的設定只有一件:準備 API 金鑰。到 https://space-ocr.com → Settings → API Keys 申請,新帳號附帶 100 次免費掃描,不需要信用卡

金鑰透過環境變數 SPACE_OCR_API_KEY=spocr_... 提供,或在專案根目錄放一個 .env 檔。腳本對 API 的驗證方式是 Authorization: Bearer spocr_...。基底網址預設為 https://api.space-ocr.com,需要時可用 SPACE_OCR_API_BASE 覆寫。設好之後,用一行 balance 確認金鑰可用、看看剩多少額度——這也是技能的習慣動作:批次處理前先查餘額。

1
2
export SPACE_OCR_API_KEY=spocr_xxx
python3 scripts/space_ocr.py balance

先從單一文件看辨識結果長什麼樣子。ocr 指令吃一張影像(可以是檔案路徑、URL 或 base64),搭配 --template 指定內建範本,就會回傳該文件型別預期的欄位。內建範本涵蓋常見證照與商業單據:invoicereceiptbusiness_cardpurchase_orderdeliveryquotebankbookpassportdriver_licenseresident_cardmy_number_cardresidence_card 等。

如果你的文件不在內建清單裡,有兩條路:用 --fields <schema.json> 自訂欄位結構,或用 --auto 讓引擎自行推斷 4 到 8 個欄位。--auto 帶有拒絕閘門——遇到非結構化、空白或橫向拍歪的照片,它會回傳錯誤、而不是憑空編造欄位,而且這次掃描會自動退還。每個回傳值都附帶它在頁面上被讀出來的位置,稍後查詢一節會說明這點為何重要。

1
python3 scripts/space_ocr.py ocr invoice.jpg --template invoice

超過一份文件,就不該把原始 OCR JSON 一直貼回對話,而是寫進一張工作表。流程分兩步。先用 create sheet 定義欄位、建立工作表;--columns columns.json 一次把欄位結構說清楚。接著用 upload 把整批影像丟進那張表,辨識在伺服器端非同步進行,加上 --wait 會阻塞到全部跑完(否則可用 job <JOB_ID> 輪詢狀態)。

這裡有一個必須記住的路徑陷阱:資料夾用名稱定位(例如 /invoices),但工作表或備忘錄要用 create 回傳的 uniqueKey 路徑定位,而不是它的顯示名稱。一張名為「March」的工作表實際住在類似 /invoices/8G90wq... 的位置,不是 /invoices/March。所以 create 給回來的那個 path 要留著,後續 uploadviewqueryedit 全部沿用它;若之後忘了,就 view/space 它的父資料夾、讀出對應項目的 path

1
2
3
4
5
6
# 1) define the columns once, then create the sheet
python3 scripts/space_ocr.py create sheet /invoices "March" --columns columns.json
# -> returns a path like /invoices/8G90wq...  (reuse this uniqueKey path)

# 2) drop every image in; OCR runs server-side (async), --wait blocks until done
python3 scripts/space_ocr.py upload /invoices/8G90wq... *.jpg --wait

影像進到工作表後,它們就成了可查詢的資料列——回答問題時不必再重新辨識。用 query <sheet> 拉資料列,並把篩選工作交給伺服器:--where 過濾、--sort 排序、--limit 限制筆數。這幾個條件相當於你的 SELECT,而且讀取不花掃描額度——只有「跑一次 OCR」或「上傳一張影像」各計 1 次掃描,檢視與查詢都是 0。

所以面對「哪一家廠商開得最多?」這類問題,正確做法是查已存的資料列、在其上推理,而不是把來源檔重新打開、重新 OCR。表很大時,更要把工作推到伺服器端(像下面這樣 --where 'total>=40000' --sort total:desc --limit 20),只把需要的列帶進上下文,而不是把整張表拖回來。

1
2
python3 scripts/space_ocr.py query /invoices/8G90wq... \
  --where 'total>=40000' --sort total:desc --limit 20

這個技能之所以又省又可信,靠的是四條預設行為準則:

  1. 存,不要倒(Store, don't dump) — 處理超過一份文件後,把結果寫進工作表,而不是把原始 OCR JSON 貼回對話。重的資料留在 API 後面,不佔上下文。真正一次性的單張文件,直接 ocr 就好。
  2. 掃描前先查(Check before you scan) — 批次前先 balance,確認額度夠;與其重新掃描,先 space/view 看它是不是已經是某張表裡的一列,能重用就重用。
  3. 從存好的列回答(Answer from stored rows) — 用 query <sheet> 搭配伺服器端的 --where/--sort/--limit;讀取免費,不要重新 OCR。
  4. 標註位置、標示不確定(Cite the location, flag uncertainty) — 每個值都帶 field_bboxes 位置;優先逐字擷取(照印出來的原樣抄,不要正規化日期數字或自行計算合計),否則重排或衍生出來的值無法錨定到頁面,定位框會漂移。

關於額度還有兩個實用細節:失敗的掃描會自動退還;/ocr/fields 與上傳都接受 Idempotency-Key——用同一把 key 重試,會重播快取結果、不會重複扣款。balance 顯示的可用總額是 free.remaining 加上 flatfee 與 paid balance。

✓ Verified

每個值都帶可查證的頁面位置。 回傳的不只是文字:每個欄位都附一個四點定位框,而且這個框是重新對齊到 Vision API 實際辨識到的符號上,不是 LLM 的猜測,座標落在 0–1000 的正規化網格。因此每個值都能被引用與核對——網頁儀表板會把這些框畫在原始文件上,讓你用肉眼逐一驗證。前提是擷取要逐字:一旦把值正規化或重新計算,框就無法對應到頁面,定位也跟著失準。

下面是把一個資料夾的發票批次匯入成可查詢工作表的最短路徑。

相關