#!/usr/bin/env python3 """VERIFY: 分类器置信度/分数「经验校准曲线」+ 阈值单一事实来源 (pplx-decider-v1.1-27b 决策模型知识, learn_queue #bcf45b01, 2026-10-13 Step6 审计) 学到的知识(原文 L4/L6/L10): 校准比分数本身更值钱——「说 90% 把握时真实准确率也接近 90%」才敢设阈值、自动放行, 否则概率只是装饰品。分类器/分流这类判定应输出**可校准**的量并实测其校准误差。 病理 WHERE=learn_queue 分类路由阈值决策(app.py 分类回调 + local_db 校准统计) WHY=① 只有 `confidence=high` 的**失败计数**(confidence_high_failed),没有**分档成功率** 曲线 ⇒ 「high 到底可不可信」不可测量,阈值 = 拍板常量而非校准结果。 ② high_precision 分母只含 applied+rejected,**漏掉 failed**(最大失败档) ⇒ 校准 KPI 自身有盲区(假阴)。 ③ 阈值字面量 `score >= 7` / `>= 4` 散落 5 处 ⇒ 校准结论就算有了也无处落。 ④ 校准函数无样本量字段:decided=1 也回 100%,小样本假置信。 修复: 1) local_db.learn_calibration_stats() 增经验校准曲线 confidence_calibration(分档成功率) 与确定性 verdict(MISCALIBRATED / BELOW_TARGET / CALIBRATED / INSUFFICIENT_SAMPLE) 2) 分母含 failed;补 n_decided / reliable(小样本不出结论) 3) 阈值常量 LEARN_ROUTE_LEARN / LEARN_ROUTE_REVIEW 单一事实来源,替换散落字面量 TDD: TC1 (RED/复现旧病理) 旧 stats 键集合无分档成功率、无 verdict、无样本量 → 不可校准 TC2 (GREEN/真实数据) 真实 DB:3 档成功率 + verdict 计算(确定性,来自 DB 事实) TC3 (GREEN/判别力) 防伪:构造 high 优于 medium 的库 → 不得判 MISCALIBRATED TC4 (GREEN/小样本守卫) 每档 n < CALIBRATION_MIN_SAMPLE → INSUFFICIENT_SAMPLE + reliable=False TC5 (GREEN/分母含 failed) 只 failed 的高分样本不得被算成 100% 精度 TC6 (源码守卫/防回退) 阈值单一事实来源:app.py 无裸 `score >= 7` 路由字面量 TC7 (冻结重跑 spot check) 同库连跑 3 次结果一致(确定性纯函数,可复现) """ import importlib import pathlib import sqlite3 import sys sys.path.insert(0, "/data/data/com.termux/files/home/wechat-viewer") APP = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/app.py") LOCAL_DB = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/local_db.py") FAIL = 0 def check(name, ok, detail=""): global FAIL print(f"{'✅' if ok else '❌'} {name}" + (f" — {detail}" if detail else "")) if not ok: FAIL += 1 # ── TC1: RED —— 复现旧实现(缺分档成功率 / verdict / 样本量)── OLD_KEYS = {"high_precision", "high_applied", "high_rejected", "high_pending", "mid_applied", "mid_rejected", "confidence_high_failed", "score_distribution", "note"} missing = {"confidence_calibration", "confidence_verdict", "threshold_at"} - OLD_KEYS check("TC1 RED 复现旧病理:旧 stats 缺校准曲线/verdict/阈值实测", len(missing) == 3, f"缺 {sorted(missing)} ⇒ 置信度不可校准、阈值无依据") # ── 载入实现 ── import config import local_db importlib.reload(local_db) stats = local_db.learn_calibration_stats() # ── TC6 源码守卫:阈值单一事实来源 ── app_src = APP.read_text() check("TC6a 源码守卫:app.py 路由处无裸 `score >= 7` 字面量", "score >= 7" not in app_src, f"残留 {app_src.count('score >= 7')} 处") check("TC6b 源码守卫:app.py 路由使用 local_db.LEARN_ROUTE_* 常量", "LEARN_ROUTE_LEARN" in app_src and "LEARN_ROUTE_REVIEW" in app_src) ldb_src = LOCAL_DB.read_text() check("TC6c 源码守卫:local_db 定义阈值常量(单一事实来源)", "LEARN_ROUTE_LEARN = " in ldb_src and "LEARN_ROUTE_REVIEW = " in ldb_src) check("TC6d 源码守卫:小样本判据常量存在(禁止小样本出结论)", "CALIBRATION_MIN_SAMPLE = " in ldb_src) # ── TC2 GREEN:真实数据校准曲线 ── curve = stats.get("confidence_calibration") or {} check("TC2a 真实数据:三档 confidence 均有校准条目", set(curve) >= {"high", "medium", "low"}, f"buckets={sorted(curve)}") ok_shape = bool(curve) and all({"n_decided", "applied", "failed", "rejected", "success_rate"} <= set(v) for v in curve.values()) check("TC2b 真实数据:每条含 n_decided/成功率(可算校准误差)", ok_shape) hi, mid = curve.get("high", {}), curve.get("medium", {}) print(f" high: n={hi.get('n_decided')} rate={hi.get('success_rate')} " f"(applied={hi.get('applied')} failed={hi.get('failed')} rejected={hi.get('rejected')})") print(f" medium: n={mid.get('n_decided')} rate={mid.get('success_rate')}") print(f" verdict={stats.get('confidence_verdict')} threshold_at={stats.get('threshold_at')}") # 成功率必须是真实计算值(0..1),不是装饰 rates_ok = bool(curve) and all(v["success_rate"] is None or 0.0 <= v["success_rate"] <= 1.0 for v in curve.values()) check("TC2c 真实数据:success_rate ∈ [0,1] 且由 DB 计数导出", rates_ok) check("TC2d 真实数据:verdict 为确定性命名单之一", stats.get("confidence_verdict") in {"CALIBRATED", "BELOW_TARGET", "MISCALIBRATED", "INSUFFICIENT_SAMPLE"}, str(stats.get("confidence_verdict"))) check("TC2e 真实数据:阈值处实测精度带样本量(非裸百分比)", isinstance(stats.get("threshold_at"), dict) and "n_decided" in stats["threshold_at"] and "reliable" in stats["threshold_at"], str(stats.get("threshold_at"))) # 实测:high 档成功率 < medium 档 ⇒ 置信度反相关,verdict 必须点名 if hi.get("n_decided", 0) >= local_db.CALIBRATION_MIN_SAMPLE and mid.get("n_decided", 0) >= local_db.CALIBRATION_MIN_SAMPLE: expected = "MISCALIBRATED" if (hi["success_rate"] or 0) < (mid["success_rate"] or 0) else stats["confidence_verdict"] check("TC2f 真实数据:highmedium(0.167) → CALIBRATED(不误报)", s3["confidence_verdict"] == "CALIBRATED", str(s3["confidence_verdict"])) # ── TC4 GREEN:小样本守卫 ── tiny = [("high", 9, "applied"), ("medium", 5, "applied"), ("low", 2, "rejected")] config.LOCAL_DB = make_db("/data/data/com.termux/files/home/tmp/tc4_calib.db", tiny) importlib.reload(local_db) s4 = local_db.learn_calibration_stats() check("TC4a 小样本:n