[控场AI]
· 7 分钟阅读· 3,903 字

用Gemma本地识别巴拿马小票,自动生成税务表格

用Gemma本地识别巴拿马小票,自动生成税务表格

开源项目Cuadre用本地Gemma模型转录巴拿马小票,代码负责计算与校验,实现隐私友好的ITBMS抵扣自动化。

Cuadre是一个开源项目,旨在自动化巴拿马自由职业者和小企业纸质小票的税务整理工作。用户只需将收据照片放入文件夹并运行一条命令,本地Gemma模型便会转录每张小票的商家、RUC、日期、小计和ITBMS税额,代码随后完成计算、校验与汇总,输出可导入Excel的CSV、按月分类的汇总报告以及需人工复核的清单。核心设计原则是“模型只转录,代码做计算”:JSON Schema约束模型输出,Decimal运算避免舍入误差,RUC格式检查、重复检测和日期合法性校验兜底模型错误。整个流程在本地运行,数据不出机器,成本仅为电费,尤其适合每月处理约3000张小票的会计师。项目目前仅在六张虚构小票上验证,真实场景和付费意愿尚待检验。

在巴拿马,自由职业者和小企业每买一次东西,几乎都会拿到一张纸质小票。上面印着商家的税号(RUC)和一行ITBMS——巴拿马的销售税,大部分商品7%,酒店10%,烟草15%。这些在经营采购中付出的ITBMS,通常可以用来抵扣每月应缴的税额(具体哪些采购符合条件由会计师决定)。但要想抵扣,就得有人在月底坐下来,把每一张小票敲进电子表格。一鞋盒的热敏纸,变成一行行商家、RUC、日期、小计、税额和总计。

干这件事的人,通常是晚上11点的自由职业者本人,或者他们的会计师——后者收到的是来自30个不同客户的模糊WhatsApp照片。

一个叫Cuadre的开源项目尝试用本地运行的Gemma模型把这件事自动化。它的做法很直接:把收据照片放进一个文件夹,运行一条命令,得到三样东西——一份可用Excel直接打开的recibos.csv(每张小票一行)、一份按月份和类别汇总的resumen.md(含已付ITBMS总额),以及一份需要人工复核的小票清单,每条都附上原因。

照片由Gemma在作者自己的笔记本电脑上读取,数据不离开机器。这个项目面向三类人:替小企业客户记账的会计师(他们每月手工转录小票,且不能把客户数据交给不受控的云服务)、每月申报ITBMS并想拿回采购已缴税款的自由职业者,以及把收据攒在盒子里等会计师来要的小店。

Cuadre项目来源截图

演示:六张虚构小票里的陷阱

为了证明系统能处理真实世界的混乱,作者用六张巴拿马虚构小票做了演示,每张都埋了一个坑:一张含有免税食品的超市小票、一张税额需要四舍五入的Fonda午餐、一张文具店小票、一张按10%征税的酒店小票、同一张文具店小票被拍了两次,以及一张没有RUC也没有税额行的手写小卖部收据。

实际输出中,系统算出已付ITBMS为B/. 10.98,与手工计算完全一致。超市小票的税额只占小计的3.5%,系统没有当作错误,而是提示“可能含有免税商品”——这个判断符合巴拿马的实际情况。酒店小票上,Gemma截断了RUC的末尾,代码检测到结果不符合巴拿马RUC的格式,自动将这张小票标记为“待复核”。重复的照片被识别出来,没有重复计入。手写小卖部收据则因为缺少税额行和RUC,被正常标记为异常。

最终交给会计师的CSV包含文件名、日期、商家、RUC、校验位、发票号、类别、小计、税额、总计、状态和警告等字段。状态为“ok”的行可以直接使用,“revisar”(待复核)的行则附上具体原因,比如“RUC «155612233-2»不符合巴拿马RUC格式”。

设计原则:模型只负责转录,代码负责计算

整个系统运行时只依赖Python标准库,自带42个测试,并用照片哈希做缓存——下周新增小票再跑一次时,只有新照片会被读取。

核心原则只有一条:模型转录,代码计算。小型视觉模型读印刷文字很在行,但算术不可靠,而税务报告不能包含编造的数字。所以Gemma只做一件事:照抄印在纸上的内容。其余全部由普通代码完成。

具体分工是:Gemma 4 E2B通过Ollama在本地读取照片,这是唯一需要视觉模型的步骤;输出结构由Ollama的format字段里的JSON Schema约束,Gemma无法返回多余字段或编造类别;金额处理交给monto_de_linea()函数,Gemma复制整行,代码从中分离税率(7%)和金额(0.83);日期由fecha()处理,遵循巴拿马日在前面的写法;求和、税率校验、RUC格式、日期合法性和重复检测由validar.py完成,可疑小票进入“待复核”而不是被悄悄计入;总计由informe.py用Decimal运算,避免舍入漂移,并排除重复项。

Gemma以temperature: 0运行且关闭思考模式——转录不需要推理,同一张照片永远得到同样的读取结果。在一台16GB内存、没有GPU的笔记本上,一张小票耗时6到27秒。

JSON Schema 约束输出

