智能风控决策引擎系统可落地实现方案(一):DSL

一、决策引擎系统介绍

风控决策引擎系统是在大数据支撑下,根据行业专家经验制定规则策略、以及机器学习/深度学习/AI领域建立的模型运算,对当前的业务风险进行全面的评估,并给出决策结果的一套系统。

在这里插入图片描述

决策引擎,常用于金融反欺诈、金融信审等互金领域,由于黑产、羊毛党行业的盛行,风控决策引擎在电商、支付、游戏、社交等领域也有了长足的发展,刷单、套现、作弊,凡是和钱相关的业务都离不开风控决策引擎系统的支持保障。决策引擎和规则引擎比较接近(严格说决策引擎包含规则引擎,之前也有叫专家系统,推理引擎),它实现了业务决策与程序代码的分离。

一套商业决策引擎系统动辄百万而且需要不断加钱定制,大多数企业最终仍会走上自研道路,市场上有些开源规则引擎项目可参考,比较出名的开源规则引擎有drools、urule,都是基于Rete算法,都是基于java代码实现,一些企业会进行二次开发落地生产。而这类引擎功能强大但也比较“笨重”,易用性以及定制性并不够好,对其他语言栈二次开发困难。今天我们对决策引擎进行抽象建模,一步步打造一套简单实用的实现,这里会基于golang语言进行实现。

关于如何实现决策引擎的文章市面极少见,实践生产落地的经验分享也基本没有。我会结合工作实践及个人思考,从业务抽象建模,产品逻辑规划以及最终技术架构和代码实现等方面给出全方位的解决方案。

二、规则和决策抽象建模

举例说明什么是规则和决策:

“如果年龄小于18岁,那么就拒绝处理”

对这条规则进行抽象建模成条件表达式:特征 (年龄) 运算符(小于) 阈值(18) —> 触发结果(拒绝)

这里产生几个概念特征feature、运算符operator、阈值value,这三要素构成条件表达式condition,加上触发结果decision,组成了规则rule的基础元素。

进一步举例:

“如果年龄小于18岁或年龄大于50岁,那么就拒绝处理”

条件表达式1:特征 (年龄) 运算符(小于) 阈值(18)

条件表达式2:特征 (年龄) 运算符(大于) 阈值(50)

逻辑关系:(或)

触发结果:(拒绝) 任何一个为false即为拒绝

这里多了逻辑关系,规则可由多个条件表达式组成,对表达式结果再进行逻辑运算。

再举个例子:

“如果职业是学生,那么就拒绝处理”

条件表达式:特征(职业) 运算符(等于) 阈值(学生)触发结果(拒绝)

其他可选阈值:老师、工人、农民、程序员

“如果订单返回结果中包含exception字样,那么就异常处理“

条件表达式:特征(订单返回结果)运算符(contain包含) 阈值(exception)触发结果 (异常)

这里有了不同的特征类型,一般特征类型总结如下:

  • 数值型:对应运算符可以有 >、<、=、>=、<=、==、!=值必须为数字。

  • 枚举型:对应运算符只有==值为字符串数组[…]{“学生”,“老师”,“工人”,“农民”,“程序员”}

  • 字符串型:对应运算符有= 、 != 、like 、in 、contain值为字符串或字符串数组

触发结果:可以为“通过”、“拒绝”、“记录”、“告警”、“异常”等,任意自定义结果。

对于一些名单类特征,比如规则是“命中黑名单则触发拒绝”,将命中结果抽象为枚举型特征,对应条件表达式就是:命中黑名单 等于
true/false。

这里做个总结:一条规则的执行,先通过数据(身份证号)计算出特征(年龄),然后带入条件表达式(年龄<18)计算,并对多个表达式结果做逻辑运算,最终根据逻辑运算决定是否触发结果。

在这里插入图片描述

三、对规则代码实现

1.直接硬编码实现

var result string
const (
    PASS = "pass"
    REJECT = "reject"
)
func rule1(age int) {
    if age < 18 || age > 50 { //18,50 can be stored in config file
        result = REJECT
    }
}
func rule2(hitBlacklist bool) {
    if hitBlacklist {
        result = REJECT
    }
}
func main() {
    rule1(19) //feature data get from idcard
    rule2(false) //feature data get from db
    fmt.Println(result)
}

