fix(caliber): 流动/速动比率改「倍」口径 + 判定层同量纲直判(ASK-20260924-11 任总放行)
ci/woodpecker/push/main Pipeline failed

- caliber_units: F_CURRENT_RATIO/F_QUICK_RATIO 由 FACTOR_PERCENT(×100) 退场为 FACTOR_IDENTITY;declared_unit=倍 / caliber=倍(无量纲)/ evidence 改引 v2.1 锚;F_GROSS_MARGIN 保持 ×100 不动

- 引用面同步 7 文件:alert_generator / kpi_unit_crosscheck / migrate_...20260916 横幅 / 三个测试套件;self-test 两条改倍口径期望 yellow

口径: CMA-CALIBER-v2.1/kpi-unit@2026-09-24 取代 2a 之两项;其余比率类仍按裁定2a 百分比口径
This commit is contained in:
wecom-fullstack
2026-09-25 10:39:18 +08:00
parent c3ff345d0a
commit a8391a03da
7 changed files with 101 additions and 63 deletions
+36 -27
View File
@@ -1,16 +1,23 @@
# -*- coding: utf-8 -*-
"""比率类 KPI 单位口径归一(取数层) —— 财务口径裁定 裁定2a
"""比率类 KPI 单位口径归一(取数层) —— 口径裁定 20260917(2a) + 20260924(v2.1 取代两项)
依据:`wecom-finance/reports/CMA_口径裁定_20260917.md` 裁定2a
· 比率类指标**统一为「百分比(%)」口径**:取数 ×100,与现有阈值(流动比率红 <150、速动 <80)同量纲;
· 逐个比率类 KPI**核对定义与单位**,出具对照表;
· **单位未核对前的「疑似错配」预警不推送**(降级待核,防假红灯 586/587)。
依据(唯一有效出处):`wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md`
—— 口径标识 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(任总 2026-09-24 16:38 放行 ASK-20260924-11)
· **取代范围仅两项**:`F_CURRENT_RATIO` / `F_QUICK_RATIO` → 统一「倍」(无量纲),**判定层不再 ×100**
(同量纲直判)、`kpi_definitions` 的 unit/阈值/target_value 同步改倍口径;历史 `kpi_values` **不重算**;
· **其余比率类指标仍按 20260917 裁定2a(百分比 %)执行**:毛利率、净利率、资产负债率、渠补率、返利率、
收入增长率等;本表内 `F_GROSS_MARGIN` 那行**保持 ×100 不动**;
· **不得类推扩用**:再发现第三个「比率类单位/量纲矛盾」指标须**另行裁定**,不得以 v2.1 件为据类推改倍数;
· 被取代件 `wecom-finance/reports/CMA_口径裁定_20260917.md`:其 2a 对其余比率类仍有效、2b 仍有效,
**原件保留、一字节不改**。
· 2a 的过渡纪律(单位未核对前的「疑似错配」预警不推送)继续生效。
问题实证(2026-09-17 只读侦查,entity 1 酣客):
`kpi_definitions` 声明单位 = %,但**取数**以比率(倍)存储:流动比率 1.29 / 速动比率 0.54 / 毛利率 0.38,
而阈值按百分数书写(<150 / <80 / <15)→ 跨 2 个数量级比较,判定不可信(09-09 的 586/587 即此形态)。
**注意**:按裁定2a 归一后 流动比率 129% < 150 → **红是合法的**(「流动性偏紧」),586/587 不是假红灯、
不作废;真正的错配处置是「未核对项不推送」。
问题实证(2026-09-17 只读侦查 / 2026-09-24 v2.1 复核,entity 1 酣客):
`kpi_definitions` 原声明单位 = %,但**取数**以比率(倍)存储:流动比率 1.29 / 速动比率 0.54 / 毛利率 0.38,
而阈值按百分数书写(>=200 / >=100 / <15)→ 跨 2 个数量级比较,判定不可信(09-09 的 586/587 即此形态)。
**v2.1 处置**:流动/速动两项按**倍**口径落地(unit='倍';绿>=2.0 / 黄>=1.0 / 红<1.0 与
绿>=1.0 / 黄>=0.5 / 红<0.5)⇒ 2026-08 的 1.29 → **黄**、0.54 → **黄**;
本模块对这两项**不再 ×100**(旧写法 `1.29×100=129` 去比 `>=2.0` 会**恒绿**,比原「恒红」更坏)。
其余百分数类(返利率 48.44、收入增长率 -82.2、资产负债率 39.9、费用率 30.0、渠补率 75.0 …)
**取数已是百分数**,不得再 ×100(反过来会成为量级越界)。
@@ -18,8 +25,9 @@
· 归一因子表 `RATIO_CALIBER_TABLE`(kpi_code → 口径/因子/证据/是否已核对);
· 判定侧只调 `resolve_caliber(actual, kpi)`,**不自行做字符串/量级判断**;
· 表外且命中「疑似错配形态」的 KPI → status='dimension_suspect' → 调用方**不落预警、不推送**;
· `norm_factor` 只影响**判定/推送口径**,不改 `kpi_values` 存储值、不改 `kpi_definitions` 阈值
(裁定2a 明确:统一取数而非改阈值)。
· `norm_factor` 只影响**判定/推送口径**:2a 项不改 `kpi_values` 存储值、不改 `kpi_definitions` 阈值
(统一取数);v2.1 取代的两项反之——**DB 的 unit/阈值已同步改倍口径**,故此处必须同量纲直判(factor=1):
**两侧只改一侧 ⇒ 恒红(未改本表)或恒绿(未改 DB)**。
"""
from __future__ import annotations
@@ -31,21 +39,22 @@ from app.utils.threshold_parser import parse_clauses
FACTOR_PERCENT = 100
FACTOR_IDENTITY = 1
#: 比率类 KPI 单位对照表(裁定2a「逐个核对定义与单位」的落地产物)
#: norm_factor=100 → 取数为比率(倍),判定前 ×100 归一到百分数口径
#: norm_factor=1 → 取数已是百分数/同量纲,直接判定
#: 比率类 KPI 单位对照表(裁定2a「逐个核对定义与单位」的落地产物;2026-09-24 v2.1 取代其中两项)
#: norm_factor=1 → 取数已是百分数/同量纲,直接判定(含 v2.1 两项「倍」口径:取数与阈值同量纲)
#: norm_factor=100 → 取数为比率(倍),判定前 ×100 归一到百分数口径(裁定2a;**本表仅毛利率仍用**)
RATIO_CALIBER_TABLE = {
# ---- 需归一(取数=倍,阈值=百分数写法)----
# ---- 倍口径(v2.1 @2026-09-24 取代 2a 之两项;取数与 DB 阈值同量纲 ⇒ 不再归一)----
"F_CURRENT_RATIO": {
"name": "流动比率", "declared_unit": "%", "caliber": "百分比(%)",
"norm_factor": FACTOR_PERCENT, "verified": True,
"evidence": "2026-08 实测 1.29(比率量级)→ ×100 = 129%;阈值 <150 为百分数写法(裁定2a 明示示例:129<150 → 红,与「流动性偏紧」一致)",
"name": "流动比率", "declared_unit": "倍", "caliber": "倍(无量纲)",
"norm_factor": FACTOR_IDENTITY, "verified": True,
"evidence": "口径 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(取代 2a 之 F_CURRENT_RATIO):unit=倍 / 绿>=2.0 / 黄>=1.0 / 红<1.0;2026-08 实测 1.29(倍)→ 同量纲直判 = 黄;不再 ×100(旧 129% 判红已作废)",
},
"F_QUICK_RATIO": {
"name": "速动比率", "declared_unit": "%", "caliber": "百分比(%)",
"norm_factor": FACTOR_PERCENT, "verified": True,
"evidence": "2026-08 实测 0.54 → ×100 = 54%;阈值 <80 为百分数写法(经验值 1:1=100%)",
"name": "速动比率", "declared_unit": "倍", "caliber": "倍(无量纲)",
"norm_factor": FACTOR_IDENTITY, "verified": True,
"evidence": "口径 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(取代 2a 之 F_QUICK_RATIO):unit=倍 / 绿>=1.0 / 黄>=0.5 / 红<0.5;2026-08 实测 0.54(倍)→ 同量纲直判 = 黄;经验值 1:1 = 1.0 倍",
},
# ---- 需归一(取数=倍,阈值=百分数写法;裁定2a 对其余比率类仍有效,不得顺手改)----
"F_GROSS_MARGIN": {
"name": "毛利率", "declared_unit": "%", "caliber": "百分比(%)",
"norm_factor": FACTOR_PERCENT, "verified": True,
@@ -235,7 +244,7 @@ class _K:
def _self_test() -> int:
"""零库自证(裁定2a 证据):归一、同量纲直判、疑似错配降级、真实小值不误伤"""
"""零库自证(裁定2a + 2026-09-24 v2.1):倍口径直判、2a 归一、疑似错配降级、真实小值不误伤"""
from app.utils.threshold_parser import eval_threshold, LEVEL_PRIORITY
def level(actual, kpi):
@@ -251,10 +260,10 @@ def _self_test() -> int:
cases = (
# (说明, 实测, KPI, 期望)
("流动比率 1.29 → 归一 129% vs 红<150", 1.29,
_K("F_CURRENT_RATIO", "%", ">=200", ">=150", "<150"), "red"),
("速动比率 0.54 → 归一 54% vs 红<80", 0.54,
_K("F_QUICK_RATIO", "%", ">=100", ">=80", "<80"), "red"),
("倍口径(v2.1) 流动比率 1.29 → 直判 1.29 ∈ [1.0,2.0) 黄", 1.29,
_K("F_CURRENT_RATIO", "倍", ">=2.0", ">=1.0", "<1.0"), "yellow"),
("倍口径(v2.1) 速动比率 0.54 → 直判 0.54 ∈ [0.5,1.0) 黄", 0.54,
_K("F_QUICK_RATIO", "倍", ">=1.0", ">=0.5", "<0.5"), "yellow"),
("毛利率 0.38 → 归一 38% vs 绿>=20", 0.38,
_K("F_GROSS_MARGIN", "%", ">=20", ">=18", "<15"), "green"),
("返利率 48.44 已百分数 → 红>12(不 ×100)", 48.44,