Drools中前向链接与后向链接实现指南

Drools是一个用Java编写的功能强大的开源业务规则管理系统(BRMS)。它允许开发人员通过以声明性格式编写规则,将业务逻辑与应用程序代码分离。最值得注意的是,这包括提供基于正向链接和反向链接推理的规则引擎、DMN决策引擎和其他项目。

在本文中,我们探讨了什么是正向和反向链接,如何以及何时实现它们,以及它们之间的主要区别。

在本教程中,我们将主要关注前向链接和后向链接,强调差异、用例和性能影响。

正向推理
正向链接从已知事实开始,并积极应用规则来生成新的事实。引擎评估所有可用的数据,触发匹配规则,并继续此过程,直到它不能再得出其他结论。

在数据驱动的方法中,将事实插入Drools的工作内存会立即触发条件与这些事实匹配的规则。这些规则创造了新的事实,反过来又激活了更多的规则,创造了一个级联的推理链。因此,前向链接动态响应不断变化的数据,使其成为监控系统、实时分析或基于流的处理等环境的理想选择:

Forward chaining process

例如,在电信配置引擎中,添加具有特定计划的客户会立即启动一系列配置-更新计费、编程交换机、调整记录以及跨多个系统进行协调。

让我们来看看为前向链接设计的一些规则:


dialect  "mvel"
rule "Suggest Manager Role"
    when
        Applicant(experienceInYears > 10)
        Applicant(currentSalary > 1000000 && currentSalary <= 2500000)
    then
        suggestedRole.setRole("Manager");
end
rule "Suggest Senior Developer Role"
    when
        Applicant(experienceInYears > 5 && experienceInYears <= 10)
        Applicant(currentSalary > 500000 && currentSalary <= 1500000)
    then
        suggestedRole.setRole("Senior developer");
end
rule "Suggest Developer Role"
    when
        Applicant(experienceInYears > 0 && experienceInYears <= 5)
        Applicant(currentSalary > 200000 && currentSalary <= 1000000)
    then
        suggestedRole.setRole("Developer");
end
设置好规则后,让我们看看正向链接的实际操作:

private void forwardChaining() {
    KieSession ksession = new DroolsBeanFactory().getKieSession();
    Applicant applicant = new Applicant("Daniel", 38, 1_600_000.0, 11);
    SuggestedRole suggestedRole = new SuggestedRole();
    ksession.setGlobal("suggestedRole", suggestedRole);
    ksession.insert(applicant);
    int fired = ksession.fireAllRules();
    System.out.println("Rules fired: " + fired);
    System.out.println("Suggested role: " + suggestedRole.getRole());
    ksession.dispose();
}
根据我们设置的规则,控制台应该显示一个规则被触发,建议的角色是Manager。

反向链接
反向链接采用相反的方法。它不是从数据开始,而是从目标或查询开始。然后,引擎会反向工作,以确定哪些事实或规则可以支持该结论。

在Drools中,反向链接是使用查询和逻辑插入实现的。当一个规则或查询请求一个事实时,Drools搜索可以逻辑地推断该事实的规则。如果找到匹配,它会尝试满足触发这些规则所需的条件,递归地朝着初始目标工作:

Backward chaining process

它在疑问系统中特别有效,例如诊断工具或查询驱动的应用程序。例如,我们可以在保险中使用反向链接来识别特定再保险合同下的保单。

让我们看看一些规则和查询来演示反向链接:


dialect  "mvel"
query belongsTo(String x, String y)
  Fact(x, y;)
  or
  (Fact(z, y;) and belongsTo(x, z;))
end
rule "Great Wall of China BELONGS TO Planet Earth"
when
    belongsTo("Great Wall of China", "Planet Earth";)
then
    result.setValue("Decision one taken: Great Wall of China BELONGS TO Planet Earth");
end
rule "print all facts"
when
    belongsTo(element, place;)
then
    result.addFact(element + " IS ELEMENT OF " + place);
end

现在,让我们运行规则:


public Result backwardChaining() {
    Result result = new Result();
    KieSession ksession = new DroolsBeanFactory().getKieSession();
    ksession.setGlobal("result", result);
    ksession.insert(new Fact("Asia", "Planet Earth"));
    ksession.insert(new Fact("China", "Asia"));
    ksession.insert(new Fact("Great Wall of China", "China"));
    ksession.fireAllRules();
    return result;
}
因此,我们应该看到以下流程:

Decision one taken: Great Wall of China BELONGS TO Planet Earth
Great Wall of China IS ELEMENT OF China
Asia IS ELEMENT OF Planet Earth
China IS ELEMENT OF Asia
Great Wall of China IS ELEMENT OF Planet Earth
Great Wall of China IS ELEMENT OF Asia
China IS ELEMENT OF Planet Earth

混合推理
Drools支持混合推理,允许我们在同一规则集中混合使用两种链接方法。我们可以使用前向链接被动地处理传入数据,同时使用后向链接查询特定条件。例如,我们可以使用反向链接来验证条件,然后触发正向链接规则来插入新的事实,从而将两种模式连接起来,以获得更灵活、更高效的解决方案。

让我们来看看一个直观的图表,说明正向链接和反向链接是如何工作的:

Visual diagram illustrating how forward chaining and backward chaining work

前向链接从已知事实(事实A、事实B)开始,触发规则1,推断事实C,然后触发规则2,最后推断事实D。

反向链接从目标(事实D)开始,寻找可能导致它的规则,检查规则2,它需要事实C,并递归地评估必要的事实和规则。

性能考虑
由于前向链接对全局的所有相关事实做出反应,因此如果规则库很大且数据流很频繁,则可能会遇到性能瓶颈。Rete算法有助于优化模式匹配,但在缩放时仍需要考虑。

同时,反向链接是目标特定的,通常执行更少的规则,并且对于目标逻辑查询更精简-但除非提示,否则它不会自动响应新数据。

因此,我们在真实的实时、自动反应的环境中选择前向链接,在精度和最小化规则遍历很重要的情况下选择后向链接。