首页 > 其他分享 >规则引擎是如何诞生的

规则引擎是如何诞生的

时间:2023-09-23 13:33:48浏览次数:19  
标签:__ 诞生 self logic feature 引擎 规则 decision

你是否陷入了整天对着一堆规则判定改来改去,虽然每个信用评分卡和额度模型看起来都很像,但是总是有不同点,一堆密密麻麻的逻辑判定,别说开发了,测试看了挠头。作为一个开发,你想把它规范化,通用化,这就是规则引擎的动机了。

先从拦截规则说起,虽然条目很多,但它简单好写也好测,举个例子如下:

A类拦截规则判定(命中任意一条都拒绝):
1、年销售额低于30万不要
2、近一年存在偷税漏税情况不要

这种简单明了的规则,相信大家分分钟就能写出来,比如这样:

def lanjie_a(year_sales, tax_evasion_count):
	if year_sales < 300000:
		return True
	if tax_evasion_count > 0:
		return True
	return False

完美写出,大家信心满满,豪气道,这种拦截我可以写十个。求仁得仁,组合拦截来了。

B类拦截规则判定(命中两条及以上都拒绝)
1、法院轻度纠纷事件大于2起
2、年收入连续三年下降
3、存在工资拖欠情况

小小变种还是难不倒聪明的开发,一个计数分分钟解决,参考如下:

def lanjie_b(a, b, c):
	counter = 0
	if a > 2:
		counter += 1
	if b:
		counter += 1
	if c:
		counter += 1
	return couner >= 2

正当开发想给自己的机智打call的时候,新规则又来了,那就是A和B一起判定,后面C、D同上。求锤得锤,开发仰天长啸:怪不得网上都说代码就是粘贴复制,原来就是这个意思。熟练的按下Ctrl-C和Ctrl-V,一堆长长的代码就这么诞生了。奋战N个回合,终于在手酸腰痛眼花的情况下搞定,七分无奈一分嘲讽,还有两分隐藏心底的自豪。等下次换个行业再来类似的拦截,一堆CV猛如虎,两分自豪慢慢消失,变成茫然与不甘。

深吸一口气,放松下自己,放弃代码,我们来看看一条规则到底是什么东西?当某个变量满足一定阈值就会给出一个判定,然后这一个判定作为变量又进入了下一轮条件判定,直到规则结束。是不是一下子有了点思路,再抽象下可以变成这样:

特征 (年销售额) 运算符(小于) 阈值(300000) --->  触发结果(拒绝)

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

image

先对条件表达式进行建模,假设只有拒绝与通过两种决策,那么模型可以这样

