AI News HubLIVE
站内改写4 分钟阅读

管理小型本地AI预算(Mac M2 16GB)

本文介绍了millfolio如何通过混合标签系统高效处理本地AI推理:使用确定性字符串和引用标签覆盖大多数交易,仅对模糊尾部使用设备端AI标签。标签在索引时计算一次并存储,查询时不重复运行。回填使用批处理、去重和优先级调度器以避免过载。性能数据显示每个不同描述约650ms,有效行速8.5行/秒。系统包含预览机制,用户可在保存前验证标签效果。

来源Hacker News AI作者: winding

在上一篇博文中,我描述了millfolio的分层设计:前沿模型在别名化模式上编写小程序,而本地模型在设备上读取您的实际文件。最后我承诺会写一篇关于运行本地模型的无聊部分——以批处理模式运行,并设置优先级,以便您的笔记本电脑保持可用。这就是那篇博文。

问题:模型无法随记录扩展

数年的银行和信用卡对账单涉及数千笔交易。本地模型擅长阅读其中一笔——“这是订阅吗?”正是它擅长的针对性问题。但它无法做到的是,在用户提问时,在笔记本电脑GPU上对数千笔交易重复回答这个问题。即使每笔记录只需几分之一秒,像“去年我在订阅上花了多少钱?”这样的问题也会变成数分钟的推理——而下个问题又会重复所有工作。

因此,设计规则变成:模型的判断是一种计算一次并存储的东西,而不是在提问时运行的东西。

答案:三种标签

每笔交易在索引时获得标签,标签来自一个纯文本规则文件,您可以打开并编辑(categories.txt——每行一条规则,该文件是事实来源)。三种标签按成本排序:

字符串标签——便宜且常见。标签是大小写不敏感的子关键词列表,带有可选的排除项以否决匹配:

phone = verizon, at&t, t-mobile, mint mobile groceries = whole foods, trader joe, safeway, costco

纯字符串匹配,几乎零成本,覆盖了大部分真实消费历史——商户字符串具有重复性。

引用标签——由其他标签构建的标签。规则可以引用@标签名而不是关键词,因此您可以组合组而不重复任何人的关键词列表:

food = @groceries, @restaurant

仍然是确定性的且免费——当引用标签已存在于行上时,引用标签触发。引用循环是无害的(标签只会添加,因此评估收敛),引用可以指向AI标签,这使第三种标签也是可组合的。

AI标签——没有关键词列表能捕获的模糊尾部。规则是一个是/否问题,设备端模型是判断者:

subscriptions : 这是周期性订阅费用吗?

这是唯一需要推理的类型——这正是它作为存储标签而不是查询时调用的原因。

在提问时,所有三种标签看起来相同:生成的程序过滤.tags,对数千条记录的答案是字符串比较,而不是模型调用。还有一个隐私红利:前沿模型被告知标签名称和作用范围说明(因此它编写过滤它们的程序),但从未接触关键词——您的实际商户字符串留在设备上。

批处理,以及何时休眠

AI标签仍然需要计算一次,“一次”意味着对现有每条记录进行回填,加上之后到达的记录。这项工作通过一个单一的设备端工作编排器运行:一个磁盘支持的队列,重启后持续存在,一次处理一个作业——索引和AI标签回填永远不会争抢引擎,并且两者都向交互式问题让步。

在回填作业内部,分类是批处理并去重的:

多个描述在一个提示中发送给模型,它回答1:是, 2:否, …——一次推理调用分类整个切片。

在模型看到之前,精确重复的描述被折叠:每月出现24次的费用被分类一次,结果扩散出去。在真实对账单上,这节省了大部分工作(统计页面保持记录分类数与覆盖数的运行计数)。

一个小账本记录每个规则已实现的程度,以单调插入代键——因此完成的标签永远不会对旧记录重新运行;新记录增量处理。

在切片之间,回填器休眠——休眠长度就是优先级设置:高是100ms呼吸,中是1.2秒,低是5秒。这一个旋钮决定了机器是“快速完成”还是“我在工作,整晚都行”。您也可以无限期暂停后台处理。

速度如何?

