Files
iAOP/core/model-framework
bot_dev1 bec0bad97a feat(#35): FeatureSpec 声明式特征定义引擎(PRD 5.3 特征 spec 语义解释)
新增 core/model-framework/feature_spec.py:把超参包中特征 spec 字段从
「非空字符串存在性校验」(issue #39)升级为可解释的声明式特征定义引擎。

- 手写递归下降解析器(tokenizer+parser),安全无 eval/exec;支持算子调用、
  裸点位、数值/窗口字面量、嵌套、中文点位、带符号/科学计数数值。
- 不可变 AST(TagRef/Number/Window/OpCall),to_dict/repr 可序列化往返。
- 语义校验:未知算子、arity、参数 kind;返回 SpecIssue 列表供配置台聚合展示。
- 依赖分析 resolve_inputs:去重、稳定顺序,供训练/推理流水线按 tag 拉取数据。
- materialize 执行:EMA/SMA/RollingStd/Max/Min/RateOfChange/Diff/Lag/Log/
  Scale/Clip/Combine 共 12 个内置算子,缺数据 fail-fast;numpy 可选依赖。
- 插件注册 register_operator:对齐 PRD「新增结构走插件注册」(呼应 #34 Recipe)。
- 49 个单元测试全通过;零内核改动,纯新增。
2026-08-04 22:31:54 +08:00
..

iAOP-Core · FeatureSpec 声明式特征定义引擎

对应 issue #35(父 EPIC #5「③ AI 模型框架 配置化重构」)与 PRD 5.3「超参包驱动」: 超参包中每个特征的 spec 字段是一段声明式特征定义表达式(FeatureSpec),本 模块负责把这段文本解释为可执行的特征计算:

EMA(CLF-01.TEMP, 5m) → 解析为 AST → 校验 → 依赖分析 → materialize 为时序

core/model-framework/hyperparam.py(issue #39)只对 spec 做「非空字符串」存在性 校验;本模块补齐其语义层,使:

  1. 配置台(issue #62~#67 Template Console)导入超参包时即可一次性展示每个特征 的解析结果与依赖点位,训练前暴露拼写/语义错误;
  2. 训练/推理流水线(issue #40)拿到 AST 后直接 materialize 为「按点位拉取 → 算子 计算」的特征管道;
  3. 切换模板仅改超参包,特征逻辑零代码(对齐 PRD「配置化」核心目标)。

模块结构

core/model-framework/
├── feature_spec.py            FeatureSpec 引擎(解析/校验/依赖/执行/算子注册)
└── tests/
    ├── _bootstrap.py          测试引导(目录含连字符,挂载包名 model_framework)
    └── test_feature_spec.py   解析/校验/依赖/执行/插件注册单元测试

设计要点

  • 零外部强依赖:解析/校验/依赖分析不依赖第三方库;执行(materialize)优先用 numpy 向量化,无 numpy 时退化为纯 Python(边缘/离线环境可加载与校验)。
  • 不可变 AST + 函数式算子:每个算子是纯函数 op(series_map, args),注册到 OPERATORS;新增算子只需 register_operator(对齐 PRD「新增结构走插件注册」, 与 issue #34 Model Recipe 插件接口呼应)。
  • 安全解析:手写递归下降解析器,绝不使用 eval/exec——FeatureSpec 是数据 而非代码,避免任意表达式注入。

语法(对齐 PRD 5.3 示例)

<Operator>(<arg>, <arg>, ...)        # 一元/多元算子
<arg>    := <tag> | <number> | <window> | <Operator>(...)
<tag>    := 点位名,允许中文/点号/连字符,如 CLF-01.TEMP / 炉压
<number> := 整数或浮点(含负号),如 3、-0.5、1e-3
<window> := <正数><单位>,单位 d/h/m/s,如 5m、180d、10s

内置算子:EMA / SMA / RollingStd / RollingMax / RollingMin / RateOfChange / Diff / Lag / Log / Scale / Clip / Combine,覆盖 PRD 5.3 超参包示例的全部特征形态。

使用

from model_framework.feature_spec import (
    parse, validate, resolve_inputs, materialize, describe, register_operator,
)

ast = parse("EMA(CLF-01.TEMP, 5m)")          # 1. 解析
issues = validate(ast)                        # 2. 语义校验(空列表=通过)
tags = resolve_inputs(ast)                    # 3. 依赖点位:['CLF-01.TEMP']
feat = materialize(ast, {"CLF-01.TEMP": xs})  # 4. 执行 → list[float]
print(describe(ast))                          #    人类可读描述

扩展算子(插件注册):

register_operator(
    "Double", min_arity=1, max_arity=1, arg_kinds=(("tag",),),
    func=lambda s, a: [x * 2 for x in s[a[0].name]], doc="翻倍",
)
materialize(parse("Double(A)"), {"A": [1.0, 2.0]})  # → [2.0, 4.0]

测试

cd core/model-framework/tests
python -m unittest test_feature_spec