黄联樵 / 2020.2.26 / 微信:arubagod(疫情期间赋闲在家找工作,坐标深圳,欢迎骚扰)
美人妆是一个以美颜相机为玩法,销售化妆品和护肤品的平台,有7500万左右的东南亚用户。我任职产品合伙人之后的第一个工作是面向国内市场开始设计和上线它的小程序。
探索

小程序中有两个主要的功能,一个是跟随短视频的化妆教程,按照视频的步骤给用户自动上妆(通过摄像头给用户拍照,再通过第三方框架给照片化妆);另一个是分析用户的面容特征,然后推荐最适合ta的整妆教程和对应的商品集合。

在我设计完小程序原型和UI的指导设计稿并投入开发之后,公司谈好了4万台电视机(带安卓端及摄像头)的合作方案,准备把这套玩法迁移到电视机上,然后安装在合作方在全国的各大商场。

后期又跟其它合作方设计了售卖机的方案,把这个电视机内置在售卖机中,实现售卖机的揽客。其中电视机系统可以看做是在售卖机所含SKU的范围之内运行它的广告,并额外对接售卖机厂商提供的购买系统。确定好这个界限之后,发现电视机的核心系统并未受到冲击。

电视机系统的需求,从描述来看一开始是简单的。平时每个电视机像分众传媒的电梯广告电视机那样,播放传统的视频广告;而当有路人靠近时,这个电视机能感知到路人,然后瞬间变成一个互联网推送模式的广告系统,给路人推送广告,并期待路人与之互动(例如通过摄像头来玩化妆游戏、扫码)。

初步在原型设计时设计了系统控制逻辑之后,发现设备的控制逻辑不能用简单的图形来表达,实际上它将是一个庞杂的系统,这个系统是纯文字的,涵义却是图形化的,因为它只能用面向对象(而非线性叙事)的文档形式来表达。
传统的电视媒体,例如分众传媒,它的每个设备的每一秒钟要做什么都是固定的,只要设定好视频资源和时间表就可以完成所有操作,设备数量的提高也只是简单的一个模型在不同数量级上的单纯的扩张,可以简单地用群组和批处理来管理。而群组和批处理并不影响单个设备的运行逻辑。
另一方面,互联网广告的推送模型从整体上来看也不复杂。不管背后需要搜集一个用户的多少资料、考虑到时间、IP、设备类型等等多少种条件,我们都可以简单地把它理解为一个黑箱,最终这个黑箱要做的事情就是此时此刻一个页面上要给一个用户展现什么内容。因此互联网广告推送可以简化地看做一对一的推送行为。
而一个电视设备要把分众传媒的传统模式和互联网的推送模式结合起来,并不是两者难度叠加,而是两者的复杂度相乘,其复杂度比前两者都要大一个数量级。例如什么决定了一个电视设备从传统传媒的模式转为一对一推送模式,其阈值在哪里?例如两个人同时靠近这个电视机,电视机到底给谁推荐商品?例如一个商家把我们的电视机看做传统的分众传媒设备,想要包整个商圈几十台设备在晚上黄金时间播放他的广告,此时我们还能不能精准推送,如果精准推送打断了这个商家的广告,费用如何计算?——我发现这个问题没人尝试解决过,难度又高,所以就开始设计这个系统了。
简化电视机的变量
前面的给出的设备控制流程图之所以复杂,是因为把电视机当做一整个系统来设计,不同的交互或系统状态之间没有章法可寻,任意细节都可以改变整个电视机的状态。例如,当电视机正在全屏播放一个视频广告的时候,此时一个人走过来,那么视频广告的字幕区就会提示这个人可以拍照,而右侧的摄像头也会无中生有地打开。而当这个人把脸对准摄像头拍照完毕后,整个电视机就会进入整体的激活状态,一面播放给这个用户推荐的专属妆容教程,一面又给用户展示化妆之后的照片,并提醒用户扫码。
这样的设计固然是前期应有的流程,毕竟没有具体的场景,就不会有后期的总结,然而现在的任务就是去总结这些不同场景的共同点。只有把设备前端的共同点(例如视觉上的共同点、屏幕不同区域的共同点)总结出来,才有可能把后端逻辑设计得优雅。单纯只考虑前端或者只考虑后端逻辑,各自为政,都不可能实现一个简洁的投放系统。