以下统计数据通过运行mill run https://raw.githubusercontent.com/millfolio/vault/5915bf52f17b9baf2c4fe71695dae03e05a9ee16/privacy-box/eval/local_classifier_eval.mojo 在Mac M2 16GB上生成。(程序只打印聚合数据——没有商户字符串或金额——并可在任何millfolio安装上运行;它流式传输的实时进度行需要millfolio ≥ 0.4.49。)

原始输出如下:

模型:Qwen/Qwen2.5-3B-Instruct 问题:“对于每个片段:如果它是杂货店或超市购买(食品购物),回答‘是’;对于所有其他情况(外出就餐、餐厅、咖啡店以及任何非杂货费用),回答‘否’。每个答案必须恰好是‘是’或‘否’——绝不能是‘无’,也绝不能是其他。”对比关键词标签‘groceries’作为参考。

数据:2930笔交易行,988个不同描述(3.0倍去重);分类了400个(平衡:29个标记+371个未标记)覆盖2228行

ML(行级别,模型对比关键词规则): 一致性70.6% 精确率39.0% 召回率100.0% F1 56.2% 混淆矩阵:TP 419 FP 654 FN 0 TN 1155 基准率18.8% (关键词标签是不完美的真实——不一致是模型与规则之间的,不是经过认证的模型错误;样本是平衡的,因此基准率和一致性反映样本,而不是完整数据) 答案分布:111是·25否·264无·0其他

操作: 400个描述在大约40次模型调用中(批处理约10个/调用),总计260.9秒 1.5个不同描述/秒·8.5行/秒有效(去重扩散后)·每个不同描述约652ms

(关于答案分布行的说明:‘无’是批处理协议的‘不适用’出口——模型将其用作大多数行的否定。它被干净地解析并计为‘否’。)

‘groceries’关键词标签——一个精心挑选的超市品牌列表——是参考标签,Qwen2.5-3B-Instruct对相同记录回答等效的是/否问题。样本是故意平衡的(每个标记的描述,用未标记的填充),分类是每次模型调用批处理10个描述,重复费用像真实标签回填那样去重:2930笔交易行折叠成988个不同描述,在模型看到之前节省了3倍。

结果,在覆盖2228行的400个不同描述上:针对关键词规则的召回率100%,精确率39%(F1 56%)。召回率数字显示模型捕捉到了关键词列表标记为杂货的419行中的每一行,从未看到列表。精确率数字比看起来更有趣:654个“假阳性”是模型说杂货而我的十几个品牌关键词列表说无的行——这混合了真正的模型错误和我的列表不知道的杂货商。这种不对称是整个混合设计的关键论点:确定性规则给您精确、可审计的头部;模型给您尾部覆盖;预览UI存在是因为两者都不应盲目信任。

操作上:每个不同描述约650ms,每秒1.5个不同描述,去重后每个判决传播出去,有效每秒8.5行——因此一个2900行的数据在几分钟的后台时间内获得完整的AI处理,这正是回填调度器设计的预算。

信任,但要预览

标签系统只有在您对规则有信心时才有效,因此UI绝不要求您盲目保存。当您创建或编辑标签时,在持久化之前会进行模拟运行:

对于字符串或引用标签,编辑后的规则在存储的交易上评估,您会看到匹配计数以及匹配的描述示例——以及未匹配的示例——因此过于贪婪的关键词会立即显现。

对于AI标签,问题在时间限制的样本(约5秒的设备端模型)上运行,您会看到“约N条记录会匹配”,并排显示正面和负面示例。

看到两侧都很重要。假阳性从匹配列表中明显可见;假阴性只有在UI也显示规则之外的内容时才显现。直到您保存才写入任何内容。代码生成模型与您合作管理分类法——当生成程序必须内联分类某些内容时,它可以将问题作为可重用的AI标签提回,这样下次就是存储的标签过滤器,而不是新的推理。

总结

这些都不足为奇——这是每个数据系统最终都会发展出的批处理与在线分离。本地化的扭曲在于昂贵的资源不是云账单,而是您的GPU和前台工作,因此调度器的工作既是礼貌也是吞吐量。一次计算判断,将其存储为标签,仅将AI预算用于模糊尾部,并始终在规则执行前向用户展示其效果。

演示:demo.millfolio.app

工作原理:millfolio.app

代码:github.com/millfolio

← 返回博客