class Condition:
    __slots__ = [feature", "operator", "value"]

    def __init__(self, feature, operator, value):
        self.feature = feature
        self.operator = operator
        self.value = value

我们可以很轻松的将条件转化为上述的模型,那么如何得到结果呢?我们以常见的数字类型来举例,Python比较简单,参考如下:

class NumberCondition(Object):

    @property
    def ops(self):
        return [">", "<", ">=", "<=", "==", "!=", "in"]

    def __call__(self, context: dict, c: Condition):
       # TODO check
        val = context[c.feature]
        if not isinstance(val, (int, float)):
            raise TypeError(f"Feature {c.feature} not (int, float)")

        expr = f'{val} {c.operator} {c.value}'
        return eval(expr, {}, context)

    def __repr__(self):
        return 'number_condition'

条件表达式解决了,但是我们一般都是一堆条件在一起组合,那又该如何建模呢?

class Rule:
    """规则中所有条件都会执行"""
    __slots__ = ['conditions', 'logic', 'decision', 'depends']

    def __init__(self, conditions, logic, decision, depends):
        self.conditions = conditions
        self.logic = logic
        self.decision = decision
        self.depends = depends

单个条件判定只有是否两种结果,因此不需要logic,而比如拦截规则,可能属于A类规则,只要有一个就行;也可能是B类,最少2条才行,因此不同规则的逻辑是不同的。不同规则拦截可能输出提示也不同,因此我们再额外引入个 decision,用来承载描述,而 depends 则是我们包含的条件需要的特征有哪些,参考引擎如下:

class RuleExpression(object):

    def __init__(self):
		# 不同类型表达式实现不一样,我们整个工厂模型打个包,统一入口
        self.condition_expr = ConditionExpression()
		# 逻辑引擎需要根据需求来完善,比如上述案例,需要实现任意一条,和最少两条的逻辑判定
        self.logic_expr = LogicExpression()

    def __call__(self, context: dict, r: Rule):
        for feature in r.depends:
            if feature not in context:
                raise KeyError(f'Rule {r.name} depends {feature} not in context {context}')

        results = list(self.condition_expr(context, c) for c in r.conditions)
        logic_results = self.logic_expr(results, r.logic)
        retrun r.decision if logic_results and r.decision is not None else logic_results

规则之间很容易组合操作,那么我们参考规则与条件的关系,把规则集也建模出来。这里需要注意,嵌套两层就差不多了,无限嵌套看似灵活,实在大坑,可以通过完善条件表达式,以及合理的设计来实现降维,保证整体效果。

经过一阵不分日夜的努力,引擎底座研发出来了,那么我们如何提供服务呢?是嵌入到代码里,还是提供服务,如果提供服务,那么规则的更新又该如何实现呢?一阵头脑风暴,DSL该登场了。

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

回想我们的引擎真实的操作对象是我们的模型,那么只要能将输入转化成模型,任何格式我们都可以用来定义我们的规则引擎。因此我们可以针对不同的场景设计不同的DSL,比如API更新,我们可以采用JSON,参考如下:

{
    'name': 'my_card',
    'rules': [
        {
            'name': 'A类拦截',
            'conditions': [
                ['number_condition', 'year_sales', '<', 300000],
                ['number_condition', 'tax_evasion_count', '>', 0],
            ],
            'logic': 'any',
            'decision': '该公司命中A类拦截,予以拒绝'
        }
    ]
}

这样整个规则变成了可配置,在设计个产品出来,一个规则引擎产品就这么诞生了。

标签:__,诞生,self,logic,feature,引擎,规则,decision
From: https://www.cnblogs.com/maycap/p/17724042.html

相关文章

  • librime引擎研究
    获取rime版本信息部署相关接口部署(初始化)功能trime菜单右上角部署选择后代码流程如下:部署主要工作是在进行init初始化工作:1、设置共享数据目录和用户数据目录、共享数据目录:用于存放各种配置源文件。用户数据目录:用于存放定制化配置文件。这两个路径从前端app的pref中获取,trime使......
  • 搜索引擎喷一喷
    1.bing搜索 childContextTypes和getChildContext搜出的结果不如google优质,google会直接显示React的文档,第一条就是精准的结果  bing搜索出的都是博客,居然第一页搜到了本人的博客,本人这篇写的还不错2.bing在网络不好的时候,会显示该词条搜索不到结果,不是搜索不到,而是网......
  • 在“百模大战”重生,搜索引擎又行了?
    文丨智能相对论作者丨沈浪“我想让人们知道,是我们(微软)让他们(谷歌)‘跳舞’。”当加入ChatGPT功能的新一代Bing上线,微软CEO纳德拉就已经按耐不住向谷歌发起了挑战。他认为加入新一代人工智能对搜索是一次重新思考的变革,从现在开始,包括核心排名在内的几乎所有东西都可以重新想象。微软......
  • 赋能游戏产业的新引擎
    在近日发布的传媒行业点评报告中,我们注意到版号短期密集发放以及生成式AI技术对游戏产业的深远影响。本报告将对这些重要趋势进行深入剖析,并阐述其主要意义和可能的影响。首先,版号短期密集发放对游戏产业的影响不可忽视。在过去的几年中,版号的限制一直是中国游戏产业的一个关键因素......
  • 搜索引擎排名因素有哪些具体的细节?
    搜索引擎排名因素有很多,以下是一些常见的因素:关键词密度和位置:搜索引擎会考虑关键词在网页上的出现频率和位置。关键词密度指的是关键词在网页内容中出现的频率与整个文本的比例。关键词的位置也很重要,例如,如果关键词出现在页面的顶部或标题标签中,则更有可能被搜索引擎重视。页......
  • ClickHouse(15)ClickHouse合并树MergeTree家族表引擎之GraphiteMergeTree详细解析
    GraphiteMergeTree该引擎用来对Graphite数据(图数据)进行瘦身及汇总。对于想使用ClickHouse来存储Graphite数据的开发者来说可能有用。如果不需要对Graphite数据做汇总,那么可以使用任意的ClickHouse表引擎;但若需要,那就采用GraphiteMergeTree引擎。它能减少存储空间,同时能提高Grap......
  • Mysql数据库存储引擎
    1.存储引擎的概念1.1什么是存储引擎MySQL中的数据用各种不下同的技术存储在文件中,每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力,这些不同的技术以及配套的功能在MySQL中称为存储引擎。存储引擎是MySQL将数据存储在文件系统中的存储方式......
  • mysql存储引擎:myisam
    Myisam:1.myisam是MySQL默认的存储引擎。myisam不支持事务,也不支持外键,优点是访问的速度快,对事物完整性没有要求(select,insert为主的应用都可以使用这个引擎来创建表)2.myisam的表支持3种不同的存储格式,分别是静态表(字段都是固定长度,优点存储迅速,容易缓存。缺点:占用的空间通常比动态......
  • Git忽略提交规则 - .gitignore配置运维总结
    在使用Git的过程中,我们喜欢有的文件比如日志,临时文件,编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。简单来说一个场景:在你使用gitadd.的时候,遇到了把你不想提交的文件也添加到了缓存中去的情况,比如项目的本地配置信息,如果你上传到Git中去......
  • MySQL索引、事务与存储引擎
    MySQL索引、事务与存储引擎索引介绍1、索引的概念索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址)。使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的......