首先我把目前的原型和效果图拿出来,再画一些飞机稿,考虑未来这个电视机还会有哪些可能的交互状态,然后总结出了几个大的屏幕区块,在这个基础上把不同的区块状态排列组合,形成了几种宏观状态的概念。有了宏观状态的几种概念之后,电视机在不同的几个宏观状态之间切换,就是控制流中最大的几个环节了。只要控制流能精准地在这些大环节之间切换,设备就能在正确的时间做正确的事,至于大环节之间的平滑过渡则是前端设计的事,而至于大环节之内的小环节则是有大局观之后的尽情发挥了。

同时,当区块的类型确定之后,播放资源(物料)的配置类型也就确定下来了。其中包括共性和特性,共性即预算、所属商家等属性,而特性则是给定具体配置类型下的模板中的具体参数配置。经过以上的步骤,这个系统的物料准备工作就做好了,交互上的变量也抽象到了无法继续抽象的地步,在这种优化的前提下,具体控制逻辑的设计之路就铺好了。
最终播放逻辑的确定
简化变量之后,我暂时跳过了中间的步骤,直接去设计一个具体设备的最终的播放控制逻辑,因为这是这个系统最终要输出的东西。在这个过程中遇到的主要的附加挑战则是多个设备如何协同完成一个投放任务。两者一个是主线,另一个是它的敌人。我把这两个问题摆在一起让它们互相PK,以便给我一个能与其它设备协同作战的单一设备控制理论。

对于一个具体设备而言,一共有三种大的控制逻辑,我把它们用优先级划分开来。当高优先级的控制逻辑满足开启条件时,低优先级的控制逻辑无条件终止,这就是控制逻辑最高层的展现。具体来看,优先级最高的是“等待扫码”,因为此时用户已经跟设备产生了交互,正在掏出手机扫码,这是我们这个设备唯一可以直接转化用户的手段;优先级中等的是“霸屏”,这是因为在设计这个系统的中途得知了一个重要需求,就是有的设备可能会被商家强制要求在某些时间段播放他们的广告。而最低优先级的控制逻辑则是“平时播放”,这才是一个设备最主要的控制逻辑。
设计这个控制逻辑的最高抽象层,用优先级来管理它们,是出于对未来未知情况的考虑。比如当电视机投放系统需要对接售卖机时,显然这个电视机也需要负责售卖机的买卖交互,那么到时也把这种交互归纳在最高抽象层,并给它分配一个最高的优先级。所以这一层是我为了电视机系统在未来不被各种具体场景的需求打乱其内在简洁性,而专门设计的一个保护层。让我直接有动机去设计这一层的原因,我已经记不太清了,但很可能是当时得知我们需要“霸屏”这个功能时,我担心未来各种需求对整个投放系统带来的破坏的恐惧。

在最主要(优先级3)的运行逻辑中,最需要解决的问题就是设备在传统电视设备和推送模式之间的切换,也就是当一个设备正在像一个普通分众传媒电梯广告屏那样播放内容时,一个人走了过来,这个系统如何判断是否要专门跟这个人互动,以及具体呈现哪一个互动;或者当一个按次数播放的广告需求投入到总系统中,系统如何分配指定的分发群组中的设备分别在什么时间播放它们,以及具体播放的频次。
所有上述的逻辑,都离不开一个问题,就是一个设备在什么时机打断当前的广播状态,呈现一个什么样的具体物料。然后我发现当自己理清这个问题的时候,这个问题本身的描述就是它的答案。
首先,我把一个设备收到的所有物料从主动性上分为两类,一类是主动,另一类是联动。联动的物料在屏幕的副区域呈现,因此不具备对整个设备状态切换的控制权;只有主动类的物料拥有这种控制权,因此系统只需要考量接下来播放哪个主动类的物料,就能知道自己下一步要做什么。其次,我再把主动类的物料划分进两个池子里,一个是常规播放的池子,也就是一个有序的播放列表,另一个池子则是无序的推送资源库,里面存放着允许这个设备自由推送的物料。
设备在每时每刻都有两种选择,其一是继续它的常规的播放列表,也就是老老实实地做一个传统电梯广告设备;而另一种选择则是从推送资源库这个池子中选择一个物料来中断当前的播放,完成它当前的推送任务。
而设备的抉择可以用一个简化的逻辑(即前文提到的,问题本身的描述)来处理。一方面,总系统要告诉这个具体的设备说“我已经给你选择了一个最佳的物料,它在你的无序池中的ID是多少,它的紧急度有多少”;而另一方面,由于所处商圈和具体场景中的职能的不同,每个设备都有不同的积极性,这个积极性决定了它对于总系统交予的任务的响应积极度,也就是每个设备的“插入阈值”。与插入阈值协同作业的还有具体一个设备的“AI敏感度”,这个敏感度决定了AI因素对于一个物料在总系统调度时紧急度运算的参与程度,也就是AI对于调度系统的影响力——因为AI不是万能的,尤其是当我们处于系统上线的前期。也正因为很多参数类的东西并不是可以一开始就能规划完美的,所以我在系统文档中预留了很多的“全局参数”和“实例化参数”,并在后台页面中统一呈现出来供运营人员集中管理。