硬编码实现,规则迭代成本高,不管是要改阈值,还是增减规则或调整表达式,都需要修改代码对程序进行发布,规则较多时可维护性极差,调整时效变差,出错概率增加。

那么如何把规则抽离出来,通过程序自动化解析,实现不用开发维护程序即可调整?这里通过自定义DSL,并实现一套DSL解析引擎。

2.自定义DSL实现

DSL是什么?全称是Domain Specific Language,领域特定语言。举个例子SQL就是数据库领域的交互语言,它定义了一套标准语法,各大数据库厂商(如mysql、oracle)对其进行解析实现,任何人都可通过编写SQL实现与数据库的交互。类似还有正则表达式、HTML&CSS等均形成了自己的语法标准。

那么为了完成规则引擎,我们也实现一套自定义DSL语法,并对其解析,通过DSL语法实现与规则引擎的交互,先参考看看droolsDSL语法长什么样?

rule "rule1"
when
  ( type == "cash", price == 5 ) || ( type == "card", price <= 10 )
then
  rs = "pass"
end

可以看出基本类似一个简版编程语言,对该DSL进行解析的难度还是比较高的,而droolsrete算法也比较复杂。这里我们根据业务做个简单化抽象建模,利用yaml格式生成一个具有风控行业语义的自定义DSL工作中一般会将用户在前端页面上配置的规则存到DB中,然后加载到本地缓存,根据规则配置执行策略,因为要保存到DB中,所以大部分情况下是保存为JSON格式,方便理解和查看,不过技术实现上和yaml区别不大,都是序列化反和序列化而已,该项目以yaml演示。

rule:
rule_id: 139
rule_name: "test2"
conditions:
- condition:
  feature: feature2
  operator: LT
  value: 18
- condition:
  feature: feature3
  operator: GT
  value: 50
logic: OR
depends: [feature2,feature3]
decision: reject

定义了DSL语法格式,接下来就对其进行解析,先将yaml格式加载并转成struct

type Rule struct {
    Conditions []Condition `yaml:"conditions,flow"`
    RuleId     string      `yaml:"rule_id"`
    RuleName   string      `yaml:"rule_name"`
    Logic      string      `yaml:"logic"`
    Decision   string      `yaml:"decision"`
    Depends    []string    `yaml:"depends"`
}
type Condition struct {
    Feature  string      `yaml:"feature"`
    Operator string      `yaml:"operator"`
    Value    interface{} `yaml:"value"`
}
//load dsl from db or file
func loadDsl() *Dsl {
    dsl := new(Dsl)
    yamlFile, err := ioutil.ReadFile("ruleset.yaml")
    if err != nil {
        panic(err)
    }
    err = yaml.Unmarshal(yamlFile, dsl)
    if err != nil {
        panic(err)
    }
    return dsl
}

rule结构体进行解析,rule包含condition,循环对condition进行运算,然后将所有结果根据rule.Logic再进行逻辑运算,最终结果为true则输出触发结果rule.Decisionfalse则输出空值。

//parse rule
func (dsl *Dsl) parseRule(rule *Rule) string {
    var conditionRs = make([]bool, 0)
    depends := getDepends(rule.Depends)
    for _, condition := range rule.Conditions {
        if data, ok := depends[condition.Feature]; ok {
            conditionRs = append(conditionRs, ConditionExpression(condition.Operator, data, condition.Value))
        }
    }
    logicRs := LogicExpression(conditionRs, rule.Logic)
    if logicRs {
        return rule.Decision
    } else {
        return ""
    }
}

其中条件表达式如何解析呢?

"data  condition.Operator condition.Value"

如果是pythonphp直接用eval即可实现,但golang没有这么好使的函数。

这时候“编译原理”可以登场了,将表达式转成AST抽象语法树,进行语法解析,利用栈计算的方式执行,这里推荐使用github开源库govaluate,已帮我们实现了一些逻辑运算,可以直接使用。

