Revise fallback highs after observed breaks

Fast evidence mode should not say DEB and models support the center when the latest METAR has already exceeded that center or the model upper edge. The fallback now treats the live observation as a lower bound for the daily high and explains the upward revision pressure.

Constraint: Fallback output must remain useful before the full AI bulletin read returns

Rejected: Keep the original DEB center until AI completes | it can be lower than an already-observed temperature

Confidence: high

Scope-risk: narrow

Tested: pytest tests/test_web_observability.py::test_city_ai_fallback_revises_up_when_latest_metar_breaks_above_models tests/test_web_observability.py::test_city_ai_fallback_reasoning_identifies_fast_evidence_mode tests/test_web_observability.py::test_city_ai_stream_request_only_asks_provider_for_observation_read -q

Tested: npm run build
This commit is contained in:
2569718930@qq.com
2026-04-27 12:33:56 +08:00
parent c3f092fc28
commit d1d9f80f0f
3 changed files with 80 additions and 2 deletions
+41 -2
View File
@@ -685,6 +685,27 @@ def _build_city_ai_fallback(
predicted = current_temp
range_low = min(values) if values else predicted
range_high = max(values) if values else predicted
model_range_high = range_high
current_above_predicted = (
current_temp is not None
and predicted is not None
and current_temp > predicted + 0.2
)
current_above_model_range = (
current_temp is not None
and model_range_high is not None
and current_temp > model_range_high + 0.2
)
observed_high_break = bool(current_above_predicted or current_above_model_range)
original_predicted = predicted
if observed_high_break:
predicted = max(
value
for value in (predicted, current_temp)
if value is not None
)
if range_high is not None and current_temp is not None:
range_high = max(range_high, current_temp)
city = str(ai_input.get("city_display_name") or ai_input.get("city") or "this city")
station = str((airport_current.get("station_code") if is_airport_metar else None) or observation_anchor.get("station_code") or current_obs.get("station_code") or "")
raw_metar = str(airport_current.get("raw_metar") or "").strip() if is_airport_metar else ""
@@ -718,12 +739,18 @@ def _build_city_ai_fallback(
metar_zh = f"当前没有可用的{source_name_zh}正文,暂以 DEB、多模型路径与最新实测为主。"
metar_en = f"No raw {source_name_en} text is available, so DEB, latest observations and the model cluster carry the read."
predicted_text = _format_ai_temperature(predicted, unit) or "--"
current_text = _format_ai_temperature(current_temp, unit) or "--"
original_predicted_text = _format_ai_temperature(original_predicted, unit) or "--"
model_range_high_text = _format_ai_temperature(model_range_high, unit) or "--"
if partial_ai.get("final_judgment_zh") or partial_ai.get("final_judgment_en"):
final_zh = str(partial_ai.get("final_judgment_zh") or partial_ai.get("final_judgment_en") or "").strip()
final_en = str(partial_ai.get("final_judgment_en") or partial_ai.get("final_judgment_zh") or "").strip()
elif partial_ai:
final_zh = f"{city} 预计最高温暂以 {predicted_text} 附近为中枢;AI 已先完成{bulletin_zh}解读,最高温结论结合 DEB、多模型与最新实测校准。"
final_en = f"{city} daily high is centered near {predicted_text}; AI has already read the {bulletin_en}, with the high calibrated against DEB, the model cluster and latest observations."
elif observed_high_break:
final_zh = f"{city} 最新实测已达 {current_text},高于原先 {original_predicted_text} 中枢;最高温中枢需先上修到至少 {predicted_text} 附近。"
final_en = f"{city} latest observation has reached {current_text}, above the prior {original_predicted_text} center; the daily-high center should be revised up to at least near {predicted_text}."
elif timed_out:
final_zh = f"{city} 预计最高温暂以 {predicted_text} 附近为中枢;当前已先用 DEB、多模型和{source_name_zh}快速证据模式判断。"
final_en = f"{city} daily high is centered near {predicted_text}; the current read uses the fast DEB/model/{source_name_en} evidence mode."
@@ -733,15 +760,27 @@ def _build_city_ai_fallback(
reasoning_zh = str(partial_ai.get("reasoning_zh") or "").strip() or (
f"AI {bulletin_zh}解读已用于校准日内节奏;DEB 与多模型集合继续约束最高温中枢,后续{source_name_zh}用于确认是否需要上调或下修。"
if partial_ai
else f"当前为快速证据模式;最新{source_name_zh}已高于原先 {original_predicted_text} 中枢{(',并超过模型上沿 ' + model_range_high_text) if current_above_model_range else ''},本轮最高温判断应优先承认实测突破并等待完整 AI {bulletin_zh}解读合并。"
if observed_high_break
else f"当前为快速证据模式;DEB、多模型集合和最新{source_name_zh}共同支撑本轮最高温中枢,完整 AI {bulletin_zh}解读返回后再合并。"
)
reasoning_en = str(partial_ai.get("reasoning_en") or "").strip() or (
f"The AI {bulletin_en} read is already used to calibrate the intraday pace; DEB and the model cluster still constrain the high-temperature center, while later {source_name_en} updates confirm whether to revise it."
if partial_ai
else f"This is the fast evidence mode; latest {source_name_en} is above the prior {original_predicted_text} center{(' and above the model upper edge ' + model_range_high_text) if current_above_model_range else ''}, so the high-temperature read should first acknowledge the observed break and merge the full AI {bulletin_en} read when available."
if observed_high_break
else f"This is the fast evidence mode; DEB, the model cluster and latest {source_name_en} jointly support the current daily-high center, and the full AI {bulletin_en} read will be merged when available."
)
risks_zh = [f"后续{source_name_zh}若明显偏离模型路径,需及时修正最高温中枢。"]
risks_en = [f"If later {source_name_en} updates diverge from the model path, revise the daily-high center promptly."]
risks_zh = (
[f"最新{source_name_zh}已突破原模型路径,若后续报文继续持平或升温,需要继续上修最高温中枢。"]
if observed_high_break
else [f"后续{source_name_zh}若明显偏离模型路径,需及时修正最高温中枢。"]
)
risks_en = (
[f"Latest {source_name_en} has already broken above the prior model path; if later reports hold steady or warm further, keep revising the daily-high center upward."]
if observed_high_break
else [f"If later {source_name_en} updates diverge from the model path, revise the daily-high center promptly."]
)
return {
"predicted_max": partial_ai.get("predicted_max", predicted),
"range_low": partial_ai.get("range_low", range_low),