# 账单识别与 Beancount 账户分类:合并 vs 解耦架构对比文档 在双式记账(Beancount / DriftLedger)离线移动客户端开发中,**“原始账单识别(Bill Recognition)”** 与 **“Beancount 账户分类(Account Classification)”** 是两个核心处理阶段。 本文档深度对比分析将这两个阶段进行 **合并(单步一体求值)** 与 **解耦(双阶段责任链)** 的架构差异,并结合 DriftLedger 源码实现,分别涵盖 **传统规则渠道(Rule-based)** 与 **大模型/AI 渠道(LLM/VLM)** 的表现。 --- ## 目录 1. [架构定义与处理模式](#一-架构定义与处理模式) 2. [传统规则渠道(Rule-based Channel)对比](#二-传统规则渠道-rule-based-channel-对比) 3. [大模型/AI 渠道(AI/LLM Channel)对比](#三-大模型ai-渠道-aillm-channel-对比) 4. [多维性能与工程指标全景对比表](#四-多维性能与工程指标全景对比表) 5. [DriftLedger (浮记) 混合架构落地推荐](#五-driftledger-浮记-混合架构落地推荐) --- ## 一、 架构定义与处理模式 在多通道系统架构中,无论采用合并还是解耦,**多通道原始内容采集(Ingestion Adapters)** 始终是前置的: - **通道 1:拍照/图库 OCR**(获取账单图像或 OCR Markdown 文本) - **通道 2:无障碍服务 UI 节点提取**(解析微信/支付宝支付成功页的 node 文本,参见 [accessibilityParser.ts](file:///C:/Users/fmq/Documents/work/DriftLedger/src/services/automation/accessibilityParser.ts)) - **通道 3:短信与系统通知监控**(解析银行/支付软件推送通知字符串) 合并与解耦的核心区别,在于拿到**账单原始内容 (Raw Bill Text)** 之后,**“事实字段识别”** 与 **“Beancount 账户分配”** 是在单次操作中完成,还是拆分为多个阶段: ```text ======================================================================================== 【多通道多源输入】 (通道A: 拍照OCR文本 / 通道B: 无障碍UI节点文本 / 通道C: 短信通知文本) │ ▼ 【合并架构 (Merged Mode / 一体化文本求值)】 单次求值 (AI Prompt 或 规则引擎): 输入: 账单原始内容 + 用户 Beancount 动态账户列表 输出: 直接一步得到【Beancount 交易草稿 TransactionDraft】(含时间、金额、商户、资金账户、支出账户) ======================================================================================== ======================================================================================== 【多通道多源输入】 (通道A: 拍照OCR文本 / 通道B: 无障碍UI节点文本 / 通道C: 短信通知文本) │ ▼ 【解耦架构 (Decoupled Mode / 责任链流水线)】 阶段 1(事实识别): 仅识别事实,输出无账户关联的【ImportedEvent】(时间、金额、商户、备注) │ ▼ 阶段 2(账户分类): 送入 BillPipeline,由规则引擎 RuleEngine 或轻量 AI 匹配 Beancount 账户 ======================================================================================== ``` --- ## 二、 传统规则渠道(Rule-based Channel)对比 在规则匹配渠道下,账户名均来自用户动态配置的规则库 (`Rule[]`),绝非代码硬编码: ### 1. 动态规则合并模式(Single-Pass Rule Evaluation / 一体化动态匹配) - **处理逻辑**: 拿到多通道的原始内容后,解析器直接调用用户配置的动态规则库 `Rule[]`。一条规则同时包含了**事实判定条件**与**双向账户分配**: ```typescript // 用户在 APP 中配置的动态规则对象(非代码硬编码) const rule: Rule = { id: "rule-101", counterpartyContains: "星巴克", sourceAccount: "Assets:Alipay:Balance", // 动态资金来源账户 categoryAccount: "Expenses:Food:Coffee", // 动态分类支出账户 narration: "星巴克咖啡" }; // 单次求值:一步构造出带有账户的完整 TransactionDraft 草稿 if (matches(rawText, rule)) { return createDraftDirectly(rawText, rule.sourceAccount, rule.categoryAccount); } ``` - **特点**: 单次求值即产生最终 `TransactionDraft`。如果入口预填了 `sourceAccount` / `categoryAccount`(参考 DriftLedger 代码 [billPipeline.ts:L169-L180](file:///C:/Users/fmq/Documents/work/DriftLedger/src/domain/pipeline/billPipeline.ts#L169-L180)),流水线直接接受该草稿。 ### 2. 规则解耦模式(Two-Stage Evaluation / 责任链流水线匹配) - **处理逻辑**: 1. **阶段 1 (事实化)**:所有通道(通知、无障碍、短信、账单 CSV)统一输出仅包含事实的 `ImportedEvent`(无账户关联)。 2. **阶段 2 (责任链评估)**:`BillPipeline` 依次执行:转账识别 ➔ 批次去重 ➔ 历史去重 ➔ 送入 `RuleEngine` 匹配规则库中的账户。 - **特点**: 解析器只需关心文本事实提取,账户分配归口给 `BillPipeline` 与 `RuleEngine` 统一调度。在分配账户前可先过滤重复事件,避免无效计算。 --- ## 三、 大模型/AI 渠道(AI/LLM Channel)对比 利用大语言模型(LLM)或多模态视觉模型(VLM)处理多通道获取的原始内容: ### 1. AI 文本级合并模式(单次 LLM 提示词一步直出草稿) - **处理逻辑**: 多通道(拍照 OCR / 无障碍节点文本 / 短信通知)提取到**账单原始内容字符串**后,**在单次 LLM 请求中**将“账单原始内容”与“用户本地 Beancount 候选账户列表”一同作为 Prompt 提交给大模型(如 `glm-4-flash-250414` 或 `Qwen2.5-7B-Instruct`)。 - **流程**: ```text [多通道原始内容文本] + [用户动态账户列表] ───(单次 LLM 求值)───> [Beancount TransactionDraft] ``` - **优势**: - **通道统一**:不管是图片 OCR、微信支付成功的节点文本、还是银行扣款短信,都使用同一种“文本级合并 Prompt”,直接返回填充好 `sourceAccount` 和 `categoryAccount` 的 JSON 草稿。 - **响应极快**:纯文本大模型(如 `glm-4-flash-250414`)处理文本合并请求耗时仅 **~2.2 秒**。 - **劣势**: - **Token 开销**:每次请求都需携带用户账户列表。 - **确定性风险**:可能偶发生成不存在的账户名。 ### 2. AI 解耦模式(内容识别提取事实 ➔ 独立分类器) - **处理逻辑**: 1. **阶段 1(事实识别)**:模型仅负责解析多通道原始内容,输出不含账户的 `ImportedEvent`(时间、金额、商户、备注)。 2. **阶段 2(账户分类)**:优先走本地 `RuleEngine`;若未命中,再调用轻量 LLM(耗时 ~2.2 秒)或向量 Embedding 模型(如 `bge-m3`,耗时 **0.3 秒**)在单独的 Prompt / 向量空间中挑选账户。 - **优势**: - **绝对确定性**:已知商户 100% 走本地规则引擎(0 延迟、0 错误率)。 - **离线优先 (Offline-first)**:本地识别出事实后,断网状态下本地规则引擎依然能完成账户分类。 --- ## 四、 多维性能与工程指标全景对比表 | 评估维度 | **规则合并模式 (单次一体匹配)** | **规则解耦模式 (责任链流水线)** | **AI 文本级合并模式 (单次LLM求值)** | **AI 解耦模式 (双阶段链式)** | | :--- | :--- | :--- | :--- | :--- | | **原始内容来源** | 多通道 (OCR/无障碍/短信) | 多通道 (OCR/无障碍/短信) | 多通道 (OCR/无障碍/短信) | 多通道 (OCR/无障碍/短信) | | **识别与分类点** | 文本解析时同步求值 | 分两阶段:解析事实 ➔ 匹配账户 | **单次 LLM 同时提取事实+账户** | 分两阶段:提取事实 ➔ LLM/向量选账户 | | **响应延迟 (Latency)** | **< 1ms** | **< 1ms** | **~2.2s** (`glm-4-flash-250414`) | **~4.3s** (OCR + 分类) | | **API 调用次数** | 0 次 | 0 次 | **1 次** | 1 ~ 2 次 | | **断网/离线鲁棒性** | 完全支持 | 完全支持 | 不支持 (需在线 LLM) | **半支持** (文本提取后离线规则分类) | | **准确率与确定性** | **100%** | **100%** | 高 (约 95%,受 Prompt 引导) | **100%** (已知规则优先覆写) | | **代码可维护性** | 良好 | **极佳** (领域分层极清) | 优秀 (通道无缝复用 Prompt) | **极佳** (管道可随意组装) | --- ## 五、 DriftLedger (浮记) 混合架构落地推荐 结合多通道采集(OCR / 无障碍 / 短信通知)与 DriftLedger 的代码实现 [billPipeline.ts](file:///C:/Users/fmq/Documents/work/DriftLedger/src/domain/pipeline/billPipeline.ts#L169-L180),推荐采取 **“多通道输入 ➔ AI 文本级合并直出 ➔ 本地流水线预填覆写”** 的最佳落地架构: ```text 【通道 A: 拍照 OCR 文本】 【通道 B: 无障碍 UI 文本】 【通道 C: 短信通知文本】 │ │ │ └────────────────────────────┼────────────────────────────┘ ▼ 【AI 文本级合并求值 (glm-4-flash-250414)】 单次 LLM 传入多通道文本 + 用户 Beancount 动态账户列表 2.2 秒内一步生成带预填账户的 `ImportedEvent` (含 sourceAccount/categoryAccount) │ ▼ 【领域核心层 / BillPipeline 责任链】 ┌────────────────────────────────┴────────────────────────────────┐ ▼ ▼ 【1: 本地规则覆写 (RuleEngine)】 【2: 多通道去重 (Dedup)】 若匹配到用户定义的 100% 精确 Rule 规则, 自动过滤历史账本已存在的重复交易, 本地规则强行覆写 AI 预测的账户,保障零差错。 避免多次拍照或无障碍重复记账。 ``` ### 总结: 1. **多通道采集(OCR / 无障碍 / 短信)** 是解耦的前置适配层。 2. **AI 合并模式** 指的是在获取到账单文本后,**用单次 LLM 请求同时完成事实提取与账户选择**(2.2 秒极速返回)。 3. **架构落地**:多通道文本输入 ➔ AI 文本级合并一步预填 ➔ 本地流水线校验覆写。