公司动态
【寻迹校园 HarmonyOS NEXT 实战 21】可解释失物匹配算法:不用大模型也能给出分数、理由与冲突
【寻迹校园 HarmonyOS NEXT 实战 21】可解释失物匹配算法不用大模型也能给出分数、理由与冲突这是“寻迹校园 HarmonyOS NEXT 实战”系列第 21 篇。本文结合项目中ReportService.getMatchBundle()与scoreCandidate()的真实代码拆解一个不依赖网络和大模型的校园失物匹配器先筛选合法候选再按分类、地点、名称和日期计算信息相似分同时输出相似理由与待确认冲突。上图为原创生成的技术插画不是项目截图。它表达的不是“AI 猜中失主”而是一条可审查的数据路径同一份公开信息同时产生分数、正向理由和冲突项用户可以知道候选为什么排在前面也能看到哪些事实仍需线下核验。一、失物匹配首先是信息排序不是所有权判定校园失物招领最容易犯的错误是把“看起来很像”直接写成“这是你的物品”。类别、地点、日期和公开特征只能说明两条记录在信息上接近无法代替只有真正失主才知道的私密细节更不能替代安全交接。因此“寻迹校园”的匹配器只回答三个问题哪些记录有资格成为候选哪些候选的公开信息更接近查询记录每个分数由哪些相似点和冲突组成。它不会自动提交认领不会改变报告状态也不会给出所有权概率。匹配结果只是进入详情和核验流程的入口。二、先建立候选门禁再谈分数当前实现先从 Repository 读取权威报告列表然后执行四个前置条件排除查询记录自身、只保留相反发布类型、只保留OPEN或CLAIMING状态、最后才进入评分。constcandidates:MatchCandidate[]reports.filter((report:ItemReport)report.id!query.idreport.reportType!query.reportType(report.statusReportStatus.OPEN||report.statusReportStatus.CLAIMING)).map((report:ItemReport)this.scoreCandidate(query,report)).sort((left:MatchCandidate,right:MatchCandidate)right.score-left.score).slice(0,3);这个顺序很重要。若先给所有记录算分再在页面临时隐藏同类型、撤回或结案记录非法候选仍可能进入缓存、日志或 AI 摘要。门禁必须位于业务 Service而不是依赖某个页面的显示条件。三、当前评分不是复杂模型而是一组可复核规则scoreCandidate()从基础分 24 开始逐项增加权重维度条件加分同时输出分类完全一致34物品分类一致地点完全一致24地点一致地点一方文本包含另一方16地点相近名称标题与分类互相命中或双方都含“双肩包”14名称特征相近日期同一天10事件日期一致日期相差一天4事件日期相近最终分数用Math.min(score, 96)截断。上限不是数学概率而是产品层主动避免显示“100% 确认”的误导设计。四、为什么需要基础分基础分让通过门禁的对向开放记录拥有一个非零起点避免页面出现大量0分候选。但基础分也意味着分数不能直接解释为“命中了多少特征”。例如分类不一致、地点不同、日期相差较大的一条记录仍可能保留 24 分同时带有三条冲突。这并不矛盾分数用于排序冲突用于解释风险。若未来希望设置最低召回阈值应根据真实标注数据验证而不能因为基础分是 24 就随意把阈值写成 25。阈值会直接影响漏召回与误召回需要单独的产品和数据决策。五、正向理由与冲突必须同时输出当前模型MatchCandidate同时保存report候选报告score信息相似分reasons相似理由conflicts待确认冲突。如果只显示分数82 分和 61 分之间的差异无法解释如果只显示相似点用户容易忽略地点或日期矛盾如果只显示冲突候选排序又失去价值。三者组合后页面可以表达为候选信息相似分相似点待确认A82分类一致、地点相近、日期一致公开特征仍需核验B61分类一致、名称相近发现地点不同C48地点相近分类不同、日期差异较大这些数值是用来说明界面表达的示例不是当前数据库中某三条真实记录的固定结果。上图展示同一候选的三种输出分数决定相对位置理由说明加分来源冲突阻止用户把排序误读成归属结论。六、冲突不是扣分的另一种写法当前代码对分类不同、地点不同和日期相差超过七天记录冲突但没有直接减分。这是一种有意简化基础分和命中项决定排序冲突作为独立解释通道。这种设计有两个好处。第一冲突内容不会被总分吞掉第二后续可以单独调整冲突提示不必同步改变排序。它也有局限一个冲突很多的候选可能仍因高权重维度获得较高分。正式产品可考虑硬冲突门禁、负权重或置信区间但必须用真实错配案例验证不能凭直觉不断叠规则。七、地点的“包含关系”只能是近似策略当前地点相近使用query.area.includes(candidate.area) || candidate.area.includes(query.area)。它能处理“图书馆”和“图书馆一层”这类文本但无法理解“东区食堂”和“一食堂”可能指同一地点也可能把过短词语误判为包含。较稳妥的升级路线是新建记录只写入标准地点词表历史文本保留并显式迁移对别名建立受控映射精确地点只用于私密核验不进入公开匹配无法归一化时回退到文本包含策略。这比直接接入地图距离更符合校园失物场景因为公开地点往往本就应该模糊化。八、名称规则为什么只做小范围特征当前名称相近包含两个通用方向标题命中对方分类以及双方都包含“双肩包”。它不是完整分词器也不理解同义词。这种小规则的价值是行为稳定、可测试、离线可用风险是项目扩展到雨伞、耳机、证件等类别后硬编码会持续增长。更好的演进方式不是在if后面无限追加物品名而是把可公开的标准特征整理为词表或策略对象并为每条词表维护来源、版本和回归用例。九、日期距离必须先验证日期评分器调用dateDistanceInDays()前会验证YYYY-MM-DD格式、真实年月日和不能晚于今天。无效日期返回-1不会得到日期加分也不会被误写成“相差较大”。这样能区分两类情况有可靠日期且距离超过七天明确冲突日期缺失或非法证据不足不应假装存在冲突。“没有证据”和“证据相反”必须是两个语义。否则用户会把数据质量问题误解为物品不匹配。十、分数上限 96 的产品含义即使所有规则都命中界面也不应该显示 100%。100% 很容易被理解成身份确认或所有权认证而当前系统没有人证、物证、账号真实性和线下交接证据。96 仍然只是当前规则集合中的上限不具有统计学意义。更保险的界面还应使用“信息相似分”并在候选页固定展示分数只反映公开信息相似程度不代表物品归属请通过私密特征和校内安全交接完成核验。这段文案属于业务安全边界不能为了界面简洁而删除。十一、页面只渲染评分规则留在 ServiceMatchResultsPage负责 loading、error、empty、候选卡片和进入详情。它不重新计算分数也不自行拼理由。这种边界让多个入口得到一致结果普通候选页、小艺摘要和后续测试都通过ReportService.getMatchBundle()使用同一份候选集合。若页面各自实现筛选或排序迟早会出现“原生页是候选 A小艺摘要却是候选 B”的分叉。工程调用链应保持ArkUI Page → ReportService → ReportRepository → RelationalStore / 本地回退候选是权威报告的派生结果不是新的长期业务实体。十二、如何测试可解释匹配当前本地状态机用例固定验证查询记录存在、候选都是相反类型、数量不超过 3、每个候选至少有一条理由、分数按降序排列。powershell-ExecutionPolicy Bypass-File.\scripts\test-local-state-machines.ps1还应继续补充以下用例同分候选顺序、地点短文本误包含、无效日期、完全冲突、没有候选、状态从OPEN变成RESOLVED后立即失效以及分类词表升级后的历史值。本地 Node 测试使用无UIAbilityContext的 Repository 回退路径可以证明业务规则但不能证明真机 RelationalStore I/O、冷启动恢复和页面视觉排序。十三、性能边界不能靠三条演示数据推断当前实现把全部报告读入内存再做过滤、映射和排序。对于比赛演示和小数据集这条路径简单清晰对于全校长期运行的数据量不能声称已经具备大规模性能。扩展时应先测量记录量、候选分布和响应时间再决定是否增加数据库条件查询、索引、分页或增量缓存。若引入缓存还要处理报告撤回、隐藏、结案与编辑后的候选失效。优化不能破坏解释性。即使召回阶段换成数据库或向量索引最终仍要保留理由、冲突和安全核验入口。十四、规则升级必须可追踪权重变化会让同一批数据得到不同排序。正式产品应为评分策略保存版本并记录哪些公开字段进入评分每项权重和硬门禁规则变更原因回归样本与误召回案例新旧版本的排序差异是否影响已生成但尚未处理的候选。这样才能解释“为什么昨天排第一今天排第二”也能避免把临时调参当成稳定业务事实。十五、本文小结可解释失物匹配的关键不是模型复杂而是边界清楚先用类型和状态门禁限定合法候选再用公开字段计算信息相似分同时输出理由与冲突最后把所有权核验留给私密特征和安全交接。“寻迹校园”当前实现是一个离线、确定性、可测试的基线。它已经能支撑候选排序与解释但地点归一化、名称策略、同分顺序、阈值和大数据性能仍需要真实数据验证不能把演示规则包装成万能算法。系列导航第 21 篇 / 共 50 篇。上一篇《标准值与历史文本共存》下一篇《信息相似分不是所有权概率》。