每个月月底,开小店的和做自由职业的人都要干同一件苦差事:把一摞纸质账单摊在桌上,一条条敲进表格,好让会计去报税。慢是一回事,账单上印着GSTIN、银行账号、客户名字,把这些东西传到云端OCR服务上,怎么算都不划算。

打开网易新闻 查看精彩图片

BillBox就是为这个场景做的。作者说,他是为一个亲戚写的——那位亲戚抽屉里塞满纸质账单,每个季度末整包交给会计。工具跑在本地,账单不出机器。

它怎么工作

把账单照片或PDF丢进去,一个跑在你自己笔记本上的视觉模型逐张读取,抽出供应商、GSTIN、发票号、日期和金额。之后有一层校验逻辑核对算术,把可疑的地方标出来。被标记的字段可以在原图旁边手动改,确认后导出一份CSV,会计用Excel就能打开。

模型通过Ollama在本地运行,配置只有一行:qwen2.5vl:3b。换更大的模型或者微调过的模型,不用改应用本身。作者的笔记本只有8GB内存、没有GPU,所以选了3B这个规格——这也直接限制了准确率。

模型是"差不多对",不是"对"

第一次测试用的是一张电费单。模型返回了合法的JSON,但日期给成了04-07-2018,而不是2018-07-04;总额返回的是字符串"1031.00",不是数字。这两种东西表格都没法用。

于是他加了一步归一化:按日在前的方式转换日期,把"1,031.00"和"Rs. 1031"这类写法解析成数字,每种情况都配了单元测试。

小模型在数字上会出错,所以输出不再被当成事实直接展示。BillBox改成跑检查:GSTIN的格式和校验位、单价乘数量是否等于金额、小计加税是否等于总额、CGST/SGST与IGST是否对得上。对不上就把字段标红,配一句大白话说明。一张没有GSTIN的账单,工具会标出"缺少供应商GSTIN"并送进待复核,而不是猜一个填上。

上下文也有坑。一次请求里塞两张图,Ollama直接崩,报"请求超出可用上下文大小"。现在改成一次一张图,缩放到1280像素,并调大上下文窗口。

还有一个教训:不是每张账单都是发票。电费单没有明细行、没有GSTIN,那些GST检查会报出根本不存在的错误。现在字段不存在时,对应检查直接跳过。

实测结果

10张真实账单——打印发票、手机拍的照、一张电费单——在一台8GB内存、无GPU的Windows笔记本上跑:

  • 每张账单约55秒
  • 校验层抓出4个错误数值,这些原本会被漏掉
  • 表现最差的是模糊的手机照片,以及没印GSTIN的账单

作者把限制写得很直白:3B模型必须人工复核,输出只能当草稿;没有GPU的情况下每张大约一分钟;线上demo是模拟的,真正的抽取只在本地跑;导出的CSV是给会计打开或导入用的,不是现成的Tally导入文件。

下一步是三件事:用人们做的每一次修正去微调模型(每次修改都是一个标注样本),加Tally兼容导出,以及在更好的硬件上试更大的模型。技术栈是Qwen2.5-VL、Ollama、FastAPI、React和OpenCV。

这个项目的取舍挺值得琢磨:它没有追求"全自动",而是承认小模型会错,然后把力气花在让人更快发现错误上。对一张账单只值几毛钱收入的场景来说,这个方向可能比堆参数更实际。