风控规则 DSL 怎么设计?一次讲清表达式能力、可解释性、灰度控制与工程落地取舍
大家好,我是一名有 4 年工作经验的 Java 后端开发。
上一篇我写了风控规则引擎平台怎么设计,很多人会继续问一个更核心的问题:规则平台里的规则到底怎么表示?
这篇文章我想系统聊一聊,风控规则 DSL 到底应该怎么设计,才能既能表达复杂规则,又不把系统做成不可维护的“可配置 if else”。
🦅个人主页
🐼
文章目录
- 风控规则 DSL 怎么设计?一次讲清表达式能力、可解释性、灰度控制与工程落地取舍
- 一、前言
- 二、DSL 到底在解决什么问题
- 三、风控规则 DSL 最少要支持什么能力
- 3.1 条件比较
- 3.2 逻辑组合
- 3.3 规则组
- 3.4 命中结果
- 3.5 规则元信息
- 四、字符串 DSL 还是结构化 DSL
- 4.1 纯字符串 DSL
- 4.2 结构化 DSL
- 五、推荐的 DSL 模型怎么拆
- 5.1 条件节点
- 5.2 逻辑节点
- 5.3 规则定义
- 5.4 规则运行元数据
- 六、为什么可解释性特别重要
- 七、灰度和版本控制怎么和 DSL 结合
- 八、最容易踩的坑
- 8.1 DSL 太灵活,谁都看不懂
- 8.2 DSL 只支持执行,不支持解释
- 8.3 规则表达和规则治理完全分离
- 8.4 一开始就支持太多花式能力
- 九、面试中怎么回答
- 9.1 回答思路
- 9.2 面试官更想听到什么?
- 十、总结
- 十一、结尾
一、前言
很多团队一说做规则平台,第一反应通常是:
- 弄个表达式
- 后台保存字符串
- 运行时解析执行
听起来好像就够了。
但真做起来很快就会发现问题:
- 规则怎么支持
AND / OR - 怎么支持规则组
- 怎么支持优先级
- 怎么返回命中原因
- 怎么做灰度发布
- 怎么做版本回滚
- 怎么让运营能理解规则
如果这些问题没想清楚,所谓的规则 DSL 很容易变成:
- 一堆难维护字符串
- 谁都不敢改
- 谁也解释不清
所以 DSL 真正要解决的,不只是“能不能写规则”,而是:
规则能不能被表达、被执行、被解释、被治理。
二、DSL 到底在解决什么问题
DSL 本质上就是:
用一种比代码更稳定、比自然语言更精确的方式描述业务规则。
在风控里,它要解决的通常是:
- 表达规则条件
- 组合多个条件
- 表达命中结果
- 让规则平台和规则执行引擎有统一协议
所以 DSL 不是为了炫技,而是为了让:
- 规则配置
- 规则执行
- 规则回放
- 规则解释
都能围绕统一结构工作。
三、风控规则 DSL 最少要支持什么能力
我建议至少支持下面这些能力。
3.1 条件比较
例如:
login_fail_count_10m > 5device_risk_level == HIGHregister_days < 1
3.2 逻辑组合
例如:
ANDORNOT
3.3 规则组
比如:
- 登录风控规则组
- 领券风控规则组
- 下单风控规则组
3.4 命中结果
例如:
- 拦截
- 验证码
- 人工审核
- 降权
3.5 规则元信息
例如:
- 规则 ID
- 规则名称
- 规则版本
- 优先级
- 生效状态
- 生效时间
没有这些,后面规则治理会很难。
四、字符串 DSL 还是结构化 DSL
这是设计里最关键的一个取舍。
4.1 纯字符串 DSL
比如:
(login_fail_count_10m > 5 AND register_days < 1) OR device_risk_level == 'HIGH'优点:
- 看起来灵活
- 类似规则引擎表达式
缺点:
- 解析复杂
- 错误提示难做
- 前端可视化配置难
- 版本治理和回放分析也更难
4.2 结构化 DSL
更推荐用 JSON 结构表达。
例如:
{"operator":"AND","conditions":[{"field":"login_fail_count_10m","op":">","value":5},{"field":"register_days","op":"<","value":1}],"decision":{"riskLevel":"HIGH","action":"BLOCK"}}我更推荐结构化 DSL,原因很明确:
- 更适合前后端联动
- 更适合校验
- 更适合回放
- 更适合命中解释
五、推荐的 DSL 模型怎么拆
我建议你至少拆成 4 层:
5.1 条件节点
最小原子条件,例如:
- 字段
- 运算符
- 值
5.2 逻辑节点
例如:
- AND
- OR
- NOT
5.3 规则定义
例如:
- 规则 ID
- 规则名称
- 条件树
- 风险等级
- 处置动作
5.4 规则运行元数据
例如:
- 优先级
- 版本号
- 生效时间
- 灰度范围
这四层分开后,你的平台会清晰很多。
六、为什么可解释性特别重要
风控平台最怕的不是拦错,而是:
- 拦了却说不清为什么
所以 DSL 设计时一定要考虑:
- 命中的是哪条规则
- 哪个条件命中了
- 命中的值是多少
- 最终为什么给了这个决策
例如返回:
{"ruleId":"RISK_LOGIN_001","hit":true,"matchedConditions":[{"field":"login_fail_count_10m","op":">","actualValue":8,"targetValue":5}],"riskLevel":"HIGH","action":"CAPTCHA"}这对:
- 排障
- 运营复盘
- 用户申诉
都特别重要。
七、灰度和版本控制怎么和 DSL 结合
这个点特别容易被忽略。
一个成熟的规则 DSL,不应该只表达“条件”,还要能带上治理能力。
例如规则里建议带:
versionstatuspriorityeffectiveTimeexpireTimegrayScope
例如:
{"ruleId":"COUPON_RISK_003","version":7,"priority":100,"status":"ACTIVE","grayScope":{"userPercent":10,"whitelistUsers":[1001,1002]}}这样规则平台才能支持:
- 灰度发布
- 版本回滚
- 分批观察效果
八、最容易踩的坑
8.1 DSL 太灵活,谁都看不懂
这会让规则平台变成少数人才能维护的系统。
8.2 DSL 只支持执行,不支持解释
后面误杀分析会很痛苦。
8.3 规则表达和规则治理完全分离
后面就会发现:
- 能执行
- 但没法管
8.4 一开始就支持太多花式能力
例如:
- 函数嵌套
- 动态脚本
- 任意脚本执行
这样平台复杂度会迅速失控。
所以一开始更推荐:
先做结构化 DSL,先支持 80% 常用规则,再逐步扩展。
九、面试中怎么回答
如果面试官问你:
风控规则 DSL 一般怎么设计?
你可以这样回答。
9.1 回答思路
第一,风控规则 DSL 的核心不是把规则变成一段字符串,而是把规则表达、规则执行、规则解释和规则治理统一起来,所以我通常更倾向于用结构化 DSL,而不是纯字符串表达式。
第二,一个比较合理的 DSL 至少应该包含条件节点、逻辑节点、规则定义和规则运行元数据四层。条件节点负责描述字段、操作符和值,逻辑节点负责表达 AND/OR/NOT,规则定义负责描述命中后的风险等级和处置动作,运行元数据则负责版本、优先级、灰度和生效时间等治理信息。
第三,风控 DSL 非常重要的一点是可解释性,所以我会要求规则执行后不仅能返回通过或拦截,还能返回命中了哪条规则、哪些条件命中了、命中的特征值是什么。
第四,我不会一开始就把 DSL 设计得特别自由,比如直接支持复杂脚本执行,因为这样维护和治理成本会非常高。我更倾向于先做结构化、可校验、可灰度、可回放的 DSL,再逐步扩展能力。
9.2 面试官更想听到什么?
面试官真正想听的,通常不是你会不会说 JSON,而是你有没有这些意识:
- 你知道 DSL 不只是表达条件,还要支持治理
- 你知道结构化 DSL 更适合平台化
- 你知道规则执行必须可解释
- 你知道灰度、版本、回滚最好从 DSL 层就考虑
- 你知道 DSL 不能一开始设计得过度复杂
如果你能把这些点讲清楚,面试官会明显觉得你不只是想到“规则配置化”,而是真的理解平台化落地。
十、总结
风控规则 DSL 真正难的,不是“怎么写条件”,而是如何让规则同时具备:
- 可表达
- 可执行
- 可解释
- 可治理
如果只记一句结论,我觉得可以记住这句:
风控 DSL 最稳的设计通常不是最灵活的,而是“结构化、可解释、可灰度、可回放、可版本管理”的那一种。
十一、结尾
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注。
后面我会继续整理一些更偏平台化、系统设计和 Java 后端工程实践的文章,尽量少写空泛概念,多写真实项目里会踩到的坑。