项目

一般

简介

02-全局可复用模块清单 » 历史记录 » 修订 3

修订 2 (Huarui Lin, 2026-04-24 09:24) → 修订 3/4 (Huarui Lin, 2026-04-24 13:14)

# 全局清单 

 

 ### 一、 全局可复用模块清单 (DRY 原则绝对落地) 
 为了防止几千行代码中出现逻辑分叉导致静默 Bug,以下 4 个模块必须在工程中作为**单一真实来源** 被各阶段强依赖调用。 

 | 模块契约名 | 物理路径映射 | 
 | :--- | :--- | 
 | `core.physical_storage_contract` | `src/core/storage/contract.py` | 
 | `core.stream_engine` | `src/core/stream/engine.py` | 
 | `core.standardization_math` | `src/core/math/zscore.py` | 
 | `core.duckdb_time_aligner` | `src/core/sql/aligner.py` | 
 | `core.telemetry` | `src/core/telemetry.py` | 

 #### 1. `core.physical_storage_contract` (物理存储死锁契约) 
 **被依赖方**:S2, S3, S4, S5.1 参数落盘。 
 **核心职责**:封装所有 Polars 的 `sink_parquet` 动作。 
 *     **强制入参**:`df`, `target_path`, `partition_cols=None` (可选按年拆分)。 
 *     **内部死锁逻辑**:无论传入什么,最终执行前必定被强制注入 `.sort(by=["net_value_date", "fund_id"], maintain_order=False)`,必定注入 `row_group_size=100000`, `compression="zstd"`, `compression_level=7`。任何业务侧企图绕过此模块直接调用 `.write_parquet()` 的代码,必须在 Code Review 中被一票否决。 
 #### 2. `core.stream_engine` (按年流式安全池引擎) 
 **被依赖方**:S2 长缺失切断、S3 特征计算、S5 Spearman 共线性。 
 **核心职责**:封装“年份外层循环 + 内存安全断言 + 释放”的拓扑。 
 *     **核心方法**:`stream_by_year(file_pattern, process_year_func)` 
 *     **内部死锁逻辑**:内部执行年份循环,读取单年数据后,**强制断言内存峰值 < 3GB**(通过 `psutil` 轮询采样),执行业务传入的 `process_year_func`,执行完毕后强制 `del` 释放,进入下一年。彻底杜绝业务侧忘记释放内存导致的 cgroup 击穿。 
 #### 3. `core.standardization_math` (标准化纯数学内核) 
 **被依赖方**:S5 训练前标准化、S6 推理服务。 
 **核心职责**:提供与语言/框架无关的绝对一致的 Z-Score 转换逻辑。 
 *     **核心方法**:`apply_zscore(feature_matrix, param_matrix)` -`standardized_matrix` 
 *     **内部死锁逻辑**: 
     *     接收的输入形状被抽象为 `(N, 38)`(S5 N=3000万,S6 N=1000)。 
     *     内部强制执行 **IEEE 754 除零拦截**(`std == 0 -None`)。 
     *     内部强制执行 **MAD 裁剪**(`clip(lower, upper)`)。 
     *     **复用意义**:S5 训练和 S6 推理绝对共用这一段底层 NumPy 广播逻辑,从数学根源上抹除“训练分布与推理分布不一致”的终极灾难。 
 #### 4. `core.duckdb_time_aligner` (DuckDB 无状态对齐器) 
 **被依赖方**:S2 粗筛阶段。 
 **核心职责**:生成唯一合法的 SQL 片段。 
 *     **核心方法**:`get_week_alignment_sql(date_col)` 
 *     **内部死锁逻辑**:返回硬编码的纯数学整数除法 SQL 实体:`DATE '1970-01-05' + ((CAST(col AS BIGINT) - CAST(DATE '1970-01-05' AS BIGINT)) / 7) * 7`。彻底封杀任何地方手写字符串拼接日期函数的可能。 

 #### 5. `core.telemetry` (结构化性能遥测内核) 
 **被依赖方**:`scripts/run_offline_pipeline.py` 统筹调用的所有业务 Step 入口函数。 --- 
 **核心职责**:提供零侵入的性能打点装饰器。 
 * **核心方法**:`profile_step(step_name: str)` 装饰器工厂。 
 * **内部死锁逻辑**: 
   * 强制捕获函数执行前后的 `time.perf_counter` 绝对耗时。 
   * 强制通过 `psutil.Process().memory_info().rss` 采样首尾内存,计算峰值。 
   * 强制将结果序列化为 JSON 并输出至 `stderr`,严禁污染业务数据 `stdout`。 
   * **绝对封杀**:严禁在此模块内引入任何高频 CPU 采样逻辑(防 GIL 抢锁悖论),严禁调用 `gc.collect()`(防 mimalloc STW)。 

 --- 

 ### 二、 企业级 CI/CD 强制红线清单 (阻断交付) 
 以下检查项必须作为 Git Pre-commit Hook 或 GitHub Actions 流水线的绝对门禁。任何一条不通过,严禁合并入主分支。 
 #### 红线 1:Pandas 零容忍扫描 (AST 静态分析) 
 *     **实施手段**:在 CI 中引入 Python 的 `ast` 模块扫描所有 `.py` 文件的抽象语法树。 
 *     **拦截逻辑**:如果发现 `import pandas` 或 `from pandas` 的节点,直接报错 `FATAL: Pandas dependency detected. Pipeline corrupted.`。 
 *     **防御目标**:防止研发在局部图方便引入 Pandas,破坏 Polars 全局 Lazy 优化图。 
 #### 红线 2:Schema 对称差拦截 (Pydantic 运行时单测) 
 *     **实施手段**:在 `tests/test_schema_symmetry.py` 中,硬编码 38 维特征名字符串集合,以及 S2(5列)、S3(42列)、S4(4列) 的长度要求。 
 *     **拦截逻辑**:在 CI 中拉起一个极小的 Mock 数据流,跑完 S2->S3->S4 的主流程骨架,在产出节点使用 Pydantic 强校验 `df.columns` 与硬编码集合的**对称差集**。差集如果不为空,直接 `pytest.exit("Schema Drift Detected!")`。 
 #### 红线 3:EWM / OLS 金色单测 (精度基线死锁) 
 *     **实施手段**:使用 NumPy 预生成一组包含精确已知输出的基准时序数据(如纯线性递增序列、已知频率的正弦波),保存在 `.npy` 文件中。 
 *     **拦截逻辑**:CI 单测中调用我们手写的 `scipy.signal.lfilter` (EWM) 与纯标量展开式 (OLS),比对输出结果与 `.npy` 基线。允许的浮点误差死锁为 `1e-7`。一旦底层库升级导致精度漂移越过此阈值,直接阻断流水线。 
 #### 红线 4:工程治理正则扫描 (魔法数字猎手) 
 *     **实施手段**:使用 `grep -En "(1\.4826|0\.90|maintain_order=True|gc\.collect|partition_by\(as_dict=True\))" src/`。 
 *     **拦截逻辑**: 
     *     `1.4826` 必须来自 `config.yaml`。 
     *     `maintain_order=True` 违反 Radix Sort 红线。 
     *     `gc.collect()` 违反 mimalloc 红线。 
     *     `partition_by(as_dict=True)` 在非安全池内触发 OOM 红线。 
     *     命中任何一条,CI 直接失败。 
 --- 

 ### 三、 系统最终交付物理清单 
 当你完成所有开发后,产出的工程目录中必须严格包含以下物理文件,缺一不可: 

 **【数据湖层】** 

 *     `s2_{year}.parquet` (按年拆分,Zstd7,Row Group=10万,严格5列) 
 *     `s3_{year}.parquet` (按年拆分,Zstd7,严格42列) 
 *     `s4_{year}.parquet` (按年拆分,Zstd7,严格4列) 

 **【模型与参数层】** 

 *     `standardization_params_wide.parquet` (S5 训练专用,宽表形态,已 shift(1)) 
 *     `standardization_params.parquet` (S6 推理专用,长表形态,已自然日 Ffill,已按 `["feature_name", "date"]` 物理排序) 
 *     `model.txt` (LightGBM LambdaRank 最终重训模型实体) 

 **【微服务层】** 

 *     `s6_inference/` (独立 FastAPI 仓库或目录,内含 `main.py`,启动即加载全局变量,接收 `(N, 38)` 矩阵,吐出 `N` 维得分) 

 **【配置与红线层】** 

 *     `config.yaml` (业务可配:MAD系数、共线性阈值、Spearman模式) 
 *     `core/constants.py` (物理死锁:52周、1970基准日、Parquet压缩级别) 
 *     `.github/workflows/ci.yml` (集成上述四大红线拦截器) 
 *     `configs/config.yaml` (业务超参唯一真实来源,含 D-1 白名单与 MAD 系数)。 

 --- 
 **全链路拆解完毕。**  

 从 S0 脚手架到 S6 矩阵推理,从 DuckDB 纯数学偏移到 50GB cgroup 下的微线程 GIL 防线,这套拓扑没有留下任何“自作聪明”的余地。你可以直接将这 5 个阶段的拆解发给研发团队作为**史诗级需求的最细粒度 Task 拆解**开始排期。