一个具体的推送类的物料,它对于一个具体设备的紧急度与三个因素有关。其一是这个物料本身的推送配额、速率和具体要求,例如某个广告的需求是全网推送5000次,每日的速率上限是500次,其中次数的定义为“驻足次数”,即行人停留在电视机前N秒才符合“1次”的计数要求;其二是这个物料对于用户画像的建议,以及具体设备当前面对的行人在多大程度上满足画像;其三是全网一共有多少个设备正在分担这个推送任务,以及这个具体设备相对于其它同僚设备是更轻松还是更繁忙。
以上三种紧急度运算的主线涉及到两个重大的运算任务,其一是对任务的全网调度,其中包括对每个设备当前状态、当日任务完成度的实时监控;其二是每个设备的摄像头对于用户画像的精确理解。这两个都是大工程,都涉及到开发人员的巨量工作,作为系统策划来讲,我能做的只有把机器学习的评分体系抽取出来。因此,我需要一个大大的黑箱把这两个任务装进去,以便为开发人员创造两个独立的系统。
因此我简单地把一个推送任务相对于一个具体设备抽象成了2个整数分数(0到99,49代表该设备如果此时响应这个任务,则全网任务可以匀速进行)和1个浮点数。分别是调度系统的紧急度、AI系统的置信度,以及AI敏感度。AI置信度乘以AI敏感度,加上调度系统紧急度,就是最终一个物料对于一个设备的分数。只要越过该设备的阈值,就可以实现设备传统广告播放的中断,即刻推送这个物料。
从这里我也学习到了设计黑箱在系统策划时的作用。产品设计中的黑箱并不等同于实际开发时的黑箱,因为就上述计算任务而言,显然全网的调度系统是统一分发任务给具体设备的,不可能把每个设备的每次任务都当做一个黑箱来看待,然而这种策划对于把一个开发难点孤岛化,并且确定该系统在业务逻辑上的输入输出,具有十分重要的意义。
俄罗斯套娃
在前面两部分,我已经从源头上确定了物料,从最终设备展现上确定了最终的控制逻辑。所以接下来的工作就是去探索中间的路怎么走,一个个具体物料经过怎样的过程进入最终的设备。由于第一版的系统就已经要面对几万台设备,所以我打算在第一版就按照一个运营人员可以管理未来几十万台设备的每日任务来设计这个系统。
显然我只有一种方法来实现这个目标,那就是让许多物料组成一个单元,再用这个单元组成更大的单元。具体的设备只要选择几个制作好的单元,就能直接拼接成它的节目池。在设计的过程中,我极力避免用批处理的手法走捷径的行为,一旦为了偷懒而在后台中设计了“把设备1的节目直接复制给设备2到999”这种批处理的功能,那么整个系统就会因为放弃了正规的内容继承规则,而在未来版本的不断的批处理功能扩充后毁掉。任何一个系统都可以看做一个生命体,一个低熵体,而懒惰的批处理行为就是它衰老(即熵增)的开始。
套娃第1层)俄罗斯套娃的第一层是“物料”,它们构成商务人员负责操作的物料池。所有的具体物料的录入都由商务人员进行,每个具体的物料都包含它的预算和具体的投放需求描述。这一层的主要目的是建造一个护城河,河的左边是庞杂的物料,庞杂的商业需求,由成日跟客户打交道的商务人员来负责;河的右边是设备运营人员,他们通过每个物料的“工单系统”来跟商务人员沟通,杜绝了两边工作互相干扰的情况发生。

套娃第2层)俄罗斯套娃的第二层是“节目”。若干个物料构成一个节目,代表某一投放需求的集合,而这些节目构成了节目池。具体一个节目往往代表具体一个商家的一次活动,例如A公司在春节期间想要投放5个广告,那么这5个物料就可以生成一个“A公司春节促销”节目;B公司想要在某个商圈大范围推广自己的各种产品,那么我们可以为B公司制作一个“B公司产品广告合集”节目。
从投放方式上看,节目被分为两种类型,“承包类”代表传统传媒节目,即分众传媒的死板循环播放模式;而“计次类”则代表按照一定的指标要求进行投放的节目。所以节目层的主要作用之二,就是把第一层物料池中商务人员给出的具体物料及其投放需求描述,转变为具体的投放任务。例如一个在第一层的物料池中,一个物料的预算有30,000元,其需求描述是“在深圳南山商圈进行圣诞前的活动预热”,然后在第二层节目池中,被运营人员分解为2个节目和3个具体任务,引用这个物料进行5000次的曝光和500次的扫码转化的分解任务,这些计划共享这个物料的30,000元预算。一个物料可以在A个节目中被引用A+N次,引用关系是严格的——物料层的数据储存在物料层,而具体的投放计划储存在节目层,多对一指向同一个物料。

