3. 规则语法设计:DRL语法基础、条件表达式、动作脚本、规则优先级与冲突解决

规则语法是规则引擎的核心语言,它决定了业务人员如何描述风控逻辑、工程师如何维护规则库。本章以 Drools Rule Language(DRL)为蓝本,讲解规则语法的四大支柱:基础语法结构、条件表达式、动作脚本以及优先级与冲突解决策略。掌握这些内容后,你将能够设计出可读、可维护、高性能的风控规则集。

3.1 DRL 语法基础

DRL 是一种声明式规则语言,每条规则由 rule 关键字定义,包含 when(条件)和 then(动作)两个主要部分。一个典型的 DRL 规则文件结构如下:

package com.risk.engine.rules

import com.risk.engine.model.Transaction
import com.risk.engine.model.UserProfile

rule "高风险交易拦截"
    salience 100
    no-loop true
    when
        $tx: Transaction( amount > 10000, currency == "CNY" )
        $user: UserProfile( riskLevel == "HIGH" )
    then
        $tx.setBlocked(true);
        $tx.setReason("高风险用户大额交易");
        update($tx);
end

关键语法元素:

元素 说明 示例
package 规则包名,用于逻辑分组 package com.risk.engine.rules
import 导入 Java 或 POJO 类 import com.risk.engine.model.Transaction
rule ... end 规则体定义 rule "规则名称" ... end
when 条件部分(LHS) when $tx: Transaction( amount > 10000 )
then 动作部分(RHS) then $tx.setBlocked(true);
salience 优先级数值,越大越优先 salience 100
no-loop 防止规则自身触发循环 no-loop true
设计建议:规则名称应体现业务语义,如“高风险交易拦截”而非“Rule001”。包名按业务域划分(如 payment、login、coupon),便于后期维护和权限管理。

3.2 条件表达式:模式匹配与约束

条件表达式是规则引擎的“眼睛”,它从工作内存(Working Memory)中筛选出符合条件的事实对象。DRL 支持丰富的条件表达方式:

3.2.1 基本字段约束

// 数值比较
$tx: Transaction( amount >= 5000, amount <= 50000 )

// 字符串匹配
$user: UserProfile( city == "Beijing", riskLevel in ("HIGH", "CRITICAL") )

// 布尔判断
$tx: Transaction( isCrossBorder == true )

// 空值检查
$tx: Transaction( merchantId != null )

3.2.2 复合条件与绑定变量

// 绑定变量并在后续条件中引用
$tx: Transaction( $amt: amount, $mid: merchantId )
$merchant: Merchant( id == $mid, riskScore > 80 )
// 此时 $amt 可在 then 部分使用

3.2.3 条件组合与嵌套

// 使用 || 和 && 组合条件
$tx: Transaction( (amount > 10000 && currency == "USD") || (amount > 50000 && currency == "CNY") )

// 使用 exists 和 not 判断存在性
not ( SuspiciousIp( ip == $tx.ipAddress ) )
exists ( BlacklistCard( cardNo == $tx.cardNo ) )
性能陷阱:避免在条件中使用复杂的方法调用(如 $tx.getHistory().size() > 10),这会降低模式匹配效率。建议将计算结果预置为事实对象的字段,或使用 eval() 函数(但 eval 会破坏索引优化,谨慎使用)。

3.3 动作脚本:规则触发后的行为

动作脚本(RHS)是规则触发后执行的代码块,通常用于修改事实、记录日志、调用外部服务或产生事件。动作脚本应保持轻量、幂等、可追溯。

3.3.1 常用动作类型

动作类型 DRL 示例 说明
修改事实 $tx.setBlocked(true); update($tx); 修改后必须调用 update() 通知引擎
插入新事实 insert(new AuditLog($tx.getId(), "BLOCKED")); 插入审计日志对象到工作内存
删除事实 retract($tx); 从工作内存中移除对象
调用服务 riskService.sendAlert($tx.getUserId(), "HIGH_RISK"); 通过全局变量或 Spring 上下文调用
设置全局变量 drools.getWorkingMemory().setGlobal("result", "BLOCK"); 将结果传递到规则引擎外部

3.3.2 动作脚本最佳实践

rule "设备指纹异常-自动处置"
    salience 80
    when
        $device: DeviceFingerprint( anomalyScore > 0.9 )
        $session: Session( deviceId == $device.deviceId )
    then
        // 1. 修改事实
        $session.setForceLogout(true);
        update($session);

        // 2. 插入审计
        AuditLog log = new AuditLog();
        log.setEventType("DEVICE_ANOMALY");
        log.setDetail("设备异常分数: " + $device.getAnomalyScore());
        insert(log);

        // 3. 调用外部服务(通过全局变量注入)
        alertService.sendNotification($session.getUserId(), "您的账号存在异常登录,已强制下线");
