可视化规则引擎决策池:规则复用、顺序执行与工程落地

发布时间:2026/10/29 4:14:24
可视化规则引擎决策池:规则复用、顺序执行与工程落地 简介面向规则引擎学习者与业务系统开发者的实操型文档聚焦“决策池”这一规则复用机制与按条件分支的决策树不同决策池会按引用顺序依次执行池内全部复用规则适合集中管理高频规则并保证执行次序的业务场景。内容以 Visual Rules Solution 旗正规则引擎为操作环境围绕规则包创建、变量定义、规则编写展开并延伸至规则包引入决策池、全局变量共享与规则实现最后借助批量测试、测试用例和测试结果核对各规则的执行状态与输出。整份资料为 1 个 docx 文件压缩包约 1.19MB图文步骤与界面截图配合便于对照软件逐项操作已有 458 人学习对初学者可建立规则复用思路对开发者可作为配置与验证的参考手册。1. 规则引擎决策池一次配置、多处复用的规则池到底解决什么问题上个季度接手一个理赔计算的老系统最烦的不是逻辑复杂是同一段「按证件号截取地区码」的规则在七个规则包里各写了一遍改一次口径要动七处漏一处就是线上事故。后来改成可视化配置器里的决策池把这段逻辑抽成独立规则包其他包只做引用口径只改一处剩下六处跟着生效。这篇拆的就是这个机制——一款可视化规则引擎配置器中的决策池它跟决策树最大的区别在于决策池里所有被引用的规则都会按引用顺序依次全部执行不做条件挑拣。适合正在做业务规则配置化、手里有一堆重复规则要收敛的后端和配置人员看。2. 规则包骨架工程、规则包、变量与 rpk/rsc 三层对象模型2.1 从工程到规则包对象树长什么样配置器里的组织方式和代码工程几乎一一对应工程Project对应一个业务域规则包Rule Package对应模块规则对应方法决策池对应一个「只能顺序执行的方法调用链」批量测试对应单元测试。原文的示例是在「功能解析」这个工程下建两个包基本工程2负责放被复用的原子规则决策池负责引用它们。落到磁盘上大致是这么一棵树功能解析/ # 工程一个业务域的顶层容器 ├── 基本工程2/ # 复用规则包原子规则都放这里 │ ├── 变量/ # 传入数据 a、b、c类型均为 string │ ├── 外部调用/ # 从左截取字符、取字符串长度 │ ├── 规则/b取值 │ ├── 规则/c取值 │ ├── 基本工程2.rpk # 设计期源文件配置器可再打开 │ └── 基本工程2.rsc # 编译产物运行引擎实际加载的东西 └── 决策池/ ├── 变量/ # 同样要定义 a、b、c ├── 决策池/决策池1 # 复合节点内部是一个有序引用列表 ├── 批量测试/批量测试1 ├── 决策池.rpk └── 决策池.rsc看清楚这棵树后面所有问题都好定位变量和外部调用是包级作用域规则是包内成员决策池节点是包内的一个复合成员批量测试挂在包上而不是挂在单条规则上。初学者最容易把「工程」当作用户隔离边界其实它只是分组真正参与编译和运行的最小单位是规则包。2.2 变量定义三个 string 变量为什么都挂在「传入数据」下基本工程2的对象库里要建三个 string 变量 a、b、c原文明确说都是在「传入数据」里添加的。这跟引擎的变量分域有关多数可视化规则引擎会把变量分成两类——「传入数据」跟随会话进出调用方塞进来什么、执行完就能取到什么「局部变量」只在一次执行内可见执行完就丢。变量名类型所属域方向用途astring传入数据入参外部送进来的原始串比如BJ1101bstring传入数据出参由规则b取值写入的地区码cstring传入数据出参由规则c取值写入的二次加工结果三个都放「传入数据」是为了让批量测试和外部 API 调用用同一套路径测试时在用例里直接给 a 填值、给 b 和 c 填期望值线上调用时用一个 Map 塞 a执行完从结果 Map 里按名字取 b 和 c。我一般会要求团队把「对外暴露的输出变量」全部放传入数据域中间量一律用下划线前缀_tmpLen这类关在局部域里避免内部值从结果里漏出去。类型统一给 string 是这类入门配置的常见做法代价是数值比较要显式转换。比如你后面想判断「长度大于 4」不能在表达式里直接拿字符串比大小得先转数值否则10 9会按字典序判成真。2.3 外部调用把「从左截取字符」挂进规则包对象库变量建好之后规则表达式里想用字符串函数不能直接写——引擎的内置公式库要先注册到规则包的对象库里相当于 Java 里给类加 import。原文的做法是打开规则包对象库的「外部调用」从「字符截取」分类里挑「从左截取字符」再补一个方法确认后对象库的「外部调用」节点下就多了两个方法规则里才能调它们。这一步的实际含义等价于给规则包挂了一个静态工具类// 规则表达式里写的是 b left(a, 2)引擎编译后等价于调用内置函数库 public final class StringFuncs { // 从左截取从下标 0 开始取 len 个字符 // len 0 返回空串len 超过原串长度时返回原串不补位、不报错 public static String left(String src, int len) { if (src null || len 0) return ; return src.length() len ? src : src.substring(0, len); } // 第二个方法一般配「取字符串长度」或「从右截取」看你要拼什么 public static int length(String src) { return src null ? 0 : src.length(); } }两个参数要留意src传的是规则变量 alen是字面量 2改成 3 就取三位。截取单位是字符不是字节北京1101取两位得北京取一位得北不会出现把一个汉字劈成半个的乱码。如果业务口径是按字节截这两个方法就不够用得自己在函数库里加一个按字节处理的版本别硬套。外部调用是包级作用域这件事是这个机制里最容易翻车的地方第 5 章会专门展开。2.4 保存与编译rpk 和 rsc 分别是什么规则写完点「全部保存」消息栏会提示保存成功并生成 rsc。这两个文件的角色必须分清否则调试能耗掉一下午rpk规则包源文件通常是 XML 或 JSON 结构里面装着变量定义、规则节点、外部调用注册、决策池的引用列表、批量测试用例。它是设计期产物配置器双击就能重新打开编辑。rsc编译产物把 rpk 里的可视化配置翻译成引擎运行时能直接加载的结构。运行侧加载的是它不是 rpk。一次「保存全部」实际走了两步先落盘 rpk再编译出 rsc。真正决定线上行为的是最后这一步有没有发生。我在项目里见过太多次「改了配置、点了保存、测试环境没变」的现场追到最后全是 rsc 没重新生成引擎加载的还是几小时前那份。3. 复用规则编写b 取值与 c 取值的赋值链路怎么落地3.1 b 取值一条赋值语句的四个要素在基本工程2上右键新增规则命名b取值进规则编辑区给变量 b 赋值。可视化配置里看着是拖控件、点选变量落到执行层面就是一条赋值语句四个要素必须齐目标变量、表达式、类型、空值策略。// 规则 b取值 的等价执行逻辑 // 目标变量bstring传入数据域 // 表达式left(a, 2) String b StringFuncs.left(a, 2); // 代入实际数据 // a BJ1101 - b BJ // a SH02 - b SH // a - b 空串原样返回不抛异常 // a null - b 看函数库实现有的版本返回 null参数怎么改2是硬编码的截取长度如果业务上不同渠道长度不同别在这里写死改成读另一个变量比如len同样放传入数据域由调用方传表达式变成left(a, len)。类型这块b 声明成 string 就不会有隐式转换问题如果误声明成数值型BJ这种结果会直接赋值失败或者变成 0测试用例立刻能暴露。空值策略值得单独说一句。a 传空串时返回空串还是返回 null取决于函数库实现很多版本两者行为不一致。我的习惯是在b取值里明确加一条前置判断a 为空就直接给 b 赋空串并跳过截取这样下游c取值拿到的永远是确定的字符串不用到处判 null。3.2 c 取值依赖 b 的二次加工c取值是同一规则包里再加的一条规则逻辑建立在 b 之上。原文只说了「在规则中添加规则逻辑」具体表达式没给按这个示例的数据流最合理的写法是拿 b 再拼上原串长度做校验位// 规则 c取值 的等价执行逻辑 // 依赖 b 已经算完 String c b _ StringFuncs.length(a); // 代入实际数据 // a BJ1101 - b BJ - c BJ_6 // a SH02 - b SH - c SH_4这里用到了第二个外部方法length也正好解释了为什么对象库里要挂两个方法而不是一个——一条规则里可以调多个内置函数缺一个就编译不过或者运行时报「方法未定义」。关键点在依赖关系c 的表达式里出现了 b。如果 b 还没赋值c 算出来的就是_6b 是空串或者null_6b 是 null。这个错误在单包测试时不一定暴露因为你可能先手动跑了一遍 b但一进决策池按顺序执行问题立刻现形。3.3 依赖顺序规则名不等于执行顺序很多人下意识觉得规则包内的规则会按名字排序执行b取值在c取值前面是「碰巧对」。实际上多数配置器是按规则在包内的排列顺序也就是你创建的先后、或者规则列表里的上下位置来触发字母序靠不住。血泪经验直接在c取值已经存在的情况下回头新建b取值新规则很可能排到列表末尾单包跑一遍就翻车。稳妥做法有两条任选其一建规则的顺序 依赖顺序先建b取值再建c取值建完在规则列表里确认一眼上下位置。不依赖包内隐式顺序把 b 的计算直接内联到c取值的第一行c left(a,2) _ length(a)牺牲一点复用性换确定性。规则超过五条、依赖超过两层的时候我更倾向这条。3.4 编译产物核对rsc 时间戳是最便宜的验证手段规则写完、保存全部之后消息栏会提示 rpk 保存、rsc 生成。想确认编译真的发生不用打开配置器直接看文件时间# 只盯这两个文件的修改时间编译有没有真的发生一眼就看出来 ls -l --time-stylefull-iso 基本工程2.rpk 基本工程2.rsc # 期望结果rsc 的时间 rpk 的时间 # 如果 rsc 时间明显更早说明只保存了没编译线上跑的还是旧逻辑这个习惯救过我很多次。规则包多了以后你根本记不住上一次点的是「保存」还是「保存全部」而这两者的差别在生产环境就是「新逻辑生效」和「一切照旧」。顺手把 rpk 和 rsc 一起提交到版本库出问题能直接 diff 两版 rpk看清楚是谁改了哪条表达式。4. 决策池搭建拖入顺序就是执行顺序4.1 新建决策池规则包并补齐同名变量基本工程2编译通过之后在同一个工程下新建决策池规则包然后再定义一遍a、b、c 三个 string 变量同样放传入数据域。这一步新手最容易省觉得「引用别人包的规则变量应该自动继承」结果一执行就发现拿不到值。原因在于决策池包是一个独立的执行单元引擎为它开自己的会话会话里只有它自己声明的变量。它调基本工程2的规则时是把同名变量按名字传进去、执行完再按名字取回来。所以决策池包里的 a、b、c 不是冗余是它对外的输入输出契约。两个包的变量名、类型必须严格一致差一个大小写都映射不上。4.2 从「所有规则」拖入引用列表就是一个有序数组决策池的添加方式和普通规则不一样不是「新建规则」而是在包里添加一个「决策池」节点然后在它的属性栏「所有规则」里找到基本工程2展开把b取值和c取值依次拖进决策池。拖完节点里就多了两条引用顺序就是拖进去的先后。配置器里看到的是一个树形节点落到底层存的就是一个有序数组{ nodeType: decisionPool, name: 决策池1, mode: sequentialAll, refs: [ { pkg: 基本工程2, rule: b取值, order: 1 }, { pkg: 基本工程2, rule: c取值, order: 2 } ] }mode: sequentialAll是决策池的语义核心——不做条件挑拣全部执行order就是拖入顺序也是执行顺序。这个结构带来两个直接结论想调整执行顺序不用改任何规则逻辑在决策池节点里上下拖动引用即可。这是决策池比决策树灵活的地方。同一条规则可以被拖进来两次它就会执行两次。有时候这是特性比如两轮校验更多时候是误操作看到输出重复两遍的时候先回来数一下 refs 长度。4.3 决策池 vs 决策树 vs 决策表三种复用单元的分工同一个引擎里三种复用单元经常被混用选错了后面全是补丁。按原文的定义决策树能设复用规则的执行条件和规则之间的条件决策池则是全执行、只保证顺序。维度决策池决策树决策表复用什么整条规则规则 执行条件行级的条件组合执行方式全部执行按引用顺序按条件选分支命中行才执行能否设执行条件不做条件挑拣可以规则间条件也能设行本身即条件顺序敏感度极高顺序错就错中取决于分支低各行独立典型场景公共赋值链、前置校验、口径统一多分支决策、审批流走向保费/折扣这类多维组合选型的一句话判断结果只跟输入有关、且每条都要跑用决策池结果取决于走哪个分支用决策树结果是多个维度的组合命中用决策表。原文这个示例属于第一种截取 拼接两条都得跑天然适合决策池。4.4 变量映射同名传参是默认不同名要显式配决策池引用外部规则时变量怎么传常见做法是按名字自动映射决策池会话里的 a 传给被引用规则包的 a被引用包算出的 b 再回写到决策池会话的 b。同名同类型零配置就能跑通这也是原文示例能一路走通的原因。一旦名字不完全一致就得在决策池节点的属性里显式配输入输出映射。踩过的坑是基本工程2里把 b 改名成regionCode决策池那边没跟着改映射跑出来 b 全是空——不报错只是结果不对这种「静默失败」最难查。所以改名这种操作我一般要求同一次提交里把引用方一起改完改完立刻跑一遍批量测试。5. 批量测试与常见问题排查用例、期望值和五类翻车现场5.1 批量测试挂载与用例录入规则写完不代表对原文的做法是给规则包添加「批量测试」在批量测试下添加若干测试用例每条用例填输入和期望结果保存并编译后执行系统逐条比对期望值与实际输出全一致才算通过。操作路径是选中规则包 → 添加批量测试 → 在该节点下添加测试用例 → 填 a 的输入值、b 和 c 的期望值。三条用例足够覆盖这个示例用例输入 a期望 b期望 c覆盖点case_1BJ1101BJBJ_6常规路径case_2SH02SHSH_4短串长度不足 4case_3空串空串_0或空串边界取决于空值策略case_3 这种边界用例价值最高。它逼你在动手前就想清楚「a 为空时 b 该是什么」而不是等线上报错再回头补判断。5.2 期望值比对测试通过的唯一标准是什么用例录完点「全部保存」让 rpk 落盘并生成 rsc然后执行批量测试。结果页会列出每条用例的期望值和实际输出值一致标通过不一致标失败。比对逻辑要注意两点。第一string 是严格相等比对期望值里手滑多敲一个空格BJ和BJ 就不相等测试红一片但规则其实是对的。第二比对发生在编译之后你改了规则没编译跑的还是旧 rsc可能出现「测试全绿但线上错」的幻觉。这两点合起来给一个操作纪律改规则 → 保存全部 → 核对 rsc 时间 → 跑批量测试四步固定顺序不要跳。5.3 排查一只保存没编译改了等于没改现象改了b取值的截取长度从 2 改到 3批量测试跑出来还是旧结果或者消息栏只有「保存成功」没有「编译成功」。原因rpk 更新了rsc 还是上一版引擎加载的一直是 rsc。配置器里「保存」和「保存全部」在部分版本上行为不同前者只落 rpk。解决统一走「保存全部」并在执行测试或发布前核对 rsc 时间戳rsc早于rpk就说明没编译。团队里把这个检查写进发布清单比靠记忆靠谱。5.4 排查二c 被拖到 b 前面输出变成_6现象单包测试通过一进决策池c 的结果变成_6或者null_6b 单独看是对的。原因决策池按引用顺序执行c 在前面时 b 还没赋值b _ length(a)里的 b 是空串或 null。解决在决策池节点里把b取值拖到c取值上面或者在c取值里做防御先判 b 是否为空为空就自己算一遍left(a,2)。前者治本后者治标赶工期时先治标也行但要在规则注释里写清楚。5.5 排查三外部调用没在两个包里都注册现象基本工程2单独测全绿把规则拖进决策池执行报「方法未定义」或者「找不到函数 left」。原因外部调用是包级作用域基本工程2的对象库里注册了left决策池的对象库里没有。规则在决策池会话里执行时查的是决策池自己的函数表。解决两个包的对象库都注册同名方法包多了以后更好的做法是建一个「基础函数包」把公共函数集中注册其余包统一引用它改一处全局生效。5.6 排查四用例只填输入不填期望现象批量测试一片通过看着很舒服上线第二天就出问题。原因期望值留空时比对逻辑要么跳过该字段要么把空值当成「不比」测试等于只验证了「没抛异常」。解决每条用例的每个输出变量都必须填期望值哪怕填「空串」也要显式写出来。加一条硬规矩用例数少于三条、或没有边界用例的规则包不允许发布。5.7 排查五同名变量撞车中间值漏到输出里现象决策池返回的结果 Map 里多出了几个你没打算暴露的字段或者数字被当成字符串拼进去6变成了6。原因决策池包自己也定义了 a、b、c和复用包同名变量在映射时按名字对齐只要你在决策池里加了个同名中间变量它就会跟着进结果集。另外所有变量都是 string 类型数值参与拼接会自动转字符串。解决中间量统一加前缀_tmp对外只暴露 a、b、c 三个需要数值运算的地方在表达式里显式转数值再比别指望引擎隐式转换。6. 二次开发把决策池接进自己的服务并做可回滚的灰度配置器里跑通只是第一步真要落地得接到 Java 服务里。我的做法是包一层很薄的 Runner把引擎句柄藏起来对外只暴露「传 a、拿 b 和 c」这一个方法public class DecisionPoolRunner { // 引擎加载 rsc 后拿到的知识库句柄随应用启动初始化一次别每次 new private final KnowledgeBase kb; public DecisionPoolRunner(KnowledgeBase kb) { this.kb kb; } public PoolResult run(String inputA) { MapString, Object in new HashMap(); in.put(a, inputA); // 只传入口变量其余留空由规则算 // 执行指定规则包返回值是该包传入数据域变量的快照 MapString, Object out kb.execute(决策池, in); PoolResult r new PoolResult(); r.setB((String) out.get(b)); // 按变量名取注意空值强转 r.setC((String) out.get(c)); return r; } }类名和execute的签名按你手上版本的实际包名替换别照抄。三个关键点知识库句柄单例复用规则包名要和配置器里完全一致取结果时对 null 做兜底。改造清单我都按这张表过一遍改造点做法验证手段版本管理rpk 进 Gitrsc 要么一起提交要么由构建流水线编出来每次发布前 diff rpk看引用列表有没有变执行埋点在引擎监听器里按 refs 顺序打每条被引用规则的耗时和输出单次调用日志能看到b取值 12ms / c取值 3ms灰度比对老代码和新 rsc 并行跑输出不一致只告警不阻断采样一周diff 率归零再切流量用例沉淀把线上采样输入导成批量测试用例随包一起提交每次改完决策池先跑一遍批量测试灰度这块值得多花两句。规则配置化和写代码最大的差别是代码 diff 看得见配置改动在 rpk 里也看得见但业务方永远说不清「这个口径改了会影响哪些下游」。所以并行跑 输出 diff 是最省事的后悔药比对不一致的样本直接变成新的测试用例一轮下来覆盖度自己就上去了。从那以后我每次动决策池里的引用顺序都强制先把批量测试跑一遍再去看一眼 rsc 和 rpk 的时间戳两个都对上才敢提发布单。这套动作看着笨但确实让我少加了很多班。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询