一張雪梨的收據,被記成泰銖
收據掃描有一個明顯的任務 ― 讀出總金額;還有一個沒人會想到的任務:判斷那個金額是「哪一種幣別」。前者出錯,使用者馬上就會發現;後者出錯,畫面上看起來一切正常。螢幕上的數字和紙上印的完全一樣,只是被歸成了另一種貨幣,然後悄悄地把你的帳目弄亂。
兩天之內,我找到五個這樣的錯誤。它們的成因完全相同,但直到把它們並排放在一起,我才看出來。
1. 終端機編號
一張日本便利商店的收據被記成加幣。那張收據上印著 小計 ¥380、税 ¥38、合計 ¥418 ― 日圓符號出現三次。同時也印著這一行:
端末番号:1CAD
那只是終端機編號。程式在找 ISO 幣別代碼時,在 1CAD 裡找到 CAD,還沒看到 ¥ 就先判定成加幣。當時的判斷會排除前面是「英文字母」的情況,卻沒有排除前面是「數字」。
2. ALDI 的稅別欄
兩張來自澳洲的收據 ― 一張是雪梨 Double Bay 的餐廳,一張是 Carindale 的 ALDI ― 被記成泰銖,大約差了二十三倍。
雪梨那張上有一行 ฿ $10.00。฿ 並不是幣別符號,而是文字辨識把某個英文字母看錯的結果。真正的金額就在旁邊,用美元符號印著。
ALDI 那張更精彩,那一行是:
1.39 ฿
在澳洲 ALDI 的收據上,這一欄的 B 代表該商品免 GST。程式把 B 讀成了 ฿,於是斷定這是泰國的收據。
兩張收據都印著 TAX INVOICE,一張有 ABN(澳洲商業號碼),一張有新南威爾斯州的郵遞區號。國別判斷的分數是澳洲 10 比 1 ― 卻輸給了一個變形的字元。
3. 菜單上的一個單字
接下來就更荒謬了。程式把印出來的 ISO 代碼當成很強的證據,用在 Total EUR 12.50 這種情況是合理的。問題是,其中有七個代碼同時也是普通的英文單字:
TRY OUR NEW MENU(歡迎試試新菜單)→ 土耳其里拉PLEASE TRY AGAIN(刷卡失敗的提示)→ 土耳其里拉MOP AND BUCKET SET(拖把水桶組)→ 澳門幣AED PADS REPLACEMENT(AED 電擊貼片)→ 阿聯迪拉姆RM 214(房號)→ 馬來西亞令吉Fr. 12 Sept(德文的「星期五」)→ 瑞士法郎
這些完全不需要辨識出錯,收據只是印了正常的英文而已。而且 AED、INR、CHF 同時也是醫療縮寫 ― 出現在一個會讀藥局收據的 App 裡。
4. 把「沒有」當成證據
香港的收據只印一個 $,沒有任何國別標示,所以大多被記成使用者手機設定的幣別。當時想到的辦法看起來很乾淨:繁體中文 + 有 $ + 沒有台灣的標示,那就是香港。這條規則命中 10 張香港收據中的 9 張,而其他 454 張都沒有誤判。
然後,一張真正的台灣收據出現了 ― 台灣大哥大的電信費收據:
實繳金額 $ 665
繁體中文、只有一個錢字號、沒有 NT$、沒有元、也不是統一發票。正好就是那條規則會判成「香港」的樣子。兩張真實收據,就足以推翻它。
5. 真正有效的做法
台灣的修正走的是相反的方向。台灣的商家收據一定會印 統編(統一編號)。在我們的測試集中,43 張台灣收據全部都有,而其他 430 張一張也沒有。這是「存在」的證據,當天就上線了。
共同的樣貌
這五個都是同一個錯誤:一個誤導性的字串,蓋過了累積起來的證據。終端機編號贏過三個日圓符號;一個看錯的字元贏過 ABN 和郵遞區號;「try」這個單字贏過一切。
能在真實紙張上存活的規則,都是建立在證據「存在」之上,而不是建立在證據「不存在」之上。統編留下來了,「沒有台灣標示所以是香港」則沒有。(ReceiptIQ 怎麼讀台灣與香港的收據,整理在這一頁。)
為什麼測試抓不到
這些都不是測試套件找出來的。我們每次改版都會驗證的 431 張收據,剛好沒有一張把 TRY 印成大寫;而那兩張澳洲收據,在公開資料集裡被分類在美國,從來沒有人把它們當成澳洲收據看過。
它們是把這些錯誤並排放好、問「它們長什麼樣子」之後才浮現的。測試集的角色在後面:每一項修正都在全部 431 張上量測過,而單字那項修正消除了六種失敗情境,同時一張收據的結果都沒有改變。
這是誠實的分工。測試告訴你某個改動安不安全,但不會告訴你哪裡有問題。要知道哪裡有問題,還是得去看收據。
ReceiptIQ 每次改版前,都會用來自十個國家的 431 張收據做驗證——其中 391 張是真實收據的照片,另外 40 張是模擬台灣統一發票的合成資料。上述修正都在發現的同一週內上線。