var operatorMap = map[string]string{
    "GT":  ">",
    "LT":  "<",
    "GE":  ">=",
    "LE":  "<=",
    "EQ":  "==",
    "NEQ": "!=",
}
//condition expression:feautre value, operator ,threshold value
func ConditionExpression(operator string, feature interface{}, value interface{}) bool {
    var params = make(map[string]interface{})
    params["feature"] = feature
    params["value"] = value
    var expr *govaluate.EvaluableExpression
    if _, ok := operatorMap[operator]; !ok {
        panic("not support operator")
    }
    expr, _ = govaluate.NewEvaluableExpression(fmt.Sprintf("feature %s value", operatorMap[operator]))
    eval, err := expr.Evaluate(params)
    if err != nil {
        panic(err)
    }
    if result, ok := eval.(bool); ok {
        return result
    } else {
        panic("convert error")
    }
}

条件表达式依赖的特征数据怎么来?

这里通过getDepends(rule.Depends),在rule中冗余了depends字段,存储规则中所有条件表达式需要的特征集合,getDepends函数实现外部url/rpc/db通讯调用以及计算特征逻辑。

获取特征可以在需要时实时调用计算,也可以提前做预加载预计算,实际上这部分可以抽象成单独系统平台(如外部数据平台、特征引擎),与决策引擎配合使用,后续文章中会有介绍。

需要对多个表达式结果根据rule.Logic再做一次运算,还是使用govaluate进行与或运算

var logicMap = map[string]string{
    "OR":  "||",
    "AND": "&&",
}
//use logic for all the condtions result
func LogicExpression(result []bool, logic string) bool {
    resultLen := len(result)
    if resultLen == 0 {
        return false
    }
    if resultLen == 1 {
        return result[0]
    }
    var exprStr string
    for i := 0; i < resultLen; i++ {
        exprStr += fmt.Sprintf(" %t", result[i])
        if i != (resultLen - 1) {
            exprStr += fmt.Sprintf(" %s", logicMap[logic])
        }
    }
    expr, _ := govaluate.NewEvaluableExpression(exprStr)
    eval, err := expr.Evaluate(nil)
    if err != nil {
        panic(err)
    }
    if result, ok := eval.(bool); ok {
        return result
    } else {
        panic("convert error")
    }
}

至此,就实现了规则的完整DSL表述和解析,那么如果多条规则在一起可以组成一个规则集。那么规则集有什么用呢?

规则集是对一组规则的集合封装。根据业务逻辑不同,规则集有不同的规则组合策略。

  • 规则集中只要一个规则命中拒绝结果即中断退出。这种方式对规则进行顺序执行,即多个规则是互斥的,从前往后,找到第一个满足的规则执行后续逻辑。

  • 规则集中所有规则要全部执行一遍。这种方式可并发执行规则,可能会命中多种不同结果,设定结果优先级,输出优先级最高的结果(如拒绝 > 记录 > 通过)

  • 规则集中所有规则要全部执行一遍。这种方式可并发执行规则,可能会命中多种不同结果,命中的分支都可以继续往后执行,即多个规则是不互斥的,通过的分支都可执行分支对应的后续逻辑。

ruleset:
  - rule:
    rule_id: 129
    rule_name: "test1"
    conditions:
    - condition:
      feature: feature1
      operator: GT
      value: 50
    logic: AND
    depends: [feature1]
    decision: reject
  - rule:
    rule_id: 139
    rule_name: "test2"
    conditions:
    - condition:
      feature: feature2
      operator: LT
      value: 18
    - condition:
      feature: feature3
      operator: GT
      value: 50
    logic: OR
    depends: [feature2,feature3]
    decision: reject     

上面就是一个完整规则集DSL,至此调整规则只要修改yaml内容即可,做到了规则调整不再依赖程序变更。可以看到最终解析执行结果如下:

在这里插入图片描述

但这种方式对风控分析师来说还是比较困难,编写DSL也容易出错,那么有没有一种办法直接赋能业务,让他们做到可以直接可视化傻瓜化调整?

3.实现可视化规则配置后台

设计实现一个可视化配置后台。添加规则设计如下:
在这里插入图片描述

特征需要预先加工好并初始相关数据,可以做相关特征管理系统,维护特征的元信息,然后策略配置时只能选择已有特征

运算符根据特征类型选择,如

  • 数值型有大于、小于、等于…
  • 枚举的运算符只有等于

