From 0ce9ec25c305b39c494a392080d3925338cce515 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Wed, 4 Mar 2026 02:09:29 +0800 Subject: [PATCH] fix: sync reality-anchored mu to bot, add graded severity, prohibit P0-P4 output --- bot_listener.py | 59 ++++++++++++++++++++++++++++++++----- src/analysis/ai_analyzer.py | 2 ++ 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/bot_listener.py b/bot_listener.py index 0e8dcbcb..febd50c6 100644 --- a/bot_listener.py +++ b/bot_listener.py @@ -271,14 +271,57 @@ def analyze_weather_trend(weather_data, temp_symbol, city_name=None): is_dead_market = True if ens_p10 is not None and ens_p90 is not None and not is_dead_market: - # (现有概率计算逻辑保留,但增加 is_dead_market 排除) - mu = ( - forecast_median * 0.7 + ens_median * 0.3 - if forecast_median is not None - else ens_median - ) - if max_so_far is not None and max_so_far > mu: - mu = max_so_far + (0.3 if not is_cooling else 0.0) + # --- Reality-anchored μ --- + # Determine peak status + if local_hour_frac > last_peak_h: + _peak_status = "past" + elif first_peak_h <= local_hour_frac <= last_peak_h: + _peak_status = "in_window" + else: + _peak_status = "before" + + # Compute forecast miss magnitude + forecast_miss_deg = 0 + if max_so_far is not None and forecast_median is not None: + forecast_miss_deg = round(forecast_median - max_so_far, 1) + + # If peak is past/in_window AND actual max is significantly below + # forecasts, anchor μ on actual max, not on failed predictions + if ( + max_so_far is not None + and forecast_median is not None + and _peak_status in ("past", "in_window") + and max_so_far < forecast_median - 2.0 + ): + if is_cooling or _peak_status == "past": + mu = max_so_far + else: + mu = max_so_far + 0.5 + else: + mu = ( + forecast_median * 0.7 + ens_median * 0.3 + if forecast_median is not None + else ens_median + ) + if max_so_far is not None and max_so_far > mu: + mu = max_so_far + (0.3 if not is_cooling else 0.0) + + # Inject forecast miss severity for AI + if forecast_miss_deg > 2.0 and _peak_status in ("past", "in_window"): + if forecast_miss_deg > 5.0: + severity = "重" + elif forecast_miss_deg > 3.0: + severity = "中" + else: + severity = "轻" + min_fc = min( + (v for v in forecast_highs if v is not None), default=None + ) + _trend_dir = "降温" if is_cooling else ("停滞" if "停滞" in trend_desc else "升温") + ai_features.append( + f"🚨 预报崩盘 [{severity}级失准]: 最低预报 {min_fc}{temp_symbol} vs 实测最高 {max_so_far}{temp_symbol}," + f"偏差 {forecast_miss_deg}°。当前趋势: {_trend_dir}。" + ) def _norm_cdf(x, m, s): return 0.5 * (1 + _math.erf((x - m) / (s * _math.sqrt(2)))) diff --git a/src/analysis/ai_analyzer.py b/src/analysis/ai_analyzer.py index e42e4113..de5d8028 100644 --- a/src/analysis/ai_analyzer.py +++ b/src/analysis/ai_analyzer.py @@ -67,6 +67,8 @@ P4 **预报背景**(最低优先级): - 🎲 盘口: [给出结算判断。用"已确认底线 X{temp_symbol}"表示下限确定;用"上沿待确认,关注 Y{temp_symbol}"表示仍有变数。若预报严重失准,注明失准等级和原因。禁止在升温未止时用"锁定"。] - 💡 逻辑: [3-5 句深度分析。含具体数值。预报失准时重点分析偏差成因。正常时分析实测与预报的动态博弈。] - 🎯 置信度: [1-10]/10 + +3. **禁止输出分析框架本身**。不要输出 P0/P1/P2/P3/P4 的分析过程或标题。只输出上方三行格式,不要多余内容。 """ # Use proxy if configured