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:
2569718930@qq.com
2026-05-10 18:16:10 +08:00
parent bf5e92e656
commit 04b2f0e4a3
2 changed files with 17 additions and 3 deletions
+15 -3
View File
@@ -1728,10 +1728,11 @@ def _analyze(
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 {}
mg_cur = mgm.get("current", {}) if mgm 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)
live_mc = mc if metar_current_is_today else {}
primary_current = sc_cur if use_settlement_current else live_mc
@@ -1742,6 +1743,15 @@ def _analyze(
cur_temp = _sf(primary_current.get("temp"))
if cur_temp is not None and not _is_plausible_city_temp(city, cur_temp, sym):
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:
cur_temp = _sf(live_mc.get("temp"))
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"),
"receipt_time": primary_current.get("receipt_time"),
"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")),
"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,
"clouds_raw": [
{"cover": c.get("cover"), "base": c.get("base")} for c in clouds
],
"visibility_mi": _sf(primary_current.get("visibility_mi")),
"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": {
"temp": _sf(live_mc.get("temp")),
@@ -2472,6 +2483,7 @@ def _analyze(
"mgm": mgm_data,
"mgm_nearby": raw.get("mgm_nearby", []),
"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": {
"today_high": om_today,
"daily": forecast_daily,
+2
View File
@@ -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_dir": current.get("wind_dir"),
"humidity": current.get("humidity"),
"pressure_hpa": current.get("pressure_hpa"),
"observation_source": current.get("settlement_source"),
},
"airport": {
"name": risk.get("airport") or airport_current.get("station_label") or airport_primary.get("station_label"),