阈值根据特征类型是数值或字符串可以自己手工输入,是枚举型只能在规定集合中选择,这些都是通过特征加工及初始化特征来实现。

在这里插入图片描述

规则集添加配置如下:

在这里插入图片描述

使用mysql数据库进行数据添加变更存储,具体数据表实现比较简单。

  • ruleset表:id,name

  • rule表:id,name,decision,ruleset_id

  • condition表:id,name,feature,operator,value,rule_id

  • feature表:id,name,type,support_operator,support_value

至此,规则变更可通过后台可视化修改,然后生成相应yaml格式,通过规则引擎解析。关于数据库表数据如何生成yaml格式可直接参考完整代码。

4.其他考虑

4.1 规则内多个条件表达式有复杂逻辑关系如何实现

对应规则中更复杂的逻辑组合实现,可以对表达式condition依次命名为A,B,C,D,E,然后根据rule.logic做与或逻辑计算。

logic= (A or B) AND (C or D) AND E

通过抽象后,利用govaluate "(A || B) && (C || D) &&E"实现起来也就比较简单了。

后台界面设计可以做成如下情况:

在这里插入图片描述

4.2 特征衍生及特征逻辑计算

当前对于条件表达式的抽象比较简单,只有3个元素,如果特征有逻辑+-*/甚至自定义函数的计算呢?如下表达式:

feature * 1.5  +3    >  5
feature1 * feature2  <  8

通过govaluate 可以实现,但换个思路,把特征的加工放到特征引擎中,那么获取的特征数据就是计算好的结果,feature_new=(feature * 1.5 +3),计算表达式feature_new > 5合理拆分系统功能边界,最大程度简化表达式,保持单一原则。

4.3 如果获取特征失败或出现空值怎么办

要根据业务场景具体分析,有的业务对数据结果有强依赖要求,获取特征失败(重试后仍失败),就进入熔断状态,等待恢复后重新执行。有的则会将表达式直接设为true(命中)或false(忽略)。

获取特征出现空值可能原因:请求特征引擎超时或出错失败;未获取到数据(本来就不存在);获取到零值数据(有些特征用0表达某种case),需要根据业务场景做出相应的处理。

4.4 自定义字段扩充dsl

上述DSL中,rule只定义id,condition,logic,rule_name,decision几个元素,根据不同业务场景可以自定义元素并进行扩充,如可加入命中产生冻结期,特征获取失败或出现空值校验也可以作为扩充字段。

ruleset可以增加策略选择,命中即退出 还是 执行全部规则,增加规则集分类和标签,方便不同业务场景维护。

还可以增加feature结构,对特征进行约束和字段补充。

四、实现决策引擎

到这里,只是实现了决策引擎的规则部分,接下来会对规则集进行流程编排,类似BPMN规范的流程图,这里叫决策流,流程引擎。下一篇会重点介绍如何实现决策流,以及决策流上除了规则集节点外,还会引入条件节点冠军挑战者节点(abtest),还有决策树、决策矩阵,后续如何执行模型引擎,热部署,监控,实时特征等内容。

原文地址:https://mp.weixin.qq.com/s?__biz=MzIyMzMxNjYwNw==&mid=2247483738&idx=1&sn=111609f176f11de8357c51a820b089b5&chksm=e8215e4adf56d75c2e6e8b81b89c1faabab667f493ce809cb749994cc9cd776342fd17d4172e&cur_album_id=2282168918070968323&scene=190#rd

