feat: Introduce AI analysis module and refactor bot_listener.py to prepare weather data for AI-driven insights.

This commit is contained in:
2569718930@qq.com
2026-02-27 01:11:28 +08:00
parent 3aee0e7832
commit eae41966e2
2 changed files with 118 additions and 233 deletions
+114 -230
View File
@@ -15,8 +15,9 @@ from src.data_collection.weather_sources import WeatherDataCollector # type: ig
from src.data_collection.city_risk_profiles import get_city_risk_profile, format_risk_warning # type: ignore
def analyze_weather_trend(weather_data, temp_symbol):
"""根据实测与预测分析气温态势,增加峰值时刻预测"""
'''根据实测与预测分析气温态势,增加峰值时刻预测'''
insights: List[str] = []
ai_features: List[str] = []
metar = weather_data.get("metar", {})
open_meteo = weather_data.get("open-meteo", {})
@@ -25,11 +26,14 @@ def analyze_weather_trend(weather_data, temp_symbol):
mgm = weather_data.get("mgm", {})
if not metar or not open_meteo:
return ""
return "", ""
curr_temp = metar.get("current", {}).get("temp")
max_so_far = metar.get("current", {}).get("max_temp_so_far") # 今日实测最高
daily = open_meteo.get("daily", {})
hourly = open_meteo.get("hourly", {})
times = hourly.get("time", [])
temps = hourly.get("temperature_2m", [])
# === 核心:整合多源预报最高温 ===
forecast_highs = [daily.get("temperature_2m_max", [None])[0]]
@@ -37,293 +41,182 @@ def analyze_weather_trend(weather_data, temp_symbol):
forecast_highs.append(mb["today_high"])
if nws.get("today_high") is not None:
forecast_highs.append(nws["today_high"])
# 加入多模型预报 (ECMWF, GFS, ICON, GEM, JMA)
for mv in weather_data.get("multi_model", {}).get("forecasts", {}).values():
if mv is not None:
forecast_highs.append(mv)
forecast_highs = [h for h in forecast_highs if h is not None]
# 取预报中的最高值作为风险防御基准
forecast_high = max(forecast_highs) if forecast_highs else None
# 取最低值用于判断是否“已触及预报高位”
min_forecast_high = min(forecast_highs) if forecast_highs else forecast_high
# 取中位数作为用户可见的"预期值"(避免极端模型误导)
forecast_median = None
if forecast_highs:
sorted_fh = sorted(forecast_highs)
forecast_median = sorted_fh[len(sorted_fh) // 2]
forecast_median = sorted(forecast_highs)[len(forecast_highs) // 2] if forecast_highs else None
wind_speed = metar.get("current", {}).get("wind_speed_kt", 0)
# 获取当地时间小时
local_time_full = open_meteo.get("current", {}).get("local_time", "")
try:
local_date_str = local_time_full.split(" ")[0] # YYYY-MM-DD
local_date_str = local_time_full.split(" ")[0]
local_hour = int(local_time_full.split(" ")[1].split(":")[0])
except:
from datetime import datetime
local_date_str = datetime.now().strftime("%Y-%m-%d")
local_hour = datetime.now().hour
# === 模型共识评分 ===
# 主要来源: 多模型预报 (ECMWF, GFS, ICON, GEM, JMA)
multi_model = weather_data.get("multi_model", {})
mm_forecasts = multi_model.get("forecasts", {})
labeled_forecasts = []
for model_name, model_val in mm_forecasts.items():
if model_val is not None:
labeled_forecasts.append((model_name, model_val))
# 额外独立源 (如有)
if mb.get("today_high") is not None:
labeled_forecasts.append(("MB", mb["today_high"]))
if nws.get("today_high") is not None:
labeled_forecasts.append(("NWS", nws["today_high"]))
# Open-Meteo 确定性预报(用于后续偏差检测,不重复加入共识)
om_today = daily.get("temperature_2m_max", [None])[0]
# 集合预报数据 (仅用于不确定性区间展示)
ensemble = weather_data.get("ensemble", {})
ens_median = ensemble.get("median")
# === METAR 趋势分析 (移到前部判断降温) ===
recent_temps = metar.get("recent_temps", [])
trend_desc = ""
if len(recent_temps) >= 2:
temps_only = [t for _, t in recent_temps]
latest_val = temps_only[0]
prev_val = temps_only[1]
diff = latest_val - prev_val
if len(temps_only) >= 3:
all_same = all(t == latest_val for t in temps_only[:3])
all_rising = all(temps_only[i] >= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1))
all_falling = all(temps_only[i] <= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1))
trend_display = "".join([f"{t}{temp_symbol}@{tm}" for tm, t in recent_temps[:3]])
if all_same: trend_desc = f"📉 温度已停滞({trend_display}),大概率到顶。"
elif all_rising and diff > 0: trend_desc = f"📈 仍在升温({trend_display})。"
elif all_falling and diff < 0: trend_desc = f"📉 已开始降温({trend_display})。"
else: trend_desc = f"📊 温度波动中({trend_display})。"
elif diff == 0: trend_desc = f"📉 温度持平(最近两条都是 {latest_val}{temp_symbol})。"
elif diff > 0: trend_desc = f"📈 仍在升温({prev_val}{latest_val}{temp_symbol})。"
else: trend_desc = f"📉 已开始降温({prev_val}{latest_val}{temp_symbol})。"
consensus_level = "unknown"
consensus_spread = None
is_cooling = "降温" in trend_desc
# === 模型共识评分 ===
mm_forecasts = weather_data.get("multi_model", {}).get("forecasts", {})
labeled_forecasts = [(model_name, model_val) for model_name, model_val in mm_forecasts.items() if model_val is not None]
if mb.get("today_high") is not None: labeled_forecasts.append(("MB", mb["today_high"]))
if nws.get("today_high") is not None: labeled_forecasts.append(("NWS", nws["today_high"]))
if len(labeled_forecasts) >= 2:
f_values = [v for _, v in labeled_forecasts]
f_max = max(f_values)
f_min = min(f_values)
consensus_spread = f_max - f_min
f_avg = sum(f_values) / len(f_values)
# 动态阈值:华氏度场景用更大的容差
is_f = (temp_symbol == "°F")
tight_threshold = 1.5 if is_f else 0.8 # 高共识
mid_threshold = 3.0 if is_f else 1.5 # 中共识
consensus_spread = max(f_values) - min(f_values)
tight_threshold = 1.5 if temp_symbol == "°F" else 0.8
mid_threshold = 3.0 if temp_symbol == "°F" else 1.5
parts = " | ".join([f"{name} {val}{temp_symbol}" for name, val in labeled_forecasts])
if consensus_spread <= tight_threshold:
consensus_level = "high"
insights.append(
f"🎯 <b>模型共识:高 ({len(labeled_forecasts)}/{len(labeled_forecasts)})</b> — "
f"{parts},极差仅 {consensus_spread:.1f}°,预报高度一致。"
)
msg = f"🎯 <b>模型共识:高 ({len(labeled_forecasts)}源)</b> — {parts},极差仅 {consensus_spread:.1f}°,高度一致。"
elif consensus_spread <= mid_threshold:
consensus_level = "medium"
insights.append(
f"⚖️ <b>模型共识:中 ({len(labeled_forecasts)}源)</b> — "
f"{parts},极差 {consensus_spread:.1f}°,有轻微分歧。"
)
msg = f"⚖️ <b>模型共识:中 ({len(labeled_forecasts)}源)</b> — {parts},极差 {consensus_spread:.1f}°,有轻微分歧。"
else:
consensus_level = "low"
# 找出最高和最低的源
highest = max(labeled_forecasts, key=lambda x: x[1])
lowest = min(labeled_forecasts, key=lambda x: x[1])
insights.append(
f"⚠️ <b>模型共识:低 ({len(labeled_forecasts)}源)</b> — "
f"{parts},极差 {consensus_spread:.1f}°!"
f"{highest[0]} 最高 ({highest[1]}{temp_symbol}) vs {lowest[0]} 最低 ({lowest[1]}{temp_symbol}),不确定性大。"
)
msg = f"⚠️ <b>模型共识:低 ({len(labeled_forecasts)}源)</b> — {parts},极差 {consensus_spread:.1f}°!{highest[0]} 最高 vs {lowest[0]} 最低。"
# 移交 AI 处理,不再给用户直接显示博弈区间
ai_features.append(msg)
elif len(labeled_forecasts) == 1:
name, val = labeled_forecasts[0]
insights.append(
f"📡 <b>仅1个预报源 ({name} {val}{temp_symbol})</b> — 无法交叉验证,共识评分不可用。"
)
msg = f"📡 <b>仅1个预报源 ({labeled_forecasts[0][0]} {labeled_forecasts[0][1]}{temp_symbol})</b>"
# 移交 AI 处理,不再给用户直接显示博弈区间
ai_features.append(msg)
# === 博弈区间提醒 (基于 WU 四舍五入结算) ===
# === 博弈区间提醒 ===
if len(labeled_forecasts) >= 2:
import math
wu_round = lambda v: math.floor(v + 0.5)
settlement_vals = sorted(set(wu_round(v) for _, v in labeled_forecasts))
unit_short = temp_symbol
# 如果实测已超所有预报,用实测值重新评估博弈区间
if max_so_far is not None and forecast_high is not None and max_so_far > forecast_high + 0.5:
actual_settled = wu_round(max_so_far)
if actual_settled not in settlement_vals:
all_vals = sorted(set(settlement_vals + [actual_settled]))
else:
all_vals = settlement_vals
insights.append(
f"🎲 <b>博弈区间</b>:模型预报已失效!实测最高 {max_so_far}{unit_short} → WU <b>{actual_settled}{unit_short}</b>"
f"但温度仍可能继续变化。"
)
msg = f"🎲 <b>博弈区间</b>:预报已失效!实测最高 {max_so_far}{temp_symbol} → WU <b>{actual_settled}{temp_symbol}</b>,温度仍可能波动。"
elif len(settlement_vals) == 1:
insights.append(f"🎲 <b>博弈区间</b>{len(labeled_forecasts)}模型全部指向 <b>{settlement_vals[0]}{unit_short}</b> 结算。")
msg = f"🎲 <b>博弈区间</b>:模型全部指向 <b>{settlement_vals[0]}{temp_symbol}</b> 结算。"
elif len(settlement_vals) == 2:
insights.append(f"🎲 <b>博弈区间</b>温度在 <b>{settlement_vals[0]}{unit_short}</b> 和 <b>{settlement_vals[1]}{unit_short}</b> 之间博弈。")
msg = f"🎲 <b>博弈区间</b>:在 <b>{settlement_vals[0]}{temp_symbol}</b> 和 <b>{settlement_vals[1]}{temp_symbol}</b> 之间博弈。"
elif len(settlement_vals) == 3:
insights.append(f"🎲 <b>博弈区间</b>温度在 <b>{settlement_vals[0]}{unit_short}</b>、<b>{settlement_vals[1]}{unit_short}</b>、<b>{settlement_vals[2]}{unit_short}</b> 之间博弈。")
msg = f"🎲 <b>博弈区间</b>:在 <b>{settlement_vals[0]}{temp_symbol}</b>、<b>{settlement_vals[1]}{temp_symbol}</b>、<b>{settlement_vals[2]}{temp_symbol}</b> 之间博弈。"
else:
insights.append(f"🎲 <b>博弈区间</b>:模型分歧太大,结算还不确定。")
# 集合预报区间 (独立于共识评分显示)
msg = f"🎲 <b>博弈区间</b>:模型分歧太大,结算还不确定。"
# 移交 AI 处理,不再给用户直接显示博弈区间
ai_features.append(msg)
# === 集合预报区间 (去除了啰嗦的预报验证) ===
ensemble = weather_data.get("ensemble", {})
ens_p10 = ensemble.get("p10")
ens_p90 = ensemble.get("p90")
ens_median = ensemble.get("median")
om_today = daily.get("temperature_2m_max", [None])[0]
if ens_p10 is not None and ens_p90 is not None and ens_median is not None:
ens_range = ens_p90 - ens_p10
insights.append(
f"📊 <b>集合预报</b>:中位数 {ens_median}{temp_symbol}"
f"90% 区间 [{ens_p10}{temp_symbol} - {ens_p90}{temp_symbol}]"
f"波动幅度 {ens_range:.1f}°。"
)
# 确定性预报 vs 集合分布偏差检测
msg1 = f"📊 <b>集合预报</b>:中位数 {ens_median}{temp_symbol}90% 区间 [{ens_p10}{temp_symbol} - {ens_p90}{temp_symbol}]。"
if not is_cooling: insights.append(msg1)
ai_features.append(msg1)
if om_today is not None:
actual_reached = max_so_far is not None and max_so_far >= om_today - 0.5
if om_today > ens_p90:
if actual_reached:
# 实测已达到预报值 → 确定性预报是对的,集合偏保守
insights.append(
f" <b>预报验证</b>:确定性预报 {om_today}{temp_symbol} 已被实测验证 "
f"(实测最高 {max_so_far}{temp_symbol}),集合预报偏保守。"
)
else:
# 还没到最高温,存在偏高风险
delta = om_today - ens_median
insights.append(
f"⚡ <b>预报偏高警告</b>:确定性预报 {om_today}{temp_symbol} "
f"超过了集合 90% 上限 ({ens_p90}{temp_symbol})"
f"比中位数高 {delta:.1f}°。实际高温更可能接近 {ens_median}{temp_symbol}"
)
elif om_today < ens_p10:
if max_so_far is not None and max_so_far >= ens_median:
# 实测已超过中位数 → 确定性预报偏低,集合更准
insights.append(
f"✅ <b>预报验证</b>:实测最高 {max_so_far}{temp_symbol} "
f"已超过确定性预报 {om_today}{temp_symbol},集合中位数 {ens_median}{temp_symbol} 更准确。"
)
else:
delta = ens_median - om_today
insights.append(
f"⚡ <b>预报偏低警告</b>:确定性预报 {om_today}{temp_symbol} "
f"低于集合 90% 下限 ({ens_p10}{temp_symbol})"
f"比中位数低 {delta:.1f}°。实际高温更可能接近 {ens_median}{temp_symbol}"
)
# === 核心判断:实测是否已超预报 ===
is_breakthrough = False
# METAR 趋势分析(最近 3-4 条报文)
recent_temps = metar.get("recent_temps", []) # [("15:00", 5), ("14:20", 5), ("14:00", 3)] 倒序
trend_desc = ""
if len(recent_temps) >= 2:
temps_only = [t for _, t in recent_temps] # 倒序:最新在前
latest_val = temps_only[0]
prev_val = temps_only[1]
diff = latest_val - prev_val
if len(temps_only) >= 3:
# 3 条以上:判断整体趋势
all_same = all(t == latest_val for t in temps_only[:3])
all_rising = all(temps_only[i] >= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1))
all_falling = all(temps_only[i] <= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1))
trend_display = "".join([f"{t}{temp_symbol}@{tm}" for tm, t in recent_temps[:3]])
if all_same:
trend_desc = f"📉 温度已停滞({trend_display}),大概率到顶。"
elif all_rising and diff > 0:
trend_desc = f"📈 仍在升温({trend_display})。"
elif all_falling and diff < 0:
trend_desc = f"📉 已开始降温({trend_display})。"
else:
trend_desc = f"📊 温度波动中({trend_display})。"
elif diff == 0:
trend_desc = f"📉 温度持平(最近两条都是 {latest_val}{temp_symbol})。"
elif diff > 0:
trend_desc = f"📈 仍在升温({prev_val}{latest_val}{temp_symbol})。"
else:
trend_desc = f"📉 已开始降温({prev_val}{latest_val}{temp_symbol})。"
if om_today > ens_p90 and (max_so_far is None or max_so_far < om_today - 0.5):
msg2 = f"⚡ <b>预报偏高警告</b>:确定性预报 {om_today}{temp_symbol} 超集合90%上限!更可能接近 {ens_median}{temp_symbol}"
if not is_cooling: insights.append(msg2)
ai_features.append(msg2)
elif om_today < ens_p10 and (max_so_far is None or max_so_far < ens_median):
msg2 = f" <b>预报偏低警告</b>:确定性预报 {om_today}{temp_symbol} 低于集合90%下限!更可能接近 {ens_median}{temp_symbol}"
if not is_cooling: insights.append(msg2)
ai_features.append(msg2)
# === 实测已超预报 & 趋势输出 ===
if max_so_far is not None and forecast_high is not None:
if max_so_far > forecast_high + 0.5:
is_breakthrough = True
exceed_by = max_so_far - forecast_high
# 合并为一条:事实 + 趋势(不给主观建议)
bt_msg = (
f"🚨 <b>实测已超预报</b>{max_so_far}{temp_symbol} 超过预报上限 "
f"{forecast_high}{temp_symbol}+{exceed_by:.1f}°)。"
)
if trend_desc:
bt_msg += f"\n{trend_desc}"
bt_msg = f"🚨 <b>实测已超预报</b>{max_so_far}{temp_symbol} 超过上限 {forecast_high}{temp_symbol}+{exceed_by:.1f}°)。"
if trend_desc: bt_msg += f"\n{trend_desc}"
insights.append(bt_msg)
ai_features.append(f"🚨 异常: 实测已冲破所有预报上限 ({max_so_far}{temp_symbol} vs {forecast_high}{temp_symbol})。")
ai_features.append(trend_desc)
else:
if trend_desc:
insights.append(trend_desc)
ai_features.append(trend_desc)
elif trend_desc:
insights.append(trend_desc)
ai_features.append(trend_desc)
# === 结算取整分析 (Wunderground 四舍五入到整数) ===
# === 结算取整分析 ===
if max_so_far is not None:
settled = round(max_so_far)
fractional = max_so_far - int(max_so_far)
# 离取整边界的距离
dist_to_boundary = abs(fractional - 0.5)
if dist_to_boundary <= 0.3:
# 在边界附近 (X.2 ~ X.8),取整结果可能随时翻转
if fractional < 0.5:
insights.append(
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}"
f"WU 结算 <b>{settled}{temp_symbol}</b>"
f"但只差 <b>{0.5 - fractional:.1f}°</b> 就会进位到 {settled + 1}{temp_symbol}"
)
msg = f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol} → WU 结算 <b>{settled}{temp_symbol}</b>,但只差 <b>{0.5 - fractional:.1f}°</b> 就会进位到 {settled + 1}{temp_symbol}"
else:
insights.append(
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}"
f"WU 结算 <b>{settled}{temp_symbol}</b>"
f"刚刚越过进位线,再降 <b>{fractional - 0.5:.1f}°</b> 就会回落到 {settled - 1}{temp_symbol}"
)
msg = f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol} → WU 结算 <b>{settled}{temp_symbol}</b>,刚刚越过进位线,再降 <b>{fractional - 0.5:.1f}°</b> 就会回落到 {settled - 1}{temp_symbol}"
insights.append(msg)
ai_features.append(msg)
# --- 峰值时刻预测逻辑 (仍以 Open-Meteo 逐小时数据为准) ---
hourly = open_meteo.get("hourly", {})
times = hourly.get("time", [])
temps = hourly.get("temperature_2m", [])
# === 峰值时刻预测 (只在还没过峰值时显示) ===
peak_hours = []
om_high = daily.get("temperature_2m_max", [None])[0]
if times and temps and om_high is not None:
if times and temps and om_today is not None:
for t_str, temp in zip(times, temps):
if t_str.startswith(local_date_str):
if abs(temp - om_high) <= 0.2:
hour = t_str.split("T")[1][:5]
peak_hours.append(hour)
# 确定用于逻辑判断的峰值小时
if t_str.startswith(local_date_str) and abs(temp - om_today) <= 0.2:
peak_hours.append(t_str.split("T")[1][:5])
if peak_hours:
first_peak_h = int(peak_hours[0].split(":")[0])
last_peak_h = int(peak_hours[-1].split(":")[0])
window = f"{peak_hours[0]} - {peak_hours[-1]}" if len(peak_hours) > 1 else peak_hours[0]
insights.append(f"⏱️ <b>预计最热时段</b>:今天 <b>{window}</b>。")
if last_peak_h < 6:
insights.append(f"⚠️ <b>提示</b>:预测最热在凌晨,后续气温可能一路走低。")
elif local_hour < first_peak_h and (max_so_far is None or max_so_far < forecast_high):
target_temp = om_today if om_today is not None else forecast_high
insights.append(f"🎯 <b>关注重点</b>:看看那个时段温度能不能真的到 {target_temp}{temp_symbol}")
if local_hour <= last_peak_h:
if last_peak_h < 6:
ai_features.append(f"⚠️ <b>提示</b>:预测最热在凌晨,后续气温可能一路走低。")
elif local_hour < first_peak_h and (max_so_far is None or max_so_far < forecast_high):
target_temp = om_today if om_today is not None else forecast_high
ai_features.append(f"🎯 <b>关注重点</b>:看看那个时段能否涨到 {target_temp}{temp_symbol}")
# 写给AI
if local_hour > last_peak_h: ai_features.append(f"⏱️ 状态: 预报峰值时段已过 ({window})。")
elif first_peak_h <= local_hour <= last_peak_h: ai_features.append(f"⏱️ 状态: 正处于预报最热窗口 ({window})内。")
else: ai_features.append(f"⏱️ 状态: 距最热时段还有 {first_peak_h - local_hour}h ({window})。")
else:
# 兜底默认值
first_peak_h, last_peak_h = 13, 15
# --- 简化的 AI 特征提取 (不对用户双重显示,仅供 AI 使用) ---
ai_features = list(insights)
# 不再生成死板的分析文案,仅保留核心事实描述
# 1. 气温节奏特征
if local_hour > last_peak_h:
ai_features.append(f"⏱️ 状态: 预报峰值时段已过 ({window})。")
elif first_peak_h <= local_hour <= last_peak_h:
ai_features.append(f"⏱️ 状态: 正处于预报最热窗口 ({window})内。")
else:
ai_features.append(f"⏱️ 状态: 距最热时段还有 {first_peak_h - local_hour}h ({window})。")
# 2. 气温偏差特征
if max_so_far is not None and forecast_high is not None:
gap = max_so_far - forecast_high
if gap > 0.5:
ai_features.append(f"🚨 异常: 实测已冲破所有预报上限 ({max_so_far}{temp_symbol} vs {forecast_high}{temp_symbol})。")
elif abs(gap) <= 1.0:
ai_features.append(f"⚖️ 状态: 实测已极度接近预报峰值。")
# 3. 气象动力特征描述 (无主观推测)
# === 其他 AI 专供的事实特征 ===
if wind_speed:
wind_dir = metar.get("current", {}).get("wind_dir", "未知")
ai_features.append(f"🌬️ 当下风况: 约 {wind_speed}kt (方向 {wind_dir}°)。")
humidity = metar.get("current", {}).get("humidity")
if humidity and humidity > 80:
ai_features.append(f"💦 湿度极高 ({humidity}%)。")
if humidity and humidity > 80: ai_features.append(f"💦 湿度极高 ({humidity}%)。")
clouds = metar.get("current", {}).get("clouds", [])
if clouds:
@@ -332,10 +225,8 @@ def analyze_weather_trend(weather_data, temp_symbol):
ai_features.append(f"☁️ 天空状况: {c_desc}")
wx_desc = metar.get("current", {}).get("wx_desc")
if wx_desc:
ai_features.append(f"🌧️ 天气现象: {wx_desc}")
if wx_desc: ai_features.append(f"🌧️ 天气现象: {wx_desc}")
# 4. 暖平流事实提取
max_temp_time_str = metar.get("current", {}).get("max_temp_time", "")
if max_so_far is not None and max_temp_time_str:
try:
@@ -350,13 +241,6 @@ def analyze_weather_trend(weather_data, temp_symbol):
ai_features.append(f"🌙 动力事实: 最高温出现在低辐射时段 ({max_temp_time_str}, 辐射{max_temp_rad:.0f}W/m²)。")
except: pass
# 5. 结算判定
if max_so_far is not None:
settled = round(max_so_far)
fractional = max_so_far - int(max_so_far)
if abs(fractional - 0.5) <= 0.2:
ai_features.append(f"⚖️ 结算事实: 当前最高 {max_so_far}{temp_symbol} 处于进位关键点 ({settled}{temp_symbol})。")
display_str = "\n".join(insights) if insights else ""
return display_str, "\n".join(ai_features)
+4 -3
View File
@@ -30,15 +30,16 @@ def get_ai_analysis(weather_insights: str, city_name: str, temp_symbol: str) ->
【分析重点】
1. **动力来源**:对比太阳辐射(W/m²)与最高温出现时间。如果低辐射时段气温冲高,说明是强暖平流,预报往往低估这种惯性。
2. **阻碍因子**:由于高湿度(>80%)、降水或全阴天气导致的升温失速。
3. **模型 spread**:多模型极差如果很大,说明结算极具博弈价值
3. **结算推演**:根据我提供给你的【博弈区间】以及【当前所处时段(是否过了最热期)】推断并告诉我最终结算温度落在哪个区间的希望更大
4. **结算边界**:如果当前温度处于 X.5 这种进位/舍位边缘,需特别预警。
【输出要求】
1. **禁止废话**,整体控制在 80 字以内。
1. **禁止废话**,整体控制在 100 字以内。
2. 严格按照以下 HTML 格式输出:
🤖 <b>Groq AI 决策</b>
- 💡 逻辑: [简述动力来源/阻碍因子。例如:暖平流强势推高,且辐射极低时段创新高,极大概率超预报。]
- 🎲 盘口: [一句话指出结算在哪里博弈,以及目前是否到了最热时段。例如:距最热时段还有3小时,目前预计在27或28之间博弈,有突破可能。]
- 💡 逻辑: [不要重复模版例子!请使用一句话提炼机场实测(如风速风向、云量、气温变化趋势)及热力动力因子。例如:实测吹强劲西南风(15kt)伴随云量减少,辐射加热强劲,破预报阻力非常小。]
- 🎯 信心: [1-10]/10
"""