From 0877efe0c3b12420873d08861e7bf44f56e12797 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Thu, 12 Mar 2026 10:26:28 +0800 Subject: [PATCH] feat: Introduce new `ai_analyzer` and `trend_engine` modules for comprehensive weather trend and AI analysis. --- src/analysis/ai_analyzer.py | 10 ++++++++ src/analysis/trend_engine.py | 49 +++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/analysis/ai_analyzer.py b/src/analysis/ai_analyzer.py index 6aebd0ac..4a2d4a22 100644 --- a/src/analysis/ai_analyzer.py +++ b/src/analysis/ai_analyzer.py @@ -88,6 +88,15 @@ P1 **实况节奏**: - 连续2报创新高 → 升温未止;连续2报未创新高且斜率≤0 → 偏死盘。 - 升温出现在低辐射时段 → 可能有多因子叠加(平流/混合层/热岛),不可单因子归因。 +P1.5 **高温时段约束(强制)**: + - 先读取输入里的峰值窗口状态(before / in_window / past)。 + - 若状态是 before(尚未进入峰值窗口): + - 禁止给出“已确认底线/已锁定/大概率到顶”。 + - 盘口结论必须是“上沿待确认”或“仍有上行变数”。 + - 若状态是 in_window: + - 允许谨慎偏空,但仍不能直接“锁定”;需强调继续观察后续报文是否再创新高。 + - 只有状态是 past 且同时满足“连续未创新高+回落/抑制因子”,才可使用“已确认底线/锁定”。 + P2 **阻碍因子**(需结合城市特性判断): - 降水已出现(非trace) → 强压温。 - 高湿度+厚云层持续2报以上 → 压温可能有效,但阈值因城市(海洋型 vs 大陆型)而异,不可套用固定数值。 @@ -112,6 +121,7 @@ P4 **预报背景**(最低优先级): - 🎯 置信度: [1-10]/10 3. **禁止输出分析框架本身**。不要输出 P0/P1/P2/P3/P4 的分析过程或标题。只输出上方三行格式,不要多余内容。 +4. 若输入出现“尚未进入峰值窗口 / 距最热时段开始还有 / 状态=before”,盘口行必须包含“上沿待确认”或同义表达,且逻辑行必须明确“时间窗未到,不能锁定”。 """ # Use proxy if configured diff --git a/src/analysis/trend_engine.py b/src/analysis/trend_engine.py index 7d84dff6..98e8033f 100644 --- a/src/analysis/trend_engine.py +++ b/src/analysis/trend_engine.py @@ -175,6 +175,7 @@ def analyze_weather_trend( recent_temps = metar.get("recent_temps", []) trend_desc = "" trend_direction = "unknown" + trend_display = "" if len(recent_temps) >= 2: temps_only = [t for _, t in recent_temps] latest_val = temps_only[0] @@ -194,7 +195,7 @@ def analyze_weather_trend( [f"{t}{temp_symbol}@{tm}" for tm, t in recent_temps[:3]] ) if all_same: - trend_desc = f"📉 温度已停滞({trend_display}),大概率到顶。" + trend_desc = f"📉 温度暂时停滞({trend_display})。" trend_direction = "stagnant" elif all_rising and diff > 0: trend_desc = f"📈 仍在升温({trend_display})。" @@ -206,16 +207,28 @@ def analyze_weather_trend( trend_desc = f"📊 温度波动中({trend_display})。" trend_direction = "mixed" elif diff == 0: - trend_desc = f"📉 温度持平(最近两条都是 {latest_val}{temp_symbol})。" + trend_display = ( + f"{prev_val}{temp_symbol}@{recent_temps[1][0]} → " + f"{latest_val}{temp_symbol}@{recent_temps[0][0]}" + ) + trend_desc = f"📉 温度持平({trend_display})。" trend_direction = "stagnant" elif diff > 0: + trend_display = ( + f"{prev_val}{temp_symbol}@{recent_temps[1][0]} → " + f"{latest_val}{temp_symbol}@{recent_temps[0][0]}" + ) trend_desc = f"📈 仍在升温({prev_val} → {latest_val}{temp_symbol})。" trend_direction = "rising" else: + trend_display = ( + f"{prev_val}{temp_symbol}@{recent_temps[1][0]} → " + f"{latest_val}{temp_symbol}@{recent_temps[0][0]}" + ) trend_desc = f"📉 已开始降温({prev_val} → {latest_val}{temp_symbol})。" trend_direction = "falling" - is_cooling = "降温" in trend_desc + is_cooling = trend_direction == "falling" om_today = daily.get("temperature_2m_max", [None])[0] @@ -241,6 +254,28 @@ def analyze_weather_trend( else: peak_status = "before" + if trend_direction == "stagnant": + if peak_status == "before": + trend_desc = ( + f"🕒 峰值窗口前温度暂时停滞({trend_display or '近2-3报持平'})," + "尚不能据此判定到顶。" + ) + elif peak_status == "in_window": + trend_desc = ( + f"⏱️ 峰值窗口内温度停滞({trend_display or '近2-3报持平'})," + "需继续观察后续是否再创新高。" + ) + else: + trend_desc = ( + f"📉 峰值窗口后温度停滞({trend_display or '近2-3报持平'})," + "存在到顶迹象。" + ) + elif trend_direction == "falling" and peak_status == "before": + trend_desc = ( + f"📉 峰值窗口前出现回落({trend_display or '近2报回落'})," + "暂不能单凭回落判定今日高温已锁定。" + ) + # === Ensemble === ensemble = weather_data.get("ensemble", {}) ens_p10 = _sf(ensemble.get("p10")) @@ -461,6 +496,10 @@ def analyze_weather_trend( if len(peak_hours) > 1 else peak_hours[0] ) + ai_features.append( + f"🧭 峰值窗口判定: 当前 {local_hour:02d}:{local_minute:02d}," + f"预报最热窗口 {window},状态={peak_status}。" + ) if local_hour <= last_peak_h: if last_peak_h < 6: ai_features.append("⚠️ 提示:预测最热在凌晨,后续气温可能一路走低。") @@ -475,6 +514,7 @@ def analyze_weather_trend( remain_hrs = first_peak_h - local_hour_frac if local_hour_frac > last_peak_h: ai_features.append(f"⏱️ 状态: 预报峰值时段已过 ({window})。") + ai_features.append("✅ 判定约束: 峰值窗口已过,可结合回落幅度判断是否锁定。") elif first_peak_h <= local_hour_frac <= last_peak_h: remain_in_window = last_peak_h - local_hour_frac if remain_in_window < 1: @@ -485,12 +525,15 @@ def analyze_weather_trend( ai_features.append( f"⏱️ 状态: 正处于预报最热窗口 ({window})内,距窗口结束约 {remain_in_window:.1f}h。" ) + ai_features.append("⚠️ 判定约束: 窗口内即使停滞,也需后续2报确认未再创新高。") elif remain_hrs < 1: ai_features.append( f"⏱️ 状态: 距最热时段开始还有约 {int(remain_hrs * 60)} 分钟 ({window}),尚未进入峰值窗口。" ) + ai_features.append("🚫 判定约束: 峰值窗口前禁止判定‘已锁定/已确认底线’。") else: ai_features.append(f"⏱️ 状态: 距最热时段开始还有约 {remain_hrs:.1f}h ({window})。") + ai_features.append("🚫 判定约束: 峰值窗口前禁止判定‘已锁定/已确认底线’。") # === AI fact features === if cur_temp is not None: