做增长干了几年,越来越觉得一件事很关键:广告投放和用户触达,必须放在同一个系统里思考。
很多团队是这么干的:投放归投放,用媒体的后台或者独立的投放管理工具,花钱买量;触达归触达,运营同学用另一套推送、短信平台,给存量用户发消息。两边各有各的人群包,各有各的频控,各有各的报表。结果就是用户可能昨天刚在某个渠道看过广告,今天又收到一条一模一样的短信,预算浪费了,用户也被打扰了。
我参与建设的这个用户增长核心系统,就是把这两块能力融合到一个技术平台上。它解决的核心问题其实是三个:第一,人群怎么统一圈选,不重复不遗漏;第二,触达策略怎么统一编排,广告和消息别再互相打架;第三,效果怎么统一度量,每一分钱花出去能算清账。这篇文章把我的整体设计思路和实操过程完整拆解一遍,适合正在做增长平台、营销中台,或者想从零搭建这套体系的同学参考。
1. 项目背景:为什么广告投放和用户触达必须放在一起
这个想法不是一开始就有,是被实际业务逼出来的。
最早我们的增长业务也是分开跑的。投放团队用广告平台的官方后台,每天手动上传人群包,定向到一些相似人群做拉新,同时控制出价;运营团队用第三方的推送和短信服务,按用户活跃状态分群,给沉默用户发召回消息。刚开始没问题,因为用户量小,渠道也少。等日活到了百万级、月投放预算过了千万,问题全冒出来了。
1.1 分散系统的三个致命问题
第一个问题是人群数据割裂。投放侧的人群画像来自广告平台的算法,触达侧的用户标签来自我们自己的数据仓库。两边定义的头号用户、高价值用户、待召回用户都不一样,同一个用户在一个系统里是VIP,在另一个系统里可能被算成沉默用户,发了一条"好久不见,送你一张券"的短信,体验极差。
第二个问题是频控没法协同。广告平台的频控是媒体自己控制的,比如一个用户24小时内最多看到几次广告;推送短信的频控是我们自己的规则,比如一个用户一周最多收到几条营销消息。但这两套规则互相不知道对方的存在。实际发生的场景是:用户上午刷到我们App的广告,中午收到一条Push说"新人专享礼包",晚上又收到一条短信推同一个活动。三条触达信息指向同一个转化目标,互相竞争,而我们在报表上还以为是三个渠道各自的功劳。
第三个问题是预算没法全局优化。如果广告投放能覆盖的人群和触达能覆盖的人群有重叠,理论上我们应该把预算花在边际收益最高的那部分人身上。但数据不通,投放和触达各自为战,我们只能拍脑袋定比例,上半年广告拿多少预算、触达拿多少预算,根本没有数据支撑。
1.2 我们这个系统最终想做成什么样
想明白了问题,目标就清晰了。
这套系统要做到四点:人群标签统一管理、触达策略统一编排、渠道能力统一接入、效果数据统一归因。做一个类比,它相当于增长团队的"中央调度室",广告投放和用户触达不再各干各的,而是由一套规则引擎根据用户状态、渠道特点、预算约束实时决定怎么跟用户沟通。
关键点:这套系统不是替代广告平台,也不是替代推送服务商,而是把他们都变成底层的"渠道",由我们自己的策略层统一指挥。
我的理解里,广告投放的精准度和触达渠道的即时性各有优势,广告适合大规模拉新和品牌曝光,触达适合存量用户的转化和召回。合在一起之后,我们可以做一件事:用户还没变成我们的用户时,用广告触达;用户已经成为我们用户时,用Push、短信、站内信触达;用户沉默了,先Push召回,实在不行再投广告追回来。这些逻辑以前全靠人工判断,现在可以写进规则引擎里自动跑。
2. 整体架构与核心模块拆解
这一章讲架构,但我不想一上来就贴架构图,先说清楚我们做架构决策时的几个关键判断,这些判断决定了整个系统的形态。
2.1 渠道抽象层:把所有触达能力变成可插拔的
不管是投放还是触达,本质都是"把一条内容发给一个人"。区别仅仅在于渠道。广告投放是通过媒体平台把创意展示给用户,推送是通过厂商通道发一条通知,短信是通过运营商网关发一段文字。
所以我做的第一件事就是把渠道抽象成统一接口。每个渠道在这个接口里实现四个方法:人群包同步、内容提交、状态回传、数据拉取。广告平台的API、厂商推送的SDK、短信网关的HTTP接口,全部适配成同一套规范。
这样做的好处非常明显。新接入一个渠道时,不需要改上层策略,只需要写一个适配器。我们实际做过一次统计,接一个头部媒体的广告投放API,两个人一周就能完成联调,如果从上层业务开始改,至少需要两周。
渠道抽象层还需要处理一个容易被忽视的问题:各渠道的限额和QPS限制是千差万别的。广告平台一般允许批量上传人群包,但创建广告计划有频率限制;厂商推送一般按App级别有日推送量限制;短信运营商按签名和模板有审核机制。这些限制必须在渠道适配器里做统一封装,否则策略层无脑下发,分分钟把渠道打爆。
2.2 事件中心:一切决策的输入源头
增长系统的最底层是数据。没有数据,人群圈选就是空中楼阁。
我们自建了一个轻量级事件中心,统一接收App端、小程序端、服务端上报的行为事件。事件结构非常简单,包含三个部分:用户标识(userId、设备ID、手机号等多维度ID)、事件名称(启动、注册、下单、点击推送等)、事件属性(时间、页面、商品等,以KV形式存储)。
这里有一个架构上的关键选择:事件链路用实时流处理,而不是离线批处理。因为触达的很多场景对延迟极度敏感。比如用户刚刚把App卸载了,我们立刻就要在投放端把这个用户从转化人群里排除,否则继续投放就是浪费预算。又比如用户刚领了一张优惠券,系统马上判断他是否进入了待触达人群,两小时内不下单就发Push提醒。这些场景如果跑T+1离线任务,就错过最佳时机了。
事件的实时性还有一个重要用途:实时频控。后面细讲。
注意:不要一上来就追求完美的事件规范。初期保留最核心的几个事件(启动、注册、首次付费、活跃、沉默),把事件能冒烟跑通,后面再逐步补充,不要设计过度。
2.3 统一人群引擎:用标签圈人,而不是用人人
人群圈选是增长系统的核心能力之一。
传统做法是建一张用户宽表,每个人一行,按标签列做筛选。这个方案在千万级用户时还能跑,但上了亿级,每次圈选都是全表扫描,慢到没法用。我们做的是另一条路:把标签变成倒排索引。
具体来说,系统维护一个标签字典,每个标签关联一批用户ID。圈选人群时,不管条件是"高活跃且近7天未下单"还是"近30天有付费且未在沉默召回黑名单里",都翻译成语义运算,对倒排索引做集合交并补操作。这样避免了全表扫描,几十个标签的复杂筛选可以在秒级完成。
人群引擎还需要解决ID映射的问题。同一个用户,在广告平台有一套设备ID,在自有App里有一个用户ID,在短信渠道里是一个手机号。如果不做ID映射,同一个用户会被当成三个人群算,频控自然就失效了。我们维护了一张ID映射表,用图合并的算法把同一实体的多个ID关联起来。这个表是实时更新的,每当事件中心收到一个新ID,就去查一下能不能关联到已有的用户。
2.4 策略编排引擎:把运营规则变成可配置的
有了人群和渠道,接下来就是把两者连接起来的策略层。
策略编排引擎是我个人觉得这套系统里最有价值的部分。它允许运营和增长同学用可视化方式配置一套完整的触达流程,包括触发条件、等待时间、分支判断、渠道选择、内容配置,最终编译成一棵策略树,由引擎调度执行。
举一个实际在用的策略例子:新用户首日未转化召回策略。触发条件是用户注册后24小时内没有产生首次付费;执行路径是,第24小时发一条Push提醒,文案是"你的专属新人礼包即将过期";Push发出后2小时检查用户是否完成付费,如果没有,第二天再发一条短信,短信内容附一个短链;短信发出后24小时仍未转化,则将该用户加入广告平台的定向排除包(避免继续投放浪费),同时加入特价商品触达人群,等下次打开App时用弹窗承接。
这套策略用代码实现也行,但用规则引擎配置的最大好处是:运营可以自助调整,不需要每次改代码发版。而且每次策略变更都会记录版本历史,做实验复盘时有据可查。
3. 核心实操:从事件接入到多渠道下发
架构聊完,讲具体怎么做。这部分我按一次完整的触达任务从创建到下发的实际流程来讲,每一步都有可落地的操作细节。
3.1 事件采集与上报:埋点规范决定数据质量
所有人群计算的前提是事件数据准确。我们的埋点规范里有几条硬性规定,踩过坑之后定下来的:
- 事件名统一使用小写下划线,禁止大小写混用,比如
app_launch而不是AppLaunch - 用户标识字段必须包含user_id、device_id、phone_md5三个中的一个以上,缺失的要能通过ID映射补齐
- 事件属性只允许传基本类型(字符串、数字、布尔),禁止嵌套对象,序列化统一用JSON,但解析时做了一层薄封装支持路径读取
- 时间戳必须传客户端事件发生时间,不能传服务端接收时间,否则离线归因和实时判断的时间口径会不一致
事件上报的链路是:App SDK先本地聚合,每5秒批量上报一次,上报通过HTTPS到一个独立的采集端点;采集端点把数据写入消息队列;下游有两条消费链路,一条走实时计算做频控和实时人群更新,一条走离线数仓做T+1的分析报表。
写SDK时有个小技巧,本地要做好事件存储和重试机制。网络差的时候不让事件丢失,这是基本原则。我们用了一个轻量级的本地数据库存事件,批量上报失败后按指数退避重试,最多重试3次,超过就丢弃但打日志。这个策略在弱网场景下表现很稳定。
3.2 人群圈选与实时更新:怎么保证圈出来的人不过期
人群圈选有两种方式,实时计算和预计算,我们是两者结合用的。
对于触发式策略(比如"用户完成注册这个事件后进入某个人群"),用实时计算。事件中心每收到一个注册事件,就实时把该用户加入注册人群,并立即评估后续策略规则。对于分析式人群(比如"近30天有付费行为"),用预计算。每15分钟跑一次增量刷新,把新增的合格用户灌入人群。
预计算还有一个优化细节:人群版本管理。每次刷新不是清空重算,而是生成新版本,旧版本保留用于对比分析。比如我们想看新策略圈出来的人群和旧策略圈出来的人群最终转化有没有差异,可以直接拉两个版本做对比。
人群圈选还有一个很实际的问题:过期策略。一个用户今天满足"高活跃"标签,可能三天后就不满足了。如果触达任务执行周期比较长,每次下发前应该重新评估人群条件,而不是直接把之前圈好的人批次发出去。我们在发送模块里加了一个实时校验开关,下发前对每个用户强制重新跑一次高频标签校验,确保不会把不符合条件的用户发进去。
3.3 频控模块:多维度协同限流的实现
频控是整个系统成败的关键。用户被过度打扰的后果是什么?卸载、投诉、退订,任何一个都够吃一壶的。
我设计的频控是三级结构,按优先级从高到低排列:
| 频控层级 | 控制维度 | 典型配置 | 实现方式 |
|---|---|---|---|
| 全局频控 | 用户维度,跨渠道 | 每自然周最多3条营销类触达 | Redis计数器,key为user_id+周维度 |
| 渠道频控 | 用户x渠道维度 | Push每天最多2条,短信每72小时最多1条 | Redis计数器,key为user_id+channel+日期 |
| 策略频控 | 策略维度 | 同策略48小时内不重复下发 | Redis缓存,key为strategy_id+user_id |
三级频控在技术上就是三次Redis查询,但顺序很关键。先查全局,全局超限就不往下走了;再查渠道,渠道超限就只跳过这个渠道;最后查策略,策略内重复的就不再发了。三个都通过才进入真正的下发环节。
这里有一个重要的工程细节:Redis计数必须做成原子操作加超时时间。我们用的方案是INCR加EXPIRE,INCR之前先检查当前值,规避了并发场景下计数不准的问题。另外频控判断要做在发送队列前面,而不是发送之后,否则大促期间瞬间涌入的请求会把渠道打死。
注意:频控规则是可配置的,不要写死在代码里。运营会不停调整阈值,每次改代码发版根本来不及。我们把三级频控的配置全部做到了后台界面上,改完立即生效。
3.4 渠道下发与状态回传:最终一小时
下发环节看似简单,其实是最容易踩坑的地方。
我们的发送模块会先把待发送任务写入延迟队列,队列消费端按照每渠道可接受的QPS匀速拉取并调用渠道接口。这样做有几个好处:削峰填谷,避免大促时瞬间流量把渠道压垮;支持重试,调用失败的消息会重新入队,按延迟级别最多重试5次;支持定时发送,运营可以设定某个策略在每天上午10点执行,到点自动触发。
状态回传也很关键。每个渠道都要回传发送结果,包括成功、失败、抵达、展示、点击等状态。回传的数据会回流到事件中心,用来做实时频控(比如用户已经点击了这波Push,后续相关Push就不再发)、效果分析(点击率、转化率)、以及渠道质量评估。
这里说一个必须避开的坑:渠道回传的数据要设置可信度校验。一些渠道的点击数据存在虚高问题,不校验的话会把假的点击量算进转化归因里。我们的做法是对关键转化事件做交叉验证,比如短信短链的点击,会同时从服务端访问日志里核对一次,两者对不上就按较低值计。
4. 效果度量:没有数据闭环的增长都是自嗨
系统跑起来只是第一步,能说清楚效果才是真正有价值的。我见过太多团队把精力花在触达系统的功能建设上,结果上线三个月了,问起来"我们的Push点击率提高了多少""广告投放拉来的用户7日留存是多少",竟然答不上来。
4.1 归因模型:谁拿走了这笔转化的功劳
归因是增长系统里最争议也最核心的模块。
我们目前采用混合归因方案。对于广告投放,使用广告平台提供的曝光和点击数据,结合我们自己的转化事件做时间窗口归因,双触点以上且间隔较近时采用线性归因;对于站内触达,采用严格的下发时间前后对比法,即用户在收到Push后一段时间内产生的转化行为,归因给该次触达。
解释一下为什么广告归因不能照搬触达归因。广告从曝光到转化通常有时间延迟,用户可能看了三天广告才下载App,归因窗口太短会漏掉大量转化;而Push的转化是即时的,用户点了消息进去基本就能判断有没有产生行为,归因窗口太长反而会把别的渠道带来的转化算到自己头上。
我们的归因结果是写入一张事实表的,每条记录包括:用户ID、转化事件、归因渠道、归因来源、曝光/点击时间、转化时间、窗口时长。这张表是后续所有报表和实验分析的基础。
4.2 实验体系:如何科学评估策略效果
增长系统的第二个关键能力是实验。
做实验最大的误区是把实验做成了A/B之外还带着一堆变量污染。比如我们要评估"新用户首日Push召回策略"是否有效,对照组是纯自然转化,实验组是Push召回。看起来没问题,但如果实验期间刚好赶上一次大促,大促本身也会带来大量转化,这时候对比数据就失真了。
我们处理这个问题的办法是:实验期间把大促这类全局干扰因素作为协变量记录,分析时做分层统计,或者干脆延后上线实验窗口,避开大促周期。另外实验分流必须做到用户级随机且稳定,我们用一致性哈希把用户分配到实验组和对照组,确保同一个用户永远落在同一个组里。
实验报表一定要看净增量,而不是毛增长。比如Push召回策略带来的毛增长是5%,但自然转化本来就有4%,那实际净增量只有1%。很多团队不看基线,直接拿毛增长邀功,这是很危险的。
4.3 投放与触达的联合分析:全局视角下的预算分配
把广告和触达的数据打通之后,出现了一些单看任何一个系统都发现不了的洞察。
最典型的例子是触达对广告的增效作用。我们在实际数据中发现,用户如果先被Push触达过一次,后面再看广告时的转化率会显著提升。大概是30%左右的提升幅度。原因并不复杂:Push让用户对App有了认知,广告再出现时用户更容易接受。反过来,如果用户已经被短信轰炸过三轮都没转化,这时候再投放广告给他,转化率反而比大盘低很多。
基于这类分析,我们开发了一个简单的预算分配建议模块。它会定期扫描当前的人群包,把用户按"近30天触达次数x转化概率"分成四个象限:高触达高转化、高触达低转化、低触达高转化、低触达低转化。高触达低转化的用户明确列入广告排除包和短信退订候选名单,预算集中到低触达高转化的用户身上。
这个模块虽然逻辑只有几十行,但带来的预算效率提升肉眼可见。因为广告平台按出价扣费,优化人群方向的收益通常比单纯调出价大得多。
5. 高频问题与排查手册
不管系统设计多完善,上线后一定会遇到问题。这一章把我实际遇到过的、以及同行交流中高频出现的问题整理成了一份速查表,再挑几个典型的详细讲讲。
5.1 送达率为什么突然掉了
症状:前一天Push送达率还有90%,第二天掉到70%。
排查思路:按这个顺序查错不会漏。
- 看事件中心是否有批量异常,是否出现大规模上报失败
- 看厂商推送通道的Token失效比例,用户卸载App后Token会失效,如果前一天有大规模卸载,送达率必然掉
- 看Push到达回执数据,有些机型会在省电模式下延迟到达,日志里表现为到达时间晚于发送时间,这不算丢失
- 看了渠道的限流状态,厂商在营销大促期间会临时限制单App的推送量,超限的请求会被拒绝
我实际遇到过的案例是:运营把一条营销Push的发送量从50万提到200万,没有预沟通,厂商侧在发送高峰期直接限流,导致超过30%的消息在队列里排队等待,用户第二天才收到。后来我们加了一个渠道预警机制,发送量超过日常峰值三倍时自动报警并进入人工审批流程,之后这个问题再没出现过。
5.2 频控为什么"失效"了
症状:配置了每周最多3条营销消息,用户却收到4条。
排查思路:频控失效90%的情况是ID映射出了问题。
我们在一次事故中就踩过这个坑。用户先在老版本App上注册,device_id用了旧格式,后来升级到新版本,device_id格式变了,系统把它当成一个新用户,所有频控计数都从零开始算。结果这个用户一周内收到了6条消息。
解决方案是两件事:第一,ID映射表要做全量回刷和周期校验,不能只靠实时关联;第二,频控判断时除了userId之外,还要带上前端传的device_id做二次匹配。另外一个隐蔽的原因是时区问题,Redis里存日期要统一用UTC,如果用了本地时间,跨时区用户(比如海外用户)的频控周期会被算乱。
5.3 短信渠道到达率下降
症状:运营商通道的短信送达率从95%降到85%。
排查思路:先查内容,再查通道,最后查号段。
短信到达率下降最常见的原因是内容触发了运营商的关键词审核,被拦截。很多团队不知道,同一个模板在白天和晚上审核宽严程度不一样,晚上发营销短信极大概率被当作骚扰拦截。另外手厅用户(手机号归属在运营商自身的用户)对营销短信的拦截率远高于普通用户,遇到大面积失败就检查是不是圈选时误捞了大量这类号码。
我们后来把短信渠道的发送策略改成了:高风险模板只走催付、发货通知等交易类场景,营销类文案一律降低频次,号码库增加运营商用户号段的剔除逻辑。这个调整后,短信到达率稳定在95%以上。
5.4 报表数据对不上
症状:广告平台报表显示点击10000次,我们自己的报表显示只有6000次。
排查思路:这是数据口径问题,不是bug。
广告平台的点击是用户点击广告素材的次数,而我们的报表只统计了点击后进入App且完成了启动事件的次数。点击了广告但页面没加载出来、用户误点、或者广告页面关闭过快,这些都不会进入我们的事件统计里。两边对不上是正常的。
我们的处理方式是:报表里同时展示两个口径,广告口径和自有口径,让决策者明确知道差异来源。同时用渠道回传的数据与广告平台数据做每日对账,偏差超过5%时触发告警,确认是否存在数据回传丢失。
6. 一些落地过程中的经验反思
最后写点不太会被写进方案文档里的体会。
第一个经验:不要过度设计系统分层,先把主流程跑通。我见过团队花了三个月搭微服务网格、做流量染色、搞全链路追踪,结果人群圈选和触达下发的主流程还没走通过。这套系统的价值在于让投放和触达协同起来,第一版只要能做到"从同一个受众池里,用规则控制多渠道下发",就已经产生价值了。架构细节后面慢慢补,完全来得及。
第二个经验:增长系统最难的永远不是技术,而是组织协同。投放团队和运营团队通常有不同的KPI,投放看拉新成本和ROI,触达看存量活跃和召回率。如果两边不在同一个系统里看数据,永远各说各话。平台能不能落地,一半取决于技术,另一半取决于是否把两边的目标口径统一了。让人惊喜的是,系统上线后我们把投放和触达的报表放到同一个页面,很多过去要开会吵半天的问题,看一眼数据就清楚了。
第三个经验:要敢于对渠道说"不"。广告平台和推送厂商都有自己的利益诉求,它们希望你多投、多发。但你的系统要做的是以用户价值为中心调度它们。有些渠道愿意配合开放更多的数据回传接口,有些渠道不愿意。遇到不愿意的,在同等条件下优先把预算分配给那些数据透明、配合度高的渠道,长期来看效果更好。
第四个经验:留好数据和配置的版本历史。增长策略每天都在变,复盘时最需要回答的问题是"这个策略以前跑过没有,当时的成本收益是多少"。如果没有版本记录,就只能靠人的记忆。我们现在的做法是每次策略变更自动打一个快照,包括人群圈选条件、频控配置、渠道组合、文案内容,全部存下来。这个成本很低,但对复盘的价值极高。
这套系统上线到现在一年多,从最初的三个人做四个月,到现在的两人小组持续迭代,它支撑着公司绝大多数的增长触达和广告投放调度。每次看到运营同学在后台拖拽配置一个新的召回策略,十几分钟就上线,我都会觉得当时坚持"投放和触达统一收口"这个设计初衷是做对了的。
如果你也在做类似的系统,我的建议是:先把受众池和频控做扎实,再去搞复杂的归因和实验能力。受众不准、频控失控,后面的花活做得再多也没用。先让基础能力稳定跑半年,积累出可信的数据基座,增长策略的优化空间才会真正打开。