决策引擎应用场景
随着互联网金融的发展以及银行数字化转型的需求,金融风控、智能风控、智能营销等话题频频出现,而 “决策引擎” 作为支撑其业务场景的核心系统,也受到了更多的关注。一般来说,决策引擎可以用于包括金融风控、内容风控、推荐营销、物联网监控等各个领域,凡是涉及到使用规则或模型来做业务决策的场景都可以考虑使用决策引擎来实现。
规则引擎(专家系统)被更多人所熟知,那么决策引擎又是什么?和规则引擎有什么区别和联系呢?我认为,决策引擎正是在规则引擎的基础上发展而来的。事实上,有不少决策引擎系统就是通过规则引擎改造实现的。而差别在于决策引擎在规则引擎的基础上实现了更多元的决策方式,并引入了流程编排过程,使其可以支持更复杂的决策场景。
规则引擎的实现模式是规则清单,执行的主体是规则,简单的规则引擎就是罗列出所有规则并执行,有些复杂的规则引擎会引入规则的优先级和规则编排,但编排的主体仍是规则。
决策引擎的实现模式是决策流编排,执行的主体是决策流,在决策流中编排不同的规则节点、决策节点以及分支节点来实现复杂的决策流程。
简单介绍了决策引擎是什么,现在我们来看一下它是如何实现的?将决策引擎拆解开来,从基础构建元素 “规则” 开始,再将规则组合起来构成更复杂的决策集合。
- 如果:(申请借款总金额 > 5000 或 申请多头借贷数量 < 3) 且 月薪 <= 8000
- 那么:拒绝
(风控规则案例)
要实现这条规则,首先想到可以通过代码 if / else 来实现。
- public string Rule(double sumAmount, int loanNum, double salary)
- {
- if((sumAmount > 5000.0 || loanNum > 3) & salary <= 8000)
- {
- return "refuse";
- }
- else
- {
- return "pass";
- }
- }
(规则模拟代码)
此代码有明显的硬编码问题,规则调整要涉及到开发、测试、上线的流程,调整周期长。且规则逻辑维护到了代码中,难以传承,也容易造成生产中跑的规则和业务预想的规则有差异。当有大量规则或决策场景接入时,工作量巨大难以维护。
(生产中的决策流包含几十个决策节点)
(生产中的规则集包含上百条规则)
为了实现规则调整不受系统制约,我们需要将 规则配置 与 程序代码 进行分离。我们可以通过在程序中嵌套脚本或代码来实现,这样每次调整规则,只需要重新加载即可,而不用修改和发布系统程序。
常见的嵌入式脚本有 Lua、Groovy、Javascript 等,市面上有不少引擎是基于此类脚本语言实现的。另一种方式可以通过自定义语言语法,也叫领域特定语言 Domain Specific Language(DSL),比如大名鼎鼎的开源规则引擎 Drools 就自己实现了一套 DSL语法(命名为 DRL)。DSL 可以更清晰地表达要实现的规则含义。
- rule "rule1"
- when
- ( age < 18) && ( age >= 0)
- then
- actor = "minor"
- end
(Drools 语法 demo)
我们也自定义实现了一套 DSL ,使用的语法结构是 Yaml。简单介绍下 Yaml 是一个轻量级的语法结构,比 xml / json 更小巧,可读性非常好,完全面向人类语言,也有很好的表达能力,支持多种数据结构。
用 Yaml 将该规则描述出来。对规则进行拆解发现,规则由三个条件表达式组成。
条件表达式一:申请借款总金额 > 5000
条件表达式二:申请多头借贷数量 < 3
条件表达式三:月薪 <= 8000
每个条件表达式又分别由 特征、运算符、阈值 构成。
然后将这三个条件表达式的计算结果,再进行逻辑(布尔)运算。
(表达式一 或 表达式二) 且 表达式三
逻辑运算后的结果,如果是 true,即代表命中,触发结果输出“拒绝”。
将上述过程用 Yaml 语法描述如下。
构建出规则 DSL 后,我们对其进行语法解析,从上述分析来看,解析过程主要是分别完成条件表达式的比较运算以及针对表达式结果的逻辑运算,最后根据结果输出。
实现比较运算的算子,要考虑不同类型特征需要支持不同的操作符和不同的实现。如数值型特征,支持大于、小于、等于、不等于、大于等于、小于等于、区间等;字符串特征支持等于、不等于、模糊查找等;数组特征的等于运算要考虑数组元素完全相同。实现时要根据特征的不同,来做不同的比较算子逻辑。
此外还要实现逻辑运算(or、and)和算数运算(+ - * / % 平方、开方等)。
对于复杂的表达式支持,要实现表达式运算。像 Python 类动态语言,支持 eval 执行字符串表达式。而像 Golang、Java 类静态语言,则需要自己实现字符串表达式的执行,其基本思路就是通过构建抽象语法树,将操作符、特征、常量区分出来,通过树的有序遍历来执行。有一些方便的三方包可以辅助实现表达式执行,如 govaluate(Golang)、SpEL(Java)、QLExpress(Java)。