风控策略同学发现一种新的薅羊毛手法,要加一条规则。如果规则写在代码里,走的是提需求、排期、开发、测试、发版这条链路,快的话一周,慢的话两周。黑产的手法生命周期可能也就几天,规则上线的时候黑产早薅完离场了。这个时间差,就是硬编码风控最大的死穴。
蜂控平台早期就是这个状态。规则和业务逻辑混在一起,改一条规则要走完整的发版流程,策略同学被开发排期卡得没脾气,开发同学被频繁的规则改动拖得没精力做正经需求。后来做情报中心重构,把规则引擎独立出来,迭代周期直接砍掉一半。核心动作就一个,把规则的变更权和代码的变更权解耦,策略同学自己配规则,秒级生效,开发不再参与。
这件事的难点不在于写一个规则引擎有多复杂,在于怎么设计让策略同学能驾驭、让性能扛得住、让规则可灰度可回滚。下面按我踩过的坑来讲。
硬编码的困境,慢是原罪
风控讲究一个快字,攻击出现到规则上线的时间窗口越短,损失越小。硬编码的链条慢在它把规则的每一次微调都变成一次完整的软件交付。
开发同学拿到一条规则需求,要理解规则逻辑,翻译成代码,写单测,过测试环境,灰度,全量。这中间任何一步卡住,规则就上不了线。更要命的是规则需求往往不是一次说清楚的,策略同学跑两天数据发现阈值要调,又得再来一轮。开发同学疲于应付这些零碎改动,真正的系统建设反而排不上。
这个困境的根子在于规则和代码耦合。规则其实是一种数据,是策略的表达,它本该和业务代码分离。硬编码把它们焊死,导致改数据要走改代码的流程。规则引擎要解决的就是把这个焊接点拆开。
DSL 的选择,别一上来就想自研
规则要用一种策略同学能写、引擎能执行的方式来表达,这就是 DSL,领域特定语言。DSL 怎么选,决定了整个引擎的易用性和可维护性。
有几条路可以走。最简单的是 JSON 结构化,规则的条件和动作用 JSON 描述,前端做成可视化表单让策略同学填。
图:四种 DSL 选型的取舍对比——表达式引擎(Aviator/Groovy)兼顾表达力与成熟度,是实战最常见的选择;自研 DSL 性价比最低
{ "ruleId": "new_user_large_amount", "name": "新号大额交易拦截", "condition": "user.registerDays < 7 && trade.amount > 10000", "action": "CHALLENGE", "expireAt": "2025-12-31"}JSON 的好处是门槛低,可视化好做,非技术同学上手快。它的短板是表达力有限,复杂逻辑嵌套深了 JSON 会很臃肿,可读性反而下降。
第二条路是表达式引擎。Aviator、Groovy 这类轻量表达式引擎,条件直接写成表达式字符串,表达力强,Java 生态集成成熟,性能也好。复杂规则用表达式,简单规则用 JSON,两者结合是实战里最常见的选择。
第三条路是成熟规则引擎,Drools 是代表。功能全,支持前向推理、规则冲突解决,但它太重了。学习曲线陡,配置复杂,对中小项目来说引入 Drools 的维护成本超过收益。我个人的判断是,除非业务规则确实复杂到需要推理引擎,否则不碰 Drools。
第四条路是自研 DSL。看着诱人,能完全贴合业务,但维护成本高得吓人。语法设计、解析器、调试工具、文档,每一项都是投入。除非团队有专人长期维护,否则自研 DSL 最后会变成没人敢动的包袱。这和当初 Matrix 弃用 Flowable 自研流程引擎的逻辑不一样,流程引擎的复杂度值得自研,规则引擎的表达需求 Aviator 基本能覆盖,自研性价比不高。
规则的数据模型,条件加动作加约束
不管用什么 DSL,一条规则的数据模型是稳定的,条件、动作、阈值、有效期。
条件是规则的核心,描述在什么情况下触发。条件引用的是画像数据,用户注册天数、交易金额、设备指纹关联的账号数、历史交易频次。动作是触发后怎么处置,拦截、挑战验证、放行、降权、标记观察。阈值是条件里的具体数值边界。有效期是规则生效的时间窗口,很多规则是针对特定活动或特定攻击波次的,活动结束或攻击消退就该下线。
这套模型的好处是规则的可组合性。条件可以复合,动作可以叠加,规则之间可以按优先级排序。策略同学拼装规则就像搭积木,而不是每次从头写逻辑。
规则匹配的性能,不能全量遍历
风控规则可能上千条,每笔交易如果全量遍历执行,性能扛不住。高峰期 QPS 几千,每条请求过几百上千条规则的表达式,CPU 很快就打满。
实战做法是分层匹配。第一层按维度索引粗筛,按交易场景、风险等级、用户标签把候选规则缩到几十条。比如信用卡交易只匹配和信用卡相关的规则,新用户只匹配新用户规则。这一层是内存里的位图或倒排索引,极快。第二层对候选规则执行表达式,这一层才是真正跑 Aviator 的地方。第三层按优先级处理命中的规则,决定最终处置。
这套分层设计的核心思想是用便宜的操作先排除大部分规则,把昂贵的表达式执行留给真正需要的少数规则。规则引擎的性能优化和数据库查询优化是同一个思路,都是减少不必要的计算。
图:规则分层匹配——索引粗筛(快)→ 表达式执行(贵)→ 优先级合并,逐层缩小计算量,和数据库查询优化同理
决策合并,风控宁可误伤不放过
一笔交易可能命中多条规则,处置各不相同。规则 A 说放行,规则 B 说挑战验证,规则 C 说拦截。最终怎么办。
合并策略通常取最严格。任何一条规则判拦截,最终就拦截,任何一条判挑战,最终就挑战。风控的取向是宁可误伤不放过,放过一个黑产的代价远大于误伤一个正常用户。误伤了正常用户,他来投诉,客服处理,最多流失一个用户。放过了黑产,可能是几十万的资损。
这个取向听起来粗暴,但它是风控行业的基本共识。不过最严原则有个前提,规则本身的精度要够。如果规则误伤率很高,最严合并会把误伤放大,正常用户大面积被拦,业务就没法做了。所以决策合并和后面要讲的规则效果分析是一对,合并策略保证不放过,效果分析保证不乱拦。
影子模式,新规则上线前的必经一步
新规则直接上线拦截,最大的风险是误伤。规则写错了,条件逻辑有漏洞,阈值拍脑袋定的,任何一个都可能让规则误伤大量正常用户。等发现的时候,影响已经造成。
影子模式是规避这个风险的标准流程。新规则先以影子模式运行,只记录命中不实际拦截。跑一段时间,观察这条规则的命中率、误伤率,和策略同学的预期对比。命中率和预期差不多、误伤率在可接受范围,再灰度生效,先对一部分流量实际拦截,观察无误伤后全量。任何一个环节发现问题,回滚或调整。
这套流程的核心价值是把规则的验证前置到生产环境。测试环境再怎么测,都模拟不了真实流量的分布和长尾。只有拿真实流量跑,才知道规则的命中和误伤到底长什么样。影子模式让这个验证过程不产生实际影响,是规则引擎能安全迭代的保障。
图:规则生命周期——新规则必须经历 影子(只记录)→ 灰度(部分拦截)→ 全量 三阶段,任何一环发现问题即回滚,杜绝直接上线拦截
规则效果分析,用数据驱动规则迭代
规则不是上线就完事,每条规则的效果要可量化,否则规则库会越长越臃肿,最后没人知道哪些规则在起作用。
核心指标有几个。命中率,规则多久触发一次,命中率太低说明规则覆盖的攻击很少,可能是规则过时了。准确率,拦截的里面真黑产占多少,准确率低说明规则在乱拦。误伤率,拦截的里面正常用户占多少,误伤率高说明规则该收紧或下线。覆盖面,这条规则防御的是哪类攻击。
效果差的规则要下线,效果好的规则要加强,这套迭代靠数据说话而不是经验。一个成熟的风控系统,规则的上下线频率应该很高,因为攻防是动态的,规则库也必须是动态的。规则只增不减,是风控系统僵化的信号。
上游数据集市,规则引擎的地基
规则引擎再好,也依赖上游的数据质量。规则引用的画像数据如果不全、不准、不及时,规则写得再精巧也没用。
风控数据集市是规则引擎的地基,包含用户画像、设备画像、交易流水、关系网络。用户画像记录历史行为和信用,设备画像记录指纹和关联账号,交易流水记录历史交易,关系网络记录账号之间的关联关系。规则引擎消费这些数据做判断。
数据集市的工程挑战是规模和时效。TB 级的交易数据要能快速检索,画像要能实时更新反映最新行为。蜂控平台用 ES 做辅助索引加 HBase 大宽表存数据,就是为了在 TB 级发票数据上支撑快速查询。这个架构直接服务于风控规则引擎的实时决策需求,画像查询的延迟直接决定规则引擎的响应时间。
数据集市的质量和规则引擎的能力必须同步建设。只建规则引擎不建数据集市,规则引用不到数据,形同虚设。只建数据集市不做规则引擎,数据沉淀了但用不起来。两者是风控系统的两条腿,缺一不可。
影子模式是规则引擎上线流程里最容易省略也最不能省的一步。直接上线拦截,规则一旦写错,误伤的规模可能是几千几万笔正常交易,事后补救的成本远高于影子模式跑几天的成本。影子模式不是可选项,是规则上线的硬性前置,团队要把这条固化进流程,不允许跳过。








