类型推导规则

July 25, 2026 · View on GitHub

规范性文档。 变量定义的合法性由本文规则判定,校验发生在保存时而非运行时。

为什么需要它

一个变量定义是否可计算,取决于三者的组合:

窗口类型(period.type) × 操作数类型(object_type) × 算子(function.method)

                    合法?→ 输出类型是什么?

这三者构成一个封闭的推导系统。1.x 已经实现了这套推导(variable_function.py 的五张算子表加 value_type.py),是整套体系中设计最完整的部分,2.0 保留其结构。

但 1.x 的推导只用于配置层校验,引擎侧的实现是另一套代码,两者不一致——推导认为合法的组合,引擎可能没实现。2.0 把推导表作为 schema 的一部分,并要求引擎实现与之逐项对应(见 ADR-0005)。


一、窗口类型分组

算子的可用性首先由窗口类型决定。

分组period.type说明
无窗口self对当前事件的值直接变形,不累积状态
简单窗口last_n_secondsever单层滑动或无界累积
周期窗口hourly整点对齐的滚动窗口
复合窗口last_n_hourslast_n_daystoday在周期窗口结果之上做二次聚合

复合窗口是二次聚合:它不直接消费事件,而是合并多个周期窗口的结果。因此它可用的算子集与前三者不同——例如可以 merge 多个小时的 map,但不能对原始事件做 count


二、算子可用性矩阵

✅ 可用 / ➖ 不可用

算子无窗口简单窗口周期窗口复合窗口
count
sum
max / min
avg
variance / stddev
cv
distinct_count
distinct
first / last
lastn
collection
group_count / group_sum
top / topn
merge / merge_value
last_value
global_latest
+ - * /

avgvariancestddev 在周期窗口上不可用,是因为它们需要保存中间统计量(计数、和、平方和)才能正确合并,而周期窗口的存储布局按定长设计。需要小时级均值时,用 sumcount 两个变量,再用 dual 变量相除。


三、操作数类型约束

操作数类型可用算子
long / doublecount sum max min avg variance stddev cv distinct_count distinct first last lastn collection top group_count group_sum global_latest
stringcount distinct_count distinct first last lastn collection group_count global_latest
boolcount distinct_count first last global_latest
mapmerge merge_value last_value global_latest
listcount distinct_count collection

类型不匹配时拒绝保存,并给出明确错误信息(哪个算子、哪个类型、可用的替代)。


四、输出类型推导

算子输入类型输出类型
countdistinct_count任意long
summaxminlonglong
summaxmindoubledouble
avgvariancestddevcvlong / doubledouble
firstlastglobal_latestTT
distinctlastncollectionTlist⟨T⟩
group_count任意map⟨string, long⟩
group_sumlong / doublemap⟨string, 同输入⟩
toptopnmap⟨string, N⟩mmap⟨string, N⟩
mergemap⟨K, V⟩map⟨K, V⟩
merge_valuemap⟨K, 数值⟩map⟨K, 数值⟩
last_valuemap⟨K, V⟩V

二元运算

运算输出
longlong+ - *long
longlong/double
任一为 double全部double

long / long → double 是刻意的:风控中的比率(失败率、转化率)几乎都由两个计数相除得到,返回整数会丢失全部精度。


五、分组键的影响

groupbykeys 的个数不改变输出类型,但改变状态形态与查询方式:

个数状态形态查询时需提供
0全局单值
1一级 key1 个键值
2二级 key2 个键值

上限为 2 个分组键。 更高维度的统计应拆成多个变量——三级以上分组的状态规模会随基数相乘而爆炸,在风控的数据规模下不可行。


六、维度的自动推导

变量的 dimension 字段可以留空,由引擎按以下顺序推导:

  1. typeevent → 空
  2. source → 继承来源的维度;多个来源时维度必须一致,否则拒绝
  3. groupbykeys[0] 映射:c_ip → ipuid → uiddid → didpage / uri_stem → page,其余 → others
  4. groupbykeys 且类型不是 event / filterglobal

推导结果会在保存时写回,不保持为空——这样查询侧不需要重复推导逻辑。


七、校验时机与错误处理

时机校验内容
保存变量时窗口 × 类型 × 算子组合合法;输出类型可推导;引用的来源存在;引用不成环
保存策略时引用的变量存在且已启用;比较运算两侧类型兼容;CEL 表达式编译通过且返回 bool
引擎启动/热更新时再次校验(防止绕过控制面直接改数据库);不合法的变量记录错误并跳过,不影响其他变量

运行时不做类型校验。 所有类型问题必须在前两个阶段拦截——这是与 1.x 最重要的区别,1.x 的大量类型问题以运行时异常的形式暴露,而且只影响单个算子,难以发现。