Ollama 的 format 字段允许调用方传入一个 JSON Schema,服务端在解码阶段就把模型的输出限制为符合该结构的合法 JSON。这与“在提示词里请求 JSON”有本质区别:后者只是软约束,模型仍可能附加解释文字、遗漏字段或返回非法转义;前者通过约束解码(constrained decoding)在采样时屏蔽不符合语法或结构的 token,使输出在格式层面几乎不可能出错。Cuadre 借此确保 Gemma 只能返回预先定义的字段,无法自创类别或塞入多余内容。需要说明的是,Schema 约束的是结构而非语义正确性——字段齐全、类型正确,但值仍可能抄错,这正是后续 validar.py 校验环节存在的理由。

Decimal 与浮点舍入漂移

Python 的 float 遵循 IEEE 754 双精度浮点,无法精确表示 0.1、0.83 这类十进制小数,累加时会产生微小误差。单张小票看不出来,但一个月三千张、每张含多个金额字段时,误差会累积,导致汇总的 ITBMS 总额与手工或税局系统对不上,而税务申报不允许这种“差不多”。decimal.Decimal 以十进制方式存储数值并支持可控舍入模式,适合货币计算。Cuadre 在 informe.py 中用 Decimal 做求和,配合排除重复项,确保汇总结果与逐张手算完全一致——演示中 B/. 10.98 的精确匹配就是这一设计的直接体现。

测试教给作者的三件事

第一版在纸面上看起来没问题。放到真实图片上跑,Gemma暴露了三个问题,每一个都对应一次代码调整。

在itbms字段里,Gemma在全部六张小票上都复制了标签"ITBMS 7%"而不是金额。代码不会编造数字,所以六张全部变成“不平衡”。修复方式是让Gemma返回完整的印刷行(ITBMS 7% 0.83),由代码提取最后一个金额、忽略百分比。

酒店小票上,Gemma丢掉了RUC的最后一部分。模型这边没什么可修的——RUC格式检查抓住了它。这正是设置检查的意义。

手写小卖部收据上,Gemma把日期10/10/26放进了小计字段,代码把它读成了B/. 26.00。修复规则是:形如日期的行永远不是金额;如果日期缺失且出现在其他字段中,代码从那里恢复它。

这些调整之后,运行结果稳定了:ITBMS总额精确、重复项被排除、免税商品被识别、每一张可疑小票都被标记待复核。作者保留了一个有意的限制:Cuadre只检查RUC的形状,不校验校验位,因为尚未验证税务局的算法。系统会明说这一点,而不是假装能查。

为什么这里需要开源模型

小票是个人数据。它透露一个人在哪里购物、买了什么、什么时候、花了多少。自由职业者的小票还会暴露客户是谁。对会计师来说,把那些照片上传到云API,等于把客户的数据交给第三方。用Ollama跑Gemma,Cuadre唯一的网络连接是localhost:11434,代码不接受任何其他地址。

成本结构也要求本地化。一个有30个客户、每客户约100张小票的会计师,每月要读大约3000张小票。用本地模型,这只需要笔记本电脑的电费。没有按张计费、随客户增长而膨胀的成本,也没有月底——恰好是所有人申报的时候——用完的配额。

作者还强调了不可被锁定的价值:如果供应商涨价或下架模型,Cuadre照常工作,模型在磁盘上,换模型只需一个参数(内存更大的机器上可以用--modelo gemma4:e4b)。而且因为运行时开放,作者可以用schema约束输出、关闭思考、固定温度,并精确看到Gemma如何失败,然后针对每种失败构建检查。下一步甚至可以用匿名化的巴拿马小票微调Gemma,让它少截断RUC。

作者也坦承了取舍:大型托管模型大概会犯更少的读取错误。但对这个任务来说,隐私和近乎为零的每张小票成本更重要,而且小模型的错误恰好是代码能抓住的那类。

ITBMS 与进项抵扣

ITBMS(Impuesto de Transferencia de Bienemuebles y Servicios)是巴拿马对动产转让和服务征收的增值税式流转税,一般税率为7%,酒店住宿10%,烟草等特定商品15%。与许多增值税体系类似,企业在经营采购中支付的ITBMS属于进项税,可以在申报时抵扣销项税,只就差额缴纳税款。这正是Cuadre存在的经济动机:每一张被正确转录的小票都对应一笔可抵扣的进项税,漏记或错记都意味着真金白银的损失。但抵扣资格取决于采购是否与经营活动相关,巴拿马税法对此有具体规定,因此文章强调“具体哪些采购符合条件由会计师决定”——工具负责把数字算准,合规判断仍留给专业人士。

下一步

目前Cuadre只在六张虚构小票上测试过。接下来的计划是在真实小票上运行(不公开图片)、拿给会计师看他们是否真的愿意付费,以及开源代码库。

值得说明的是,作者用Claude Code(Claude Opus 5.5)作为编码代理完成了这个项目——它实现了代码、测试和文档,生成了演示小票,并根据测试记录起草了文章。作者选择了想法和要检查的巴拿马税务规则,并审查了结果。

分享:

相关推荐