end
动作脚本原则:每个规则只做一件事,避免在 then 中写超过 10 行代码。复杂的业务逻辑应封装到 Java 服务中,规则只负责编排和决策。

3.4 规则优先级与冲突解决

当多个规则的条件同时满足时,引擎需要决定执行顺序。风控场景中,优先级错误可能导致误放行或误拦截。DRL 提供以下机制:

3.4.1 显式优先级:Salience

salience 是一个整数值,默认值为 0。数值越大,优先级越高,越先执行。

rule "黑名单拦截" salience 100
    when $user: UserProfile( blacklisted == true )
    then $user.setBlocked(true); end

rule "VIP用户豁免" salience 50
    when $user: UserProfile( vipLevel >= 5 )
    then $user.setBlocked(false); end

上述场景中,如果用户既是黑名单又是 VIP,黑名单拦截 先执行(salience 100),将用户拦截;随后 VIP用户豁免 执行(salience 50),又将拦截取消。这可能导致逻辑错误,需要引入冲突解决策略。

3.4.2 冲突解决策略

策略 机制 适用场景
Salience 排序 按 salience 值降序执行 明确业务优先级,如“黑名单优先于白名单”
议程分组(Agenda Group) 将规则分组,按组顺序执行 分阶段处理:先校验、再决策、后处置
激活组(Activation Group) 同一组内只执行一条规则 互斥决策:如“通过”和“拒绝”不能同时生效
规则流(Rule Flow) 通过流程图控制规则执行顺序 复杂业务流程编排

3.4.3 使用 Activation Group 解决互斥

rule "高风险-拒绝交易"
    activation-group "decision"
    salience 100
    when $tx: Transaction( riskScore > 90 )
    then $tx.setDecision("REJECT"); end

rule "中风险-人工审核"
    activation-group "decision"
    salience 50
    when $tx: Transaction( riskScore > 70 && riskScore <= 90 )
    then $tx.setDecision("REVIEW"); end

rule "低风险-自动通过"
    activation-group "decision"
    salience 0
    when $tx: Transaction( riskScore <= 70 )
    then $tx.setDecision("APPROVE"); end

三个规则属于同一个 activation-group "decision",引擎只会执行其中 salience 最高且条件满足的那一条,确保每笔交易只有一个最终决策。

冲突解决常见错误:不要依赖规则在 DRL 文件中的物理顺序来决定执行顺序。引擎不保证文件顺序与执行顺序一致,必须显式使用 salience 或 agenda-group 控制。

3.5 规则语法设计全景图

下图展示了 DRL 规则语法的完整结构,从包声明到动作执行,以及优先级与冲突解决机制在规则执行流程中的位置:

DRL 规则语法结构全景图 规则头部:package / import / global / function 规则体:rule "名称" 属性块 (salience / no-loop / activation-group) when 条件部分 (LHS) 模式匹配 · 字段约束 · 绑定变量 exists / not / eval / 复合条件 then 动作部分 (RHS) 修改事实 · insert / update / retract 调用服务 · 设置全局变量 · 日志 冲突解决:Salience 排序 · Agenda Group 分组 · Activation Group 互斥 · Rule Flow 编排 规则执行结果:决策输出 · 事实变更 · 事件通知 提示:优先级设计应在规则编写阶段确定,避免运行时动态调整导致不可预期的冲突

3.6 规则优先级设计模式

在实际风控项目中,规则数量可能达到数百甚至数千条,合理的优先级设计至关重要。以下是三种常见设计模式:

模式 Salience 分配策略 典型场景
金字塔模式 黑名单/白名单 100,高风险 80,中风险 50,低风险 20 先执行强规则,后执行弱规则,避免弱规则覆盖强规则
阶段模式 校验阶段 100,决策阶段 50,处置阶段 0 结合 Agenda Group 分阶段执行,每阶段内再按 salience 排序
互斥模式 同一 Activation Group 内,按业务权重分配 salience 决策互斥:拒绝 > 人工审核 > 自动通过
实践建议:在规则管理系统中,将 salience 值作为规则元数据的一部分,允许业务人员在界面上通过“优先级:高/中/低”来间接控制,后台映射为 salience 100/50/0。避免直接暴露 salience 数值给非技术人员。

3.7 本章小结

规则语法设计是风控规则引擎的基石。通过本章学习,你应掌握:

  • DRL 基础结构:package、import、rule、when、then 的完整写法
  • 条件表达式:字段约束、绑定变量、复合条件、存在性判断
  • 动作脚本:事实修改、插入、删除、服务调用、全局变量
  • 优先级与冲突解决:salience、agenda-group、activation-group 的适用场景