套娃第3层)俄罗斯套娃的第三层是“总控”,构成全网的总控池。一个总控就是一个完整的设备控制系统,包含一个设备运行所需的全部物料、投放计划、配比、参数、特殊配置。两个设备应用了同一个总控,那么两个设备的行为准则(而非具体行为,因为每个设备面对的具体人流不同,对共享任务的具体分解也不同)就是一致的。
制作一个总控,最主要的操作就是从上一层的节目池中引用若干个具体的节目,然后在此基础上配置总控层所要求的除引用节目ID之外的额外参数,在某种程度上,与第二层引用第一层的行为形成了美妙的递归。举例来说,一个南山区万象城的所有设备都需要同时播放“万象城节目”中的5个广告、“金龙鱼化妆品节目”中的15个广告,和“深圳市公益广告”中的100个广告。那么运营人员就引用这3个节目制作了一个总控,其中虽然公益广告的数量很多,但是运营人员只要调低这个节目的配比就能减少播放公益广告的总时间。
节目的配比是对一个节目所含具体物料的播放总时间,占整个设备运行总时间的调控手段,这个手段与计次类投放的任务是不冲突的,因为只有“承包类”的节目,即一个设备平时待机状态像分众传媒电梯广告那样运行时的节目,可以调节其占比。而对于计次类节目,即各种拥有明确推送任务的节目中的物料,至于这些物料的具体投放任务相关。一个物料要求每日投放3000次,只有100个设备拥有这个任务,那么这些设备自然会拼命投放这些物料。
而“霸屏”及未来类似的操纵设备的功能,都是建立在这层套娃内部的虚拟层中的,这就防止了逻辑上的错乱。以霸屏来举例,霸屏功能要求一个设备在固定的时间循环播放1个节目,我没有在这里用任何的override技巧,而是在已经配置了承包节目的基础上,简单地加多一个时间表。我也不允许多个节目同时霸屏,因为这样我们的节目配比数据就会从一层变为多层。这种拒绝override的理念贯穿了整个投放系统——如果一个系统是优雅的,它理应不需要任何的强行搭线行为。
一个设备拥有很多区块,但不一定全部开启。开启的区块必须至少配置一个承包类的节目,以便在这个区块没有收到投放任务时依然能运转,不至于黑屏。各区块之间有的具备相互的依赖性,或者单方面的依赖性——它们构成了配置一个设备总控的防呆策略。

从左到右的三列,就是套娃的三层结构的传递过程。第一层物料池是商务人员的领地,每个物料都有各自的状态,其中只有“可投放”状态意味着这个物料是正常的,可以被第二层选用——第一层储存的是物料。
在第二层节目池中,若干物料构成一个节目,一个节目具有健康、残疾和瘫痪三种身体状态。对于承包类(传统模式)的节目而言,具体物料的状态直接决定了节目的身体状态;而对于计次类(按指标投放模式)的节目而言,物料先与具体的投放配置绑定,形成一个个具体的“投放”。物料的状态传递给投放,投放再传递给节目,最终决定这个节目的身体状态——第二层引用第一层,并储存具体的投放规则。
在第三层总控池中,若干节目构成一个总控。总控同样具备三种身体状态。同时总控由于直接控制设备,所以存在自身的低保要求。只有并非“瘫痪”的节目共同满足了一个总控的低保,这个总控才能避免瘫痪——第三层引用第二层,并储存具体的设备控制规则。
一个系统的模块化固然重要,但不是越多层级越好,禁止无节制的套娃是很有必要的。这三个层级在具体业务上的划分比较重要,便于实际的运营。再多会带来过分的解耦,导致不必要的复杂度;再少则会影响业务的运转,系统虽然简单了,运营人员的工作却多了。例如假设我把节目池去掉,那么每个具体的设备播放策略,都需要运营人员重复地去整理一个个的物料,带来大量不必要的工作。
安装设备、分发内容和统计结果
一个具体的电视机设备既需要用“地区群组”的体系来管理它的物理位置,又需要用“分发群组”来控制它的具体行为。这两种群组的概念到了这一步几乎是自然产生的了,唯一需要抉择的是两者分别承载什么数据。

