You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在NRules中实现正向推理:基于前置规则结果执行且免额外类

我明白你不想为每个规则都写一个新类的痛点——这种硬编码的方式不仅繁琐,还会让代码库变得臃肿。其实可以用数据驱动的通用规则模型来实现正向推理,不用每个规则都搞一个专属类。下面我给你详细拆解这个方案:

核心思路

正向推理的本质是:维护一个事实库,循环匹配所有规则的触发条件,执行满足条件的规则以生成新事实,直到没有新事实可以添加为止。我们可以把每个规则的「条件判断」和「执行动作」封装成可配置的委托,而不是硬编码成类,这样所有规则都可以复用同一个通用规则结构。

具体实现步骤

1. 定义事实模型

首先需要一个通用的Fact类来存储事实的类型和值,方便引擎识别和匹配:

public class Fact
{
    public string Type { get; set; } // 事实类型,比如"CustomerType"、"OrderTotal"
    public object Value { get; set; } // 事实的值

    // 重写Equals和GetHashCode,避免重复添加相同事实导致死循环
    public override bool Equals(object obj)
    {
        return obj is Fact fact && Type == fact.Type && EqualityComparer<object>.Default.Equals(Value, fact.Value);
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Type, Value);
    }
}

2. 定义通用规则结构

不用继承Rule类,而是创建一个GenericRule类,用委托来承载规则的条件和动作:

public class GenericRule
{
    public string Name { get; set; } // 规则名称,方便调试
    // 条件:判断当前事实集合是否满足规则触发要求
    public Func<IEnumerable<Fact>, bool> Condition { get; set; }
    // 动作:执行规则逻辑,生成新事实并添加到传入的列表中
    public Action<IEnumerable<Fact>, List<Fact>> Action { get; set; }
}

3. 实现正向推理引擎

引擎负责维护事实库和规则列表,核心逻辑是循环触发规则直到没有新事实:

public class ForwardChainingEngine
{
    private List<Fact> _facts = new List<Fact>();
    private List<GenericRule> _rules = new List<GenericRule>();

    // 添加初始事实
    public void AddFact(Fact fact)
    {
        if (!_facts.Contains(fact))
        {
            _facts.Add(fact);
        }
    }

    // 添加规则
    public void AddRule(GenericRule rule)
    {
        _rules.Add(rule);
    }

    // 启动正向推理
    public void Run()
    {
        bool hasNewFacts;
        do
        {
            hasNewFacts = false;
            List<Fact> newFacts = new List<Fact>();

            // 遍历所有规则,检查是否满足条件
            foreach (var rule in _rules)
            {
                if (rule.Condition(_facts))
                {
                    rule.Action(_facts, newFacts);
                }
            }

            // 将新事实加入库中,标记是否有更新
            foreach (var fact in newFacts)
            {
                if (!_facts.Contains(fact))
                {
                    _facts.Add(fact);
                    hasNewFacts = true;
                }
            }

        } while (hasNewFacts); // 直到没有新事实产生为止
    }

    // 辅助方法:根据类型获取事实值
    public T GetFactValue<T>(string factType)
    {
        var fact = _facts.FirstOrDefault(f => f.Type == factType);
        return fact != null ? (T)fact.Value : default;
    }
}
示例:替代类式规则

你提到的PreferredCustomerDiscountRule,现在可以直接用GenericRule实例实现,完全不用新建类:

// 初始化推理引擎
var engine = new ForwardChainingEngine();

// 添加初始事实:优选客户、订单总额1000元
engine.AddFact(new Fact { Type = "CustomerType", Value = "Preferred" });
engine.AddFact(new Fact { Type = "OrderTotal", Value = 1000.0 });

// 定义优选客户折扣规则(不用新建类)
var preferredDiscountRule = new GenericRule
{
    Name = "PreferredCustomerDiscountRule",
    Condition = facts => 
    {
        var customerType = facts.FirstOrDefault(f => f.Type == "CustomerType")?.Value as string;
        var orderTotal = facts.FirstOrDefault(f => f.Type == "OrderTotal")?.Value as double?;
        return customerType == "Preferred" && orderTotal >= 500;
    },
    Action = (facts, newFacts) =>
    {
        var orderTotal = (double)facts.First(f => f.Type == "OrderTotal").Value;
        var discount = orderTotal * 0.1; // 10%折扣
        newFacts.Add(new Fact { Type = "DiscountAmount", Value = discount });
    }
};

// 再添加一个链式规则:折扣>=50元时额外减10元
var extraDiscountRule = new GenericRule
{
    Name = "ExtraDiscountRule",
    Condition = facts => 
    {
        var discount = facts.FirstOrDefault(f => f.Type == "DiscountAmount")?.Value as double?;
        return discount >= 50;
    },
    Action = (facts, newFacts) =>
    {
        var currentDiscount = (double)facts.First(f => f.Type == "DiscountAmount").Value;
        newFacts.Add(new Fact { Type = "DiscountAmount", Value = currentDiscount + 10 });
    }
};

// 将规则加入引擎并运行
engine.AddRule(preferredDiscountRule);
engine.AddRule(extraDiscountRule);
engine.Run();

// 获取最终折扣结果
var finalDiscount = engine.GetFactValue<double>("DiscountAmount");
Console.WriteLine($"最终折扣金额:{finalDiscount}"); // 输出110.0
方案优势与扩展
  • 无需新增类:所有规则都通过GenericRule实例定义,避免类爆炸
  • 动态性强:规则可以动态添加、修改,甚至从配置文件加载(如果把条件/动作转换成可序列化的表达式字符串)
  • 可扩展性:可以给GenericRule添加Priority属性控制规则执行顺序,或者增加规则冲突检测逻辑
  • 职责分离:推理引擎负责核心逻辑,规则只关注自己的条件和动作,代码更清晰

内容的提问来源于stack exchange,提问作者Kishore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:32:25