AMOS 数据接入分析层和 AI 预测判定
问题:AMOS 数据在采集层取出后从未被 _analyze() 读取,AI 无法看到跑道级数据 修复: - analysis_service.py:_analyze() 新增 AMOS 温度读取优先级 观测来源顺序:结算源 > AMOS跑道传感器 > METAR > MGM > NMC - AMOS 数据自动覆盖 current.wind_speed_kt、current.pressure_hpa、current.raw_metar - 输出新增 "amos" 字段携带完整跑道数据(temp_source、runway_temps) - scan_terminal_service.py:AI prompt 的 current 区块新增 pressure_hpa 和 observation_source - AI 现在知晓数据来自 AMOS 跑道传感器(observation_source="amos"),可据此判断数据权威性 数据流: AMOS fetch -> raw["amos"] -> _analyze() -> current.observation_source="amos" -> result["amos"] -> _build_city_ai_prompt -> city_snapshot.current -> AI reads runway sensor temp/wind/pressure as authoritative observation Tested: python -m ruff check ., npx tsc --noEmit
This commit is contained in:
+15
-3
@@ -1728,10 +1728,11 @@ def _analyze(
|
|||||||
utc_offset=int(utc_offset or 0),
|
utc_offset=int(utc_offset or 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 2. Current conditions (city-specific settlement source first, then METAR/MGM fallback) ──
|
# ── 2. Current conditions (settlement > AMOS runway sensors > METAR > MGM > NMC fallback) ──
|
||||||
mc = metar.get("current", {}) if metar else {}
|
mc = metar.get("current", {}) if metar else {}
|
||||||
mg_cur = mgm.get("current", {}) if mgm else {}
|
mg_cur = mgm.get("current", {}) if mgm else {}
|
||||||
sc_cur = settlement_current.get("current", {}) if settlement_current else {}
|
sc_cur = settlement_current.get("current", {}) if settlement_current else {}
|
||||||
|
amos_data = raw.get("amos") or {}
|
||||||
use_settlement_current = settlement_source in {"hko", "cwa", "noaa", "wunderground"} and bool(sc_cur)
|
use_settlement_current = settlement_source in {"hko", "cwa", "noaa", "wunderground"} and bool(sc_cur)
|
||||||
live_mc = mc if metar_current_is_today else {}
|
live_mc = mc if metar_current_is_today else {}
|
||||||
primary_current = sc_cur if use_settlement_current else live_mc
|
primary_current = sc_cur if use_settlement_current else live_mc
|
||||||
@@ -1742,6 +1743,15 @@ def _analyze(
|
|||||||
cur_temp = _sf(primary_current.get("temp"))
|
cur_temp = _sf(primary_current.get("temp"))
|
||||||
if cur_temp is not None and not _is_plausible_city_temp(city, cur_temp, sym):
|
if cur_temp is not None and not _is_plausible_city_temp(city, cur_temp, sym):
|
||||||
cur_temp = None
|
cur_temp = None
|
||||||
|
# AMOS runway sensor: authoritative for Korean airports (RKSI/RKPK)
|
||||||
|
if cur_temp is None:
|
||||||
|
amos_temp = _sf(amos_data.get("temp_c"))
|
||||||
|
if amos_temp is not None and _is_plausible_city_temp(city, amos_temp, sym):
|
||||||
|
cur_temp = amos_temp
|
||||||
|
current_source = "amos"
|
||||||
|
current_source_label = amos_data.get("source_label") or "AMOS"
|
||||||
|
current_station_code = amos_data.get("icao")
|
||||||
|
current_station_name = amos_data.get("station_label")
|
||||||
if cur_temp is None:
|
if cur_temp is None:
|
||||||
cur_temp = _sf(live_mc.get("temp"))
|
cur_temp = _sf(live_mc.get("temp"))
|
||||||
if cur_temp is not None and not _is_plausible_city_temp(city, cur_temp, sym):
|
if cur_temp is not None and not _is_plausible_city_temp(city, cur_temp, sym):
|
||||||
@@ -2427,16 +2437,17 @@ def _analyze(
|
|||||||
"report_time": primary_current.get("report_time"),
|
"report_time": primary_current.get("report_time"),
|
||||||
"receipt_time": primary_current.get("receipt_time"),
|
"receipt_time": primary_current.get("receipt_time"),
|
||||||
"obs_time_epoch": primary_current.get("obs_time_epoch"),
|
"obs_time_epoch": primary_current.get("obs_time_epoch"),
|
||||||
"wind_speed_kt": _sf(primary_current.get("wind_speed_kt")),
|
"wind_speed_kt": _sf(amos_data.get("wind_kt")) if current_source == "amos" else _sf(primary_current.get("wind_speed_kt")),
|
||||||
"wind_dir": _sf(primary_current.get("wind_dir")),
|
"wind_dir": _sf(primary_current.get("wind_dir")),
|
||||||
"humidity": _sf(primary_current.get("humidity")),
|
"humidity": _sf(primary_current.get("humidity")),
|
||||||
|
"pressure_hpa": _sf(amos_data.get("pressure_hpa")) if current_source == "amos" else _sf(primary_current.get("pressure_hpa")),
|
||||||
"cloud_desc": cloud_desc,
|
"cloud_desc": cloud_desc,
|
||||||
"clouds_raw": [
|
"clouds_raw": [
|
||||||
{"cover": c.get("cover"), "base": c.get("base")} for c in clouds
|
{"cover": c.get("cover"), "base": c.get("base")} for c in clouds
|
||||||
],
|
],
|
||||||
"visibility_mi": _sf(primary_current.get("visibility_mi")),
|
"visibility_mi": _sf(primary_current.get("visibility_mi")),
|
||||||
"wx_desc": primary_current.get("wx_desc"),
|
"wx_desc": primary_current.get("wx_desc"),
|
||||||
"raw_metar": primary_current.get("raw_metar"),
|
"raw_metar": amos_data.get("raw_metar") if current_source == "amos" else primary_current.get("raw_metar"),
|
||||||
},
|
},
|
||||||
"airport_current": {
|
"airport_current": {
|
||||||
"temp": _sf(live_mc.get("temp")),
|
"temp": _sf(live_mc.get("temp")),
|
||||||
@@ -2472,6 +2483,7 @@ def _analyze(
|
|||||||
"mgm": mgm_data,
|
"mgm": mgm_data,
|
||||||
"mgm_nearby": raw.get("mgm_nearby", []),
|
"mgm_nearby": raw.get("mgm_nearby", []),
|
||||||
"nearby_source": raw.get("nearby_source") or ("mgm" if city.lower() in TURKISH_MGM_CITIES else "metar_cluster"),
|
"nearby_source": raw.get("nearby_source") or ("mgm" if city.lower() in TURKISH_MGM_CITIES else "metar_cluster"),
|
||||||
|
"amos": amos_data if amos_data.get("temp_c") is not None else None,
|
||||||
"forecast": {
|
"forecast": {
|
||||||
"today_high": om_today,
|
"today_high": om_today,
|
||||||
"daily": forecast_daily,
|
"daily": forecast_daily,
|
||||||
|
|||||||
@@ -351,6 +351,8 @@ def _build_city_ai_prompt(data: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
"wind_speed_kt": current.get("wind_speed_kt"),
|
"wind_speed_kt": current.get("wind_speed_kt"),
|
||||||
"wind_dir": current.get("wind_dir"),
|
"wind_dir": current.get("wind_dir"),
|
||||||
"humidity": current.get("humidity"),
|
"humidity": current.get("humidity"),
|
||||||
|
"pressure_hpa": current.get("pressure_hpa"),
|
||||||
|
"observation_source": current.get("settlement_source"),
|
||||||
},
|
},
|
||||||
"airport": {
|
"airport": {
|
||||||
"name": risk.get("airport") or airport_current.get("station_label") or airport_primary.get("station_label"),
|
"name": risk.get("airport") or airport_current.get("station_label") or airport_primary.get("station_label"),
|
||||||
|
|||||||
Reference in New Issue
Block a user