一張報價單上有七個欄位要抄。第八件事,是驗算它有沒有抄錯。
供應商報價單 / 訂單 PDF → 結構化欄位 → 用文件自身的算術恆等式反查辨識結果。
現在
導入後
這裡的「7 個欄位」與「2 道恆等式」是這條管線實際的欄位數與檢查數,不是效能宣稱。
這一頁不會出現任何「節省 X% 時間」或「準確率 X%」的數字——我們沒有量過,就不寫。
09:00 — 收到報價單 PDF
現在
信件附檔下載下來。第一件事是判斷它是什麼:有文字層的 PDF、還是掃描件轉的圖?
是掃描件的話,多半得先請對方重寄,或是自己重新掃一次。有時候乾脆印出來,用手抄。
這一步不產生任何價值,但它每天都要做一次。
導入後
上傳。系統自己看這份 PDF 的文字層覆蓋率,決定走哪一條路:
- 有文字層 → 主路徑(Azure Document Intelligence
prebuilt-layout) - 純影像、無文字層 → OCR 路徑(
mistral-ocr-latest轉 markdown,再交給 GPT-4o 讀欄位)
你不需要知道這一份是哪一種。這是輸入型態路由,是決定性程式碼在判斷,不是模型在猜。
10:30 — 逐張 key 入系統
現在
一列一列抄。品名、規格、數量、單位、單價、金額、備註——七個欄位。
抄到一半會遇到這些:
- 日期寫
114/06/23,ERP 只吃西元,心算換成2025/06/23 - 單位這張寫
m2、上一張寫㎡、再上一張寫「平方米」,統一成同一個 - 一格塞了兩個貨號(
AB-1203 / AB-1208),要自己判斷拆成兩列還是留一列 - 這張沒印小計,只有每列金額和一個總計,得自己按計算機把小計補出來
這四件事沒有一件困難。困難的是它們每天發生幾十次,而且每一次都是新的手動判斷。
導入後
同樣這四件事,交給後處理正規化層。這一層完全沒有模型參與,是可以逐項寫單元測試的決定性程式碼:
| 髒資料 | 處理 |
|---|---|
114/06/23 | 民國年 → 西元 |
m2 / ㎡ / 平方米 | 單位正規化為 m² |
AB-1203 / AB-1208 | 多貨號拆解成獨立列 |
| 缺小計 | 依明細自動加總補上 |
| 欄名同義詞(單價 / 單價(未稅)/ Unit Price) | 同義詞對映到同一欄 |
這些規則綁台灣單據的習慣。對台灣市場這是加分,不是限制。
14:00 — 發現數字對不起來
這是全頁最重要的一段。
現在
總計欄位跟你剛剛加出來的數字差了幾百塊。
接下來可能是:從第一列開始重新核對一遍;或是打電話問供應商是不是他們印錯;或是——最常見的——先照著紙上的總計 key 進去,反正供應商印的應該不會錯。
第三種做法會把錯誤放進系統,而且沒有留下任何痕跡。月底對帳時,你會看到一個對不起來的數字,但已經不知道是哪一張、哪一列造成的。
導入後
這件事在辨識當下就發生了,不是在月底。
抽取完成後,結果先過數學自洽仲裁——兩條純算術的恆等式:
subtotal + tax == grand_total
Σ(line_total) == subtotal這兩條不需要 ground truth,不需要人工標註,也不需要另一個模型來評分。文件自己就帶著答案。
對不上時的處置有順序:
- 判定是抽取問題 → 換另一條辨識路徑重跑(主路徑 ↔ 自研回退路徑)
- 重跑後仍對不上 → 這份文件本身可能就是錯的,或版型超出處理範圍
- → 標記,交給人。不猜、不靜默採用、不自動修正。
17:00 — 月底對帳
現在
資料散在三個地方:ERP 裡 key 過的、Excel 裡自己記的、以及那疊還沒 key 的紙本。
要出一份給客戶的報價單時,複製貼上重排一次。要出 PDF 時,中文變成一排問號或方框——這是 PDF 產生器沒有內嵌中文字型的典型症狀,而且通常要到寄出去之後才會有人告訴你。
導入後
辨識結果一次匯出兩種格式:
- XLSX — 給內部繼續加工
- PDF — 給客戶,中文字型(NotoSansTC)已內嵌
中文字型內嵌這件事聽起來不像功能,但它是這條產線上踩過的一個具體的坑:常用的 PDF 產生函式庫預設不支援中文,要另外掛字型工具包並把字檔內嵌進去。這個坑我們踩完了。
中場:這條管線實際上長什麼樣
先把最容易被誤會的一件事講清楚:
主辨識路徑用的是微軟的 Azure Document Intelligence(prebuilt-layout),那不是 AMAX 自研的模型。
AMAX 自研的是它旁邊那三層:回退路徑、路由層、仲裁層。這三層決定了「什麼時候該相信辨識結果」,而這正是把一個辨識 API 變成一個可交付產品的差距所在。
三層路由
| 層 | 判斷依據 | 決定什麼 |
|---|---|---|
| ① 輸入型態 | PDF 文字層覆蓋率 vs 純影像 | 走 Azure DI,還是走 OCR 路徑 |
| ② runtime 健康度 | 主路徑逾時 / 異常 | 是否回退到自研管線 |
| ③ 結果自洽性 | 兩條算術恆等式 | 這次結果可不可信;不可信就換路徑重跑 |
自研回退路徑(兩階段)
主路徑不可用時走的是自研 Hybrid 管線,設計成兩階段:
- Q1 — Gemini 抽表頭與合計區(供應商、單號、日期、小計、稅、總計)
- Q2 — GPT 逐頁讀明細七欄,並且逐欄給出信心度(per-column confidence),不是整份給一個分數
逐欄信心度的用處很實際:整份 0.85 沒有可操作性;「單價欄 0.42」可以直接變成人工複核佇列。
失效處置的細節(技術方最愛問的一題)
雙 key failover 只在 401 / 403 觸發。
主路徑用長時作業輪詢(2 秒間隔、90 秒上限)。逾時的時候不換 key,直接走回退路徑。
原因:逾時是服務端負載問題,換一把憑證解決不了它,而且換 key 會讓你在監控上把一次逾時記成一次憑證失效——下次真的憑證出問題時,你分不出來。憑證問題用憑證的辦法處理,逾時用逾時的辦法處理。
這一層與文件型態無關。如果你手上有的是另一種「模型會抽錯、而且抽錯不會報錯」的場景,那是另一個產品頁的主題(AI 上線護欄)。
支援哪些單據類型
已驗證(有評測樣本)
| 類型 | 說明 |
|---|---|
| 供應商報價單 | 主要驗證對象 |
| 採購訂單 | 主要驗證對象 |
評測集規模:18 個訂單樣本。這個數字很小,我們不假裝它很大。
結構相近,需要一次語料校準
| 類型 | 為什麼相近 | 需要調什麼 |
|---|---|---|
| 發票 | 同為「表頭 + 明細 + 合計」結構,合計恆等式直接適用 | 欄名同義詞字典 |
| 送貨單 | 同結構,但通常無金額欄 | 恆等式改為數量核對 |
| 對帳單 | 同結構,多期別維度 | 表頭欄位擴充 |
明確不在範圍
| 類型 | 原因 |
|---|---|
| 手寫單據 | 未驗證。手寫的抽取失敗率與印刷完全不同,而且自洽仲裁對「整份都讀錯」無效 |
| 無合計欄的自由格式文件 | 兩條恆等式都用不上,等於少了核心護欄 |
| 合約 / 條款類長文件 | 這是另一種問題,不是欄位抽取 |
什麼情況它會交還給人
這一區寫得比上面任何一區都直白,因為它決定你導入之後會不會被它坑。
① 兩條恆等式對不上,且換路徑重跑後仍對不上。
標記後交人。系統不會為了讓數字好看而自動補差額。
② 某一欄的信心度偏低。
逐欄信心度會跟著結果一起出來。你可以自己定門檻,把低於門檻的欄位排進複核佇列。
③ 整份文件一致地抄錯。
這種情況自洽仲裁抓不到——因為錯誤本身是自洽的。這是這條管線的已知盲區,只有人看得出來。
④ 版型超出已驗證範圍。
評測集只有 18 個訂單樣本。你的供應商如果用一套很特別的版型,第一次跑的結果需要人看過。
⑤ 手寫欄位。
未驗證,見上一區。
這個產品的主張不是「它不會錯」。
是「它錯的時候,你會在當下就知道,而不是在月底」。
拿三張你手上最難讀的單據來
最好包含一張掃描件、一張民國年日期、一張合計對不起來的。
我們跑一次,把每一欄的信心度與仲裁結果攤開給你看——包含它失敗的那幾張。
實測不需要提供你的 ERP 存取權限。單據檔案即可。