checkpoint: 基建与确定性文档更新——运营手册 3.1.0 + 引擎实施记录 18.23

运营手册升 3.1.0:定时任务 jobstore 落 PG 说明、SQLite 彻底清理、require_redis 显式
503 守卫行、备份口径同步。实施记录追加 18.23:引擎确定性修复(系谱按 id 规范排序,
消除 DB 行序对 MME 布局影响,根因链 + 验证)。
This commit is contained in:
34047007@qq.com
2026-08-07 22:44:03 +08:00
parent 7e449b5c9d
commit 93af27d1e1
2 changed files with 25 additions and 6 deletions
@@ -2095,3 +2095,18 @@ PYTHONIOENCODING=utf-8 ENVIRONMENT=dev PYTHONPATH=backend python Temp/claude/e2e
**③ 授粉窗口规划(window-plan)**:新端点 `GET /bre/pollen/window-plan?window_start=&window_end=`controller + service.window_plan,纯计算不落库)。`_project_bloom(g, ref_year)` 把花期投影到窗口起始年:结构化区间按年际月日(桃花期 3-4 月单年窗口假设),缺区间 fallback `_BLOOM_TIER_SPAN`(5 档 → 代表区间,粗粒度标注)。花期种质给花粉覆盖三态 `covered`(整段有批次 `collect<=bs and expiry>=be`/`partial`/`none`(无覆盖 → n_need_pollen + 提示需采粉贮藏或同期授粉);库存侧给批次窗口状态 `full`(全程覆盖)/`expiring`(窗口内到期)/`entering`(窗口内采集、有效期出窗)/`expired`(窗口前失效)/`not_started`(窗口后采集)。窗口反向 409。前端 pollen 页顶部「授粉窗口规划」卡片:daterange 默认本年度/次年花期(3-4 月在当年、否则次年 03-01~04-30+ 生成按钮 + 缺粉 alert + 花期覆盖表 + 库存状态表;pollen.ts 加 `getWindowPlan` + `PollenWindowPlan`/`PollenBloomItem`/`PollenInventoryItem`/`PollenLotRef` 类型。
**验证**`e2e_o1_bloom_20260805.py` 探针全过——① CreateSchema→CRUD 双 Date 列落库;② 6 种质 15 对(重叠/错开/需采粉贮藏三类 + 档位 fallback mid×very_early→store + 区间×档位混合对→unknown,flags 仅非重叠进警示);③ 窗口 A(03-05~04-08)花期覆盖三态 covered/partial/none + 档位投影种质、窗口 B03-10~03-30)库存五态 full/expiring/entering/expired/not_started + 窗口反向 409。`vue-tsc` EXIT=0。全量回归 + `run_ci.py` EXIT=0 零回归。版本连锁:4 e2e 探针(statsflow/spatial_sparse×2/prediction_rigor/mendelian_persist+ o234 tc 断言 v2.29→v2.30。备份镜像 `Temp/claude/backup_o1_bloom_20260805/`
# 18.23 统计引擎确定性修复:系谱按 id 规范排序,消除 DB 行序对 MME 布局的影响(2026-08-07,无版本变更)
> 背景:全量回归**间歇失败**(`run_regression` 串跑 70/72`e2e_prediction_rigor` 偶发 acc 0.406 vs 期望 0.405、`e2e_spatial_sparse` 偶发 EBV diff 0.78)——单跑稳定、只在全量串跑出现,属典型的非确定性可重现难题。系统排查逐层排除 DB 残留/序列/并发后定位到引擎本身。
**根因链(三层)**
1. **引擎输入顺序敏感**`blup._order_pedigree` 保持调用方输入顺序 → MME 变量布局(个体序号)随输入序变化。平似然/退化数据(真实观测稀少、似然面平坦)上,golden-section/二分搜索会被**末位浮点噪声**带偏到任意 h²——同内容不同序实测 **h²=0.616 vs 0.237**(实测 20x 同输入逐位一致、乱序即变)。
2. **服务层行序不固定**`statistics/service.py` 从 DB 构建系谱**无 ORDER BY**`group_by(obs.tree_id)` 聚合后行序由执行计划决定),行序在运行间漂移 → 引擎输入序漂移 → 结果不重现。
3. **双舍入边界**:服务层 accuracy 存储 `numeric(5,3)`3 位舍入),探针由 `numeric(6,4)` 可靠性(6 位)再舍 3 位——真值恰落在 3 位舍入边界时可差 **0.001**(如 0.408 vs 0.409),1e-4 容差比自身的双舍入预算还紧。
**修复(`backend/scripts/breeding_stats/blup.py`**
- `_order_pedigree` 改为对 **base 与同世代并列个体均按 id 确定性排序**`sorted(base, key=str)` / `sorted(non_base, key=lambda it: str(it[0]))`),输入顺序不参与计算——同内容输入必逐位同输出。单 chokepoint 收口全部 6 个调用点(solve / solve_spatial / solve_multi / rrblup / bayesb / relationship_matrix),服务层 DB 行序不再影响任何引擎入口。
- `e2e_prediction_rigor` 断言容差 1e-4 → **2e-3**(匹配双舍入预算,注释写明依据)。
**验证**:同输入 20x 逐位一致;打乱输入顺序输出仍逐位一致;prediction_rigor 6 次串跑稳定 acc=0.408;全量回归 `run_regression` **72/72 稳定通过**(含正式 tc 套件)。**附带收益**:同内容输入跨运行可复现,与 MLOps 溯源(input_hash 语义)一致,任何调用方从 DB 取行序不再影响结果。