作者:勇皓 根根 王欣等
美团到店综合业务(以下简称到综)是美团到店业务的重要板块之一,涵盖洗浴、KTV、美业、医美、亲子、结婚、运动健身、玩乐、教育培训、家居、宠物、酒吧、生活服务等数十个重点细分行业,满足数以亿计用户多样化的本地生活需求。
推荐系统在其中是实现供给和需求高效匹配的重要环节,是传递数据价值的出口,而推荐系统的质量决定了匹配效果的折损。如下图 1 所示,数据经过数仓处理、算法加工,再通过数据服务到各个业务系统,最后通过客户端埋点又重新流转回数仓,形成了数据的“飞轮效应”,而质量恰恰是这条链路中齿轮啮合的关键点,是提升效率和保障效果的重要前提。
质量保障要围绕着度量开展,才能“看得见”、“理得清”、“改得准”。但是传统的后台服务质量指标并不能很好地描述当前“数据飞轮”的质量。我们希望通过综合业务推荐系统的质量模型建设,为类似多业务线、效果导向的系统质量度量提供一种新的思考角度和实践参考。
图1 推荐系统的“数据飞轮”
推荐系统是效果类系统,质量特点与功能类系统有所不同。功能类系统一般降级后会较为显性地影响用户体验,但推荐结果返回 A 或者 A',用户很难有明显感知。但实际上,如果匹配效果变差,就会直接影响到用户的隐性体验,需要被识别。功能类系统一般以可用性为核心来构建质量指标体系,在综合业务推荐系统的业务实践中,我们发现可用性等指标存在以下的局限性:
在质量建设中,过去以故障等级作为目标,验证周期长,具备偶然性,且目标和动作逻辑推导关系不强。另外,故障本身偏事后,这种问题驱动的思路不利于持续运营。总的来说,以可用性为目标,在实际落地计算时存在种种问题,所以我们考虑进行推荐系统的质量模型建设,以可用性为基础,然后调整计算方式,进而指导精细化的质量运营。
建设质量模型,先回到对质量本质的理解。根据国际标准化组织(ISO)的定义,质量是反映实体满足明确或隐含“需要”能力的特征总和。另一个常用的质量概念是稳定性,稳定性的核心是让系统长时间地运行在“预期”状态。无论是质量还是稳定性,都要搞清楚系统需要满足谁的需要和预期。在推荐的场景下,这个对象是产品和算法。业务产品通过理解用户场景,抽象用户需求,向推荐团队提出产品需求,体现为对外的产品迭代;同时推荐系统团队内部相互协作,学习最佳优化模型策略,体现为数据团队内部的算法迭代。
如下图 2 所示,在可用性的计算公式中,强调了长时间,而“需要” 和“预期” 只体现在对外提供服务上。这里具有一定的合理性,一是可用性作为业界通用的指标,定义必然是泛化的,那么质量的共性和底线就是对外提供服务;二是大多数后台系统交付功能,对外提供服务大多在“有”和“无”之间,也有一定的空间给到服务降级。但是对于以效果为核心目标的推荐系统,在功能“有”和“无”之间,存有很长的效果“好”和“坏”的光谱。我们对推荐系统质量的思考迭代,核心改变就是从对外提供服务的“有”“无”,变更到对外提供服务的“好”“坏”,这也是改造可用性计算方式的出发点。
图2 对缺陷的认知影响质量度量
不满足“需要”或者“预期”则会产生缺陷,缺陷是质量折损的原因。ISO/IEC 25010 Software Quality Model (2011) 软件质量模型定义了软件缺陷,可以看作是缺陷的全集,它包含了功能适用性、性能效率、兼容性、可用性、可靠性、安全性、可维护性、可移植性 8 个特征及 31 个子特征。这里面有一些质量特征后台服务不涉及(用户界面美学、易学性等),有一些在当下认知中不构成 C 端质量的突出要素(模块性、共存性、不可抵赖性、可重复使用性等)。结合推荐系统的业务特色和高频质量问题,现阶段我们重点考虑如下图 3 所示的质量特征作为缺陷来源。
图3 推荐系统的质量特征
我们发现传统可用性的度量,大多集中在可靠性、功能完整性、正确性方面,但是对于大部分的功能准确性、适当性以及安全性都缺乏度量,这些都与推荐的质量和效果紧密相关。准确性、适当性对效果的影响比较直观,其他则较间接。比如安全性,以安全性中的爬虫访问为例,爬虫由于访问行为不符合真实人类的行为习惯,会影响 UVCTR 等核心指标的回收,从而造成效果误判;同时如果不能识别和剔除爬虫数据,噪声会进一步影响模型训练的准确性。数据质量问题是数据“飞轮效应”中的“毒丸”,会产生正反馈不断放大缺陷。我们将在第四章计算规则中,量化上述的缺陷,拓展可用性的外延。
可用性可以分为度量方式和计算方式:度量即我们常说的 N 个 9,计算则用平均故障间隔时间和平均恢复时间的函数来衡量。在度量方式上,业界常用的质量度量方式如下图 4 所示:
图4 度量方式
度量方式选几分制,不是现阶段质量分的重点,可用性本身采用的 N 个 9 也足够简单可比较,我们重点考虑计算方式。由于到综业务线众多,推荐系统作为平台型产品,系统与业务是 N:N 的关系,当下系统的可用性难以去计算每个行业、项目和业务的可用性。一个流量位置,它可以归属于休闲玩乐这个业务,可以归属于剧本杀这个项目,可以归属于核心展示主路径的一环,也可以归属于内容推荐的一种,这种灵活的归属性,用请求来聚合计算是最合适的。如下图 5 所示,如果可用性是请求的函数,它既可以包括上一节中我们关心的质量特征,也可以在多个维度统计有业务意义的质量情况。
图5 从请求的角度度量质量
根据上一章节的建设思路,从故障到缺陷,从推荐结果的“有”、“无”到推荐效果的“好”、“坏”,从整体到各个业务,我们描述了一个好的质量分应该有的特征。这一章节我们着重在指标的计算逻辑上,选取关键缺陷,定义“成功的请求响应”,并增加质量分的业务聚合维度。
结合 3.2 章节中描述的质量特征,从成功请求占比的角度评估系统质量,在实际落地计算时可以分成以下四个层面的缺陷:
一条请求,在生命周期的任意环节经历了缺陷,则在结果上定义为缺陷响应,具体的缺陷环节是分析下钻的维度。我们从 3.2 章节的质量特征和上述缺陷的四个层面选取典型问题(业务痛点、高频质量问题)进行计算,以下图 6 为例:
图6 质量分计算方法
到综推荐系统的业务特色是多业务线,行业差异大,推荐物料位置多,这折射到质量度量上,我们需要各个层次的聚合分析,进而指导精细化的运营,如下图 7 所示:
图7 各业务层次的聚合分析
到综有很多中低频业务,此时比值的波动受请求绝对值影响较大。针对这些场景,可以聚合部分小流量位,只在行业或者项目层面进行分钟级的监控。
如下图 8 所示,我们将推荐系统响应的一条请求作为一次产品交付行为看待,这些请求中无缺陷的比例,就是推荐系统的质量分,是顶层的质量输出指标。可以根据请求的生命周期,建立一级输入指标,衡量核心流程的质量现状,如召回缺陷率、排序缺陷率等。还可以再将一级指标进一步拆解,得到二级输入指标,比如召回缺陷率比较高时,可以再去衡量召回空值率、召回超时率等。用户的请求还可以根据业务进行垂直、横向、时间维度聚合,得到有业务属性的质量分,这样会更有针对性,更加聚焦。
图8 质量指标体系
这套改进后的质量分,以请求为基本单位,相较于最初的可用性计算方式,在一定范围内解决了它的局限性:对缺陷敏感,可以包括数据链路带来的影响,方便进行多业务维度的聚合分析。
质量分以请求的粒度统计,在数据应用服务中,请求只是数据对外输出的形式之一。在完成基础的质量分后,请求的生命周期应该延展到数据全链路,这样对质量的度量才完整。这时就依赖数据的血缘关系,将数据表 - 业务系统 - C 端流量关联起来,构建全景的质量画像,如下图 9 所示:
图9 推荐系统的数据血缘
血缘关系是人类社会由婚姻和生育产生的人际关系,如父母和子女的关系、兄弟和姐妹的关系,以及由此派生出的其他亲属关系,数据也可以通过融合、转换产生数据的血缘关系。数据的血缘关系分数据库、数据表、字段不同级别,一般用于数据资产(引用热度计算、理解数据上下文)、数据开发(影响分析、归因分析)、数据治理(链路状态追踪、数仓治理)、数据安全(安全合规检查、标签传播)四个方面。在目前推荐系统质量分的思路下,主要用影响分析去拓展质量分,将所有途径故障节点的请求都打上标记,扣除相应分数。
在推荐系统的业务语义下,我们定义了六种业务元数据:快照、方案、组件、索引、模型、特征,基于元数据我们构建血缘,可以分为任务接入、血缘解析、数据导出。任务接入分为采集模块和入库模块,当任务接入完成后,将通过图数据库存储节点及节点的关系,利用图算法建立血缘。建立血缘之后,节点本身的异常支持系统发现和人工标记,影响分析则可以自动完成。当节点出现异常则进行消息通知,异常信息会沿着血缘传播,继而影响下游环节的质量分计算。
当异常波及到用户端,我们尝试用业务语言重新描述损失。根据到综收入模型,可以计算出各个业务线每个意向 UV 的价值(用户访问商户详情页、团单详情页称之为意向访问),再利用该流量位周同比的访问情况,自动推导业务损失。
质量分的系统实现方式依赖于埋点和诊断。推荐全链路包含参数输入、召回前置处理、召回、召回后置处理、粗排、精排、重排等多个环节,每个环节都有可能出故障,因此数据采集需要覆盖运行时异常、各环节的关键输入输出信息等。如下图 10 所示,我们通过 Kafka 异步收集埋点数据,然后分场景进行数据处理:生产环境下,近实时 ES 构建索引,提供近 4 天快速查询服务,4 天前的日志入 Hive 归档,另外通过 Flink 引擎解析埋点数据,经过必要的诊断后,实时计算分数并推送告警信息;测试环境下,日志实时分拣至 MySQL,方便测试排查。最后,结构化展示推荐不同阶段的质量情况,提高了结果的可读性。
图10 质量分的系统实现
分数体系的完善需要逐步推进,对于推荐系统,没有推荐结果是最严重的质量问题。我们首先采集和计算的是推荐空结果,对应一级指标里的结果缺陷率、召回缺陷率和二级指标里的结果空值率、召回空值率等。同时由于到综的业务特性,行业众多、供给时空分布不均,大量可交叉的筛选条件也会有空结果,影响质量分的计算。
如何剔除符合业务预期的空结果,消除质量分噪声,在实现埋点的基础上,诊断就变得非常重要。以空结果为例,我们主要从参数诊断、数据诊断、链路诊断三个环节去识别。其中数据诊断指的是当线上筛选条件出现空结果时,回源二次校验底层数据,查询底表数据是否为空。如果底表确实没有相关供给,则沉淀免告警规则,设置免告警有效期,在一段时间内,当前城市当前行业确实缺少相关供给,该空结果不纳入质量分计算。
如果底表存在供给,则说明是数据加工或者服务过程中出现了异常,导致无法召回,则再经过链路诊断确定出错环节,纳入相应质量分计算。如何建立规则匹配机制(即规则引擎)是诊断引擎的关键。当下的规则引擎选择非常多,例如 EasyRule、Drools、Zools、Aviator 等。根据上文分析,诊断引擎需要能够对请求参数、推荐链路以及底层数据进行规则诊断。对于请求参数、推荐链路的诊断均可通过内存参数进行诊断,而数据诊断则需要从第三方存储中获得信息,因此必然有一部分需要定制开发。考虑人员工具使用成熟度以及便利性来说,Aviator 表示式引擎较为合适。为契合需要诊断的内容,设计的表达式诊断原语如下:
<span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//参数诊断-原语表达</span><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//是否符合一定参数的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">include</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">string</span>.<span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">split</span>(<span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">'1,2,3,4,5,6,7,8,9,10,16,17'</span>,<span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">','</span>),<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">str</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span>))]<br><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//链路诊断-原语表达</span><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//1、召回异常诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#exception</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> ]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//2、召回空无异常的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> ]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//3、召回不为空,过滤规则执行后为空的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predictFiltersEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predict</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#filters</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">||</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span>)<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predictFiltersEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span>]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//4、执行某一具体过滤规则后,导致无结果的匹配</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">PredictStage</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#filter</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#_compSkRef</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">'deleteItemByConditionalFilter'</span> ]<br><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//数据诊断-原语表达(判断底层是否有数据,若没有则为true,否则为false)</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keys</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keySpread</span>[<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">prefix</span> <span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">138_</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">ymtags_</span>][<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">crossOrder</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">city_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span>}<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">_platform_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">platformNo</span>}<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">_surgery_prj_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">genericLvlIds</span>}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cellar</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cellar</span>[<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">count</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keys</span>}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">long</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span>) <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);"> <span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">0</span> ]</span>
质量分可以用于实时监控和运营复盘,需要团队成员及时跟进异动。一般公司通用的告警系统,都是基于服务名称粒度配置告警接收人。推荐系统这类平台型的服务,通过统一的接口提供服务,但是模型策略却是由不同的同学维护,业务间存在一定的行业知识和理解门槛。默认广播式的告警,容易引起告警风暴,每个人无法专注于自己模块的问题,有时也会遗漏告警。出于跟进率的考量(如下图 11 所示),我们基于现有告警二次开发了跟进功能,将特定流量位的告警路由到专属负责人,并记录跟进状态流转,便于及时周知及事后复盘。在运营方面,我们通过数据报表搭建质量分看板,定期回顾不同业务的质量波动情况。
图11 告警跟进流程
质量分的落地以结果空值率为抓手,按流程拆解采集召回空值率、模型预测空值率、重排算子空值率,并按业务聚合成平台、业务、形态、项目、流量位多个维度。治理动作和成果分为以下几个方面:
经过空结果的治理和识别,目前核心流量位空值率为 0.01%,即保证核心流量位 99.99% 的请求有结果,在建设质量分的同时,保证系统发现率和告警跟进率。
推荐系统传递的是数据的价值,只有数据被资产化,这种价值才是可持续可增值的。建设推荐系统质量模型的过程,其实也在做数据资产化沉淀。数据在采集后变成资产,一般要满足以下四个条件:可流动、可计量、可管控、可增值,这些在第四章计算方式中都有所涉及。指标运营的过程,同时也是沉淀质量知识资产的过程。软件缺陷模型究竟如何影响最终的产品交付质量,他们之间是否有相关性、因果性,这种影响是显式地参与分数计算,还是间接影响的。在质量分运营过程中,我们可以逐渐填补脑海中的质量地图,形成指标间、缺陷间、指标和缺陷间的拓扑关系,这是一个将质量资产化的过程。比如通过推荐系统的业务实践,我们发现 80% 的线上故障是由于发布引起的,发布故障中的 80% 又是由于数据发布引起的,这可以指导我们通过治理数据发布减少线上故障。
我们以可用性为基础,调整计算方式,建立了多层次的推荐系统质量分,并拓展到各种推荐物料、各个业务模块,核心是我们完成了从对外提供服务的“有无”到对外提供服务的“好坏”的认知迭代,这也是质量精细化运营的基础。后续的规划,一方面是继续充实质量模型的计算和链路覆盖;另一方面,我们会基于质量模型做更多的质量治理工作,后续将重点思考与迭代的一些方向包括:
勇皓、根根、王欣、贺贺、俐聪等,均来自美团到店平台技术部/到综业务数据团队。
以上是美团综合业务推荐系统的质量模型及实践的详细内容。更多信息请关注PHP中文网其他相关文章!