构建 AI 驱动的账单分摊工具:OCR、LLMs 与实时状态
Source: Dev.to
1. OCR 管道:从像素到原始文本
构建像 Hackbill 这样的工具的首要挑战是将可能模糊、光线不足的手机照片转换为机器可读的文本。传统的光学字符识别(OCR)技术已经取得了长足进展,但收据具有独特的难点:字体多变、纸张皱折以及复杂的布局(数量、名称和价格的列)。
预处理步骤
在送入 OCR 引擎之前,通常需要通过一系列处理来提升准确率:
- 灰度转换: 去除颜色噪声。
- 透视校正: 使用边缘检测(例如 Canny)找到收据的四个角点,并执行四点透视变换。
- 自适应阈值化: 处理纸张上不均匀的光照。
选择引擎
- Tesseract: 开源标准。
- 基于云的解决方案(AWS Textract、Google Cloud Vision): 对多列布局提供更好的结果,因为它们返回“块”和“表单”,而不仅仅是原始字符串。
2. 使用 LLM 进行结构化数据提取
原始 OCR 输出往往是一堆无结构的字符串。例如,一行可能显示为 1 BU RGER $15 .00。基于正则表达式的传统解析非常脆弱,因为每个 POS 系统的收据格式都不相同。
这正是大型语言模型(LLM),如 GPT‑4o 或 Claude 3.5 Sonnet 发挥作用的地方。与其编写上百行正则表达式,不如将原始 OCR 文本传给 LLM,并使用系统提示让其返回符合 JSON 架构的结果。
示例实现(Node.js)
const extractReceiptItems = async (rawText) => {
const prompt = `
Extract the items, quantities, and prices from this receipt text.
Return ONLY a JSON array of objects with keys: name, quantity, price.
Text: "${rawText}"
`;
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
response_format: { type: "json_object" },
});
return JSON.parse(response.choices[0].message.content);
};
通过使用 AI 驱动的扫描,Hackbill 能够智能地识别哪些行是商品条目,哪些是元数据(例如日期或服务员姓名),从而显著减少用户的“审查”阶段。
3. 实时协作与状态管理
一旦收据被扫描并提取出商品,接下来的技术难点就是“共享与认领”阶段。从开发者的视角来看,这是一道分布式状态问题。如果三个人同时查看同一张账单,我们必须确保不会出现两个人同时认领同一瓶啤酒的情况。
实时同步的技术栈
要实现 Hackbill 工作流中提到的“实时查看谁在认领什么”功能,通常有三种选择:
- WebSockets (Socket.io): 适用于低延迟、双向通信。
- Server‑Sent Events (SSE): 适合单向更新(服务器 → 客户端)。
- 实时数据库 (Supabase/Firebase): 对于快速开发最为高效,因为它们已经内置了发布/订阅逻辑。
冲突处理
当用户点击 “Claim” 时,客户端应在后台验证请求的同时,对 UI 进行乐观更新。如果数据库中该商品已经被另一个 user_id 认领,后端会拒绝该事务,UI 随即回滚。
4. 公平小费分配的数学
Hackbill 哲学中最具创新性的功能之一是 Fair Tip Distribution(公平小费分配)。大多数人只是把小费平均分配,但这在技术上并不公平。如果我点了一份 5 美元的沙拉,而你点了一份 50 美元的牛排,那么把 10 美元的小费平均分配就意味着我相对于我的消费支付了过多。
算法
Hackbill 确保只有声明了项目的人才支付他们应付的小费份额。其数学计算方式为加权百分比:
-
计算小计: 所有已声明项目的总和。
-
计算用户小计: 用户 A 所声明项目的总和。
-
计算比例:
User A Proportion = User A Subtotal / Total Subtotal -
应用小费/税费:
User A Total = User A Subtotal + (Total Tip * User A Proportion) + (Total Tax * User A Proportion)
在后端实现此逻辑可确保最终的“Claim”(声明)金额在数学上是合理的,并且在社交上也没有摩擦。
5. 安全性与隐私考虑
作为开发者,我们必须考虑收据数据的敏感性。收据通常包含信用卡的后四位数字、商家的地址以及用餐习惯。
- 数据最小化: 仅存储商品名称和价格。成功解析后丢弃原始图像。
- 短期会话: 使用基于 UUID 的唯一 URL 分享账单,并在设定时间后失效。
- 加密: 确保所有传输中的数据通过 HTTPS 处理,且个人可识别信息(PII)在静止时加密。
结论:用代码解决社交摩擦
构建像 Hackbill 这样的工具是将多种现代技术——计算机视觉、自然语言处理和实时网络系统——相结合,以解决日常人类问题的典范。
对于想要构建类似应用的开发者,关键点很明确:
- 不要用僵硬的正则表达式去对抗现实世界的非结构化特性。 采用大型语言模型进行数据提取,使用实时同步实现协作,并始终确保你的数学处理诸如加权小费分配等边缘情况。
准备好摆脱手动计算了吗? 访问 Hackbill,亲眼见证这些技术原理的实际应用,并简化你下一次的团体聚餐。
开发者关键要点
- OCR 是起点,而非终点: 使用大语言模型(LLM)来结构化 OCR 产生的混乱数据。
- 实时性不可妥协: 使用 WebSockets 或 Supabase 实现无缝的“认领”体验。
- 加权计算 > 简单计算: 始终基于比例小计来计算份额,以确保公平。
祝编码愉快,愿你的下一张午餐账单像这段 Markdown 一样清晰!