<think>嗯,用户问的是Drools自定义可视化规则配置,我需要先回忆下Drools的相关知识。首先,Drools是个业务规则管理系统,它允许用户通过编写规则来实现业务逻辑。而自定义可视化配置应该是指让非技术人员也能通过图形界面来配置规则,对吧? 那我得先介绍Drools的基础,可能用户已经知道,但还是要简单提下。然后,用户的需求可能是如何在不写代码的情况下配置规则,所以需要讲清楚可视化配置的必要性,比如降低技术门槛,提升效率这些。 接下来,我应该分步骤说明如何实现自定义可视化配置。可能需要提到Drools的决策表,但决策表可能不够灵活,所以需要更高级的可视化工具。然后,需要提到前端技术,比如拖拽界面,表单生成,这部分可能需要结合些前端框架,比如Vue或React,或者现成的工具如bpmn.js之类的。 然后,规则设计器的实现,可能需要后端将规则数据存储为结构化的JSON或者XML,前端再解析生成界面。这部分需要详细说明,比如如何定义规则模型,如何设计UI组件,如何与Drools引擎集成,验证和发布流程等。 另外,可能需要注意的技术点,比如规则语法检查,避免用户输入错误,还有性能优化,比如规则编译和加载的效率。同时,扩展性也很重要,可能需要设计插件机制来支持更多的规则类型。 用户可能需要实际应用的例子,比如金融风控、电商促销,这些场景下可视化配置规则的好处。最后,总结下优势,比如降低门槛,提升协作,快速响应业务变化。 需要确保回答结构清晰,分点说明,每个步骤都有具体的实现方法。还要注意使用中文,避免专业术语过多,保持易懂。同时,检查是否符合之前的系统指令,比如数学公式的正确格式,但这里可能用不上数学公式,不过要确保LaTeX正确,如果有的话。总之,整个思考过程需要覆盖用户可能关心的各个方面,提供实用且可行的解决方案。</think>Drools自定义可视化规则配置实现路径分析: 、Drools规则基础架构 1. 核心引擎工作原理: - 规则存储于.drl文件或决策表中 - 通过KIE(Knowledge Is Everything)模块管理知识库 - 运行时使用Rete算法进行模式匹配 2. 原生规则语法示例: ```drl rule "年龄限制规则" when $p : Person(age < 18) then $p.setEligible(false); end ``` 二、可视化配置的必要条件 1. 业务需求解耦: - 分离业务逻辑与技术实现 - 建立领域特定语言(DSL) 2. 技术实现要素: ```mermaid graph TD A[可视化界面] --> B(规则模型抽象) B --> C{存储格式} C -->|JSON/XML| D[Drools转换器] D --> E[DRL文件] E --> F[规则引擎] ``` 三、实现自定义可视化配置的步骤 1. 规则模型设计 - 实体属性元数据定义 ```json { "entity": "Person", "fields": [ { "name": "age", "type": "int" }, { "name": "income", "type": "double" } ] } ``` 2. 可视化编辑器开发 - 前端技术选型: * 拖拽式界面:React-DnD/Vue.Draggable * 逻辑表达式构建:Blockly/Ace Editor - 功能模块: ```mermaid flowchart LR 条件配置 --> 动作配置 --> 规则校验 --> 版本管理 ``` 3. 规则转换层实现 - 转换器设计模式: ```java public class RuleConverter { public String convertToDRL(VisualRule visualRule) { // 解析可视化规则结构 // 生成DRL语法 } } ``` 四、关键技术挑战解决方案 1. 复杂逻辑表达处理 - 嵌套条件支持:采用树形结构存储条件表达式 - 运算符扩展:预定义运算符映射表 ``` AND → && OR → || NOT → ! ``` 2. 规则校验机制 - 静态检查: * 类型匹配验证 * 死规则检测 - 动态测试: ```java KieSession.fireAllRules(); assertTestResults(); ``` 五、典型应用场景 1. 金融风控系统: - 可视化配置信用评分规则 - 实时规则热更新 2. 电商促销系统: - 图形化配置满减规则 - 多条件组合优惠策略 六、性能优化建议 1. 规则编译加速: - 增量式规则编译 - 预编译缓存机制 2. 执行效率提升: - 规则优先级优化 - Agenda分组管理 优势总结: 1. 业务响应周期缩短40%-60% 2. 规则维护成本降低70% 3. 多角色协同效率提升3倍 4. 系统迭代速度提高5倍以上 实际落地建议: 1. 采用渐进式实施方案 2. 建立规则版本控制体系 3. 配套可视化调试工具开发 4. 制定规则生命周期管理规范 当前技术演进方向: 1. 与机器学习结合实现智能规则推荐 2. 自然语言规则配置(NLP) 3. 云原生规则服务化部署 注:具体实现需根据实际业务场景调整架构设计,建议初期采用开源框架(如Drools Workbench)进行二次开发,逐步过渡到完全自定义的可视化配置系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值