地区群组最主要的功能当然是管理具体的省、市、区,直到具体的商场及楼层,然后在具体的楼层(或街道、店铺等同级别载体)中储存若干个设备,这些初等概念不再赘述。关键是它额外承载的两类数据——计费系数和画像信息。
首先是计费系数。由于价格根据投放行为的不同而产生的各种计费方式本来就已经够多了,所以全网应该只有一个标准的价目表,不同区域的设备根据区域的消费水平和营销策略进行相关的系数调整,不能容忍军阀割据、销售人员的混乱定价(至于真的要给一个商家何种优惠政策,我已决定分割到商家账号这个模块中设计,因为那个模块简单太多)。
在一开始的设计中,我采用以完整追溯的方式来确定最终一个设备的计费系数。例如,广东省的系数是<+10%>,深圳市的系数是<+20%>,坪山区的系数是<-50%>,具体商场的系数是<+5%>,商场五楼由于客流量较小所以系数是<-30%>,五楼有一个设备的屏幕比较大所以系数是<+10%>,那么最终这个设备的计费系数就是65折。但是当我设计后台页面时发现,系数的向上追溯带来的是困难的操作,而如果在界面直接给出最终系数的呈现,那么运营人员会依赖这个最终数字,不再去管理每一层级的具体系数,导致整个体系失去了它的意义。因此最后我采用了就近原则,就近原则既能批量管理一个片区的设备,又不妨碍具体商圈内营销策略的快速部署。
其次是用户画像的相关数据的储存。具体设备的摄像头不是万能的,在它有限的“人工智能”中,它只能判断出有限的信息,例如当前的客流量、具体一个人是男人还是女人、大概的年龄是多少。在未来至少20年内,我们需要各种人工录入的信息来确保一个设备实现它的“人工智能”。在地区群组中,我们可以储存很多已知的数据,例如这个商圈主要的消费人群有哪些,他们平时几点钟喜欢干什么。与计费系数的影响范围不同,画像信息采用完整继承(信息冲突时再采用就近原则),例如我们在南山区万象城这个群组里储存了这个片区用户的消费倾向,而无需储存他们在几点钟更喜欢食物类的广告,因为我们已经在“全国”这一层储存了“大家喜欢在12点和20点左右吃饭”这个信息。

而分发群组自然是控制内容的分发。运营人员只需要从总控池里选择一个配置好的总控,把它绑定给一个分发群组,这个分发群组的设备就会都按照这个总控的资源和要求进行投放了。分发群组本身的大部分逻辑其实已经在总控模块中设计完毕了,剩下需要解决的主要是运营人员的体验问题。因此我设计了一个特殊的分发群组:“地区群组映射”,在很多情况下,对一批设备的分发都是按照一个地理区域来进行的,而“地区群组映射”顾名思义就是地区群组的影子。一个设备一旦分发了内容就不应该在非人为的情况下被“智能纠错”,所以当一个设备的地理属性发生变更,或某个片区的地区群组发生调整时,地区群组映射是惰性的,是需要人为更新的。

所有设备在每晚一个固定时间进行当日任务的统计结算,不光统计出每个商家的具体花费,更是去衡量当日的调度系统对任务的完成程度,以此为依据进行机器学习。统计的工作主要由负责大数据的开发同事在各系统进行埋点来实现。
这里的计费体系、商家资金系统(包括前文提到的,商务人员可在此处给商家赠送金币、调整折扣)都是比较常见的系统。值得一提的是“有效播放时长阈值”这个全网参数。由于我们的设备在日常扮演分众传媒传统广告屏时经常会被推送任务打断,所以一个视频放了多久才算作确实已经放完、才能让金主的钱没有白花,背后存在一个值得被反复推敲的标准。这个标准仅针对传统节目以及非结果导向的推送,至于结果导向的推送(例如扫码算作一次)自然不受它的影响。
另外值得一提的是,由于一个设备每天扮演分众传媒的时间不是承包类节目本身可以决定的(除非使用更贵的“霸屏”方案),而是这个设备的推送任务所决定的,所以承包类节目不存在严格意义上的时间表,不可指定具体时间,只能24小时无限参与循环,因此它的计费方式是按照具体物料在当日进行有效循环(参见上一段的“有效播放时长阈值”)的时间占设备全日运行总时长(而非24小时)的比例来进行梯度计价的。至此,一个从未有过的,支持精准推送的电视传媒投放系统问世了。
网友评论