ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

大模型天气预测实战:从数据接入到预报生成的完整链路

大模型天气预测实战:从数据接入到预报生成的完整链路 1. 天气预测这件事大模型到底能插手到什么程度先把话说在前头大模型不是用来替代传统数值天气预报的。如果你指望把一个LLM接上气象数据就能算出明天下午三点会不会下雨那大概率会失望。但如果你想让大模型在天气预测这条链路里承担“理解、解释、辅助决策”的角色那它的价值就非常实在了。我自己从2023年开始折腾大模型和气象数据的结合踩过的坑不算少。最开始的想法很朴素把历史天气数据喂给模型让它预测未来温度。结果发现这完全是南辕北辙——大模型擅长的是语言模式识别和推理不是数值计算。你让它做回归预测它给你的答案可能还不如一个线性回归模型靠谱。那大模型在天气预测领域到底能做什么我总结下来主要是四个方向气象数据的自然语言查询与解读把复杂的数值预报产品翻译成人话让非气象专业的人也能看懂多源气象信息的融合分析把卫星云图描述、地面观测报告、雷达回波文字等多模态信息整合成统一的分析结论预报文案的自动生成根据数值预报的输出自动撰写天气预报文本这在气象服务行业是刚需极端天气事件的预警辅助结合历史案例和实时数据辅助判断天气事件的严重程度和影响范围注意大模型做天气预测核心定位是“辅助工具”和“翻译层”不是“计算引擎”。这个定位如果搞错了后面所有工作都是白费力气。适合读这篇内容的人有一定编程基础、对气象数据感兴趣、想用大模型做点实际东西的开发者。如果你是大模型零基础建议先补一下Python和API调用的基本知识不然实操部分会比较吃力。2. 整体方案设计为什么我不建议你从零训练气象大模型2.1 技术路线选型的核心逻辑一上来就想着训练一个气象专用大模型这是很多人容易犯的错误。我见过不少团队花了几百万买GPU收集了几十年的气象数据最后训出来的模型效果还不如直接调用现成API加上精心设计的提示词。为什么因为气象领域的训练数据虽然量大但标注质量参差不齐而且大模型的气象知识需要和实时数据结合才有意义。你训练的时候用的是历史数据但天气是实时变化的模型学到的只是统计规律不是物理规律。我的建议是走“通用大模型 气象数据接口 领域提示词工程”的路线。具体来说方案成本效果适用场景从零训练气象大模型极高百万级不确定大型气象机构微调通用大模型中等万级较好有特定需求的企业通用大模型提示词工程低百元级够用个人开发者、小团队通用大模型RAG检索增强较低千元级好需要历史数据支撑的场景我实测下来对于大多数应用场景“通用大模型 气象API RAG”的组合已经能覆盖80%的需求。你不需要自己训模型只需要把气象数据接口调通把历史天气案例做成向量库然后用提示词把大模型的推理能力引导到正确的方向上。2.2 数据源的选取与处理天气数据的来源比你想象的多。公开渠道能拿到的包括地面观测数据各地气象站的气温、湿度、风速、气压等通常每小时更新数值预报产品GFS、ECMWF等全球预报模型的输出分辨率从0.25度到1度不等卫星云图数据红外、可见光波段的云图可以用来判断云系发展雷达回波数据降水强度的直接观测对短时预报特别重要历史天气数据过去几十年的逐日天气记录用来做案例检索和模式匹配我一般用Python的requests库直接调公开的气象数据API拿到JSON格式的数据后做清洗和结构化。这里有个关键点大模型对结构化数据的理解能力有限你需要把数据转成自然语言描述再喂给它。比如# 原始数据 weather_data { temp: 28.5, humidity: 75, wind_speed: 3.2, pressure: 1008, cloud_cover: 80 } # 转成自然语言 weather_text f当前气温28.5摄氏度相对湿度75%风速3.2米每秒气压1008百帕云量80%。这个转换过程看起来简单但实际做的时候要注意单位统一、数值精度、异常值处理。我踩过的坑是有些API返回的温度是华氏度有些是摄氏度如果不做统一大模型给出的分析会完全离谱。2.3 大模型选型的实操考量选哪个大模型这个问题没有标准答案取决于你的预算、部署条件和精度要求。我列一下我实际用过的几个方案云端API方案调用方便按token计费适合快速验证。缺点是数据要传到云端对数据隐私有要求的场景不太合适本地部署方案用Ollama或者vLLM在本地跑开源模型数据不出本地适合对隐私敏感的场景。缺点是需要一定的硬件投入混合方案敏感数据本地处理通用分析走云端API如果你刚开始尝试我建议先用云端API跑通流程确认效果后再考虑本地部署。本地部署的话7B到14B参数的模型在消费级显卡上就能跑量化后显存占用可以压到8GB以内。实操心得不要一上来就追求最大的模型。我试过用70B的模型做天气文案生成效果确实好但推理速度慢到无法接受。后来换成7B的量化模型配合好的提示词效果差距其实没有想象中那么大。3. 核心实操从数据接入到预报生成的完整链路3.1 环境准备与依赖安装先把基础环境搭起来。我假设你用的是Windows或者LinuxPython版本3.9以上。# 创建虚拟环境 python -m venv weather-llm source weather-llm/bin/activate # Linux/Mac # weather-llm\Scripts\activate # Windows # 安装核心依赖 pip install requests openai pandas numpy chromadb sentence-transformers如果你打算本地部署模型额外安装Ollama# Linux安装Ollama curl -fsSL https://ollama.com/install.sh | sh # 拉取模型以Qwen2.5-7B为例 ollama pull qwen2.5:7b这里解释一下为什么选这些依赖requests用来调气象APIopenai库虽然名字叫openai但实际上可以兼容很多云端API接口chromadb用来做向量检索sentence-transformers用来生成文本嵌入。3.2 气象数据接入与预处理我以Open-Meteo的免费API为例这个接口不需要API Key适合快速验证。import requests import pandas as pd from datetime import datetime, timedelta def fetch_weather_data(lat, lon, days7): 获取指定经纬度的未来天气数据 url https://api.open-meteo.com/v1/forecast params { latitude: lat, longitude: lon, hourly: temperature_2m,relative_humidity_2m,wind_speed_10m,pressure_msl,cloud_cover,precipitation, forecast_days: days, timezone: Asia/Shanghai } response requests.get(url, paramsparams) data response.json() # 转成DataFrame方便处理 hourly data[hourly] df pd.DataFrame({ time: hourly[time], temp: hourly[temperature_2m], humidity: hourly[relative_humidity_2m], wind: hourly[wind_speed_10m], pressure: hourly[pressure_msl], cloud: hourly[cloud_cover], precip: hourly[precipitation] }) df[time] pd.to_datetime(df[time]) return df # 示例获取北京未来7天数据 df fetch_weather_data(39.9042, 116.4074) print(df.head(24))拿到数据后关键的一步是把数值转成自然语言描述。我写了一个转换函数把逐小时数据压缩成一段人类可读的天气摘要def weather_to_text(df, hours24): 把天气数据转成自然语言描述 subset df.head(hours) # 计算统计量 temp_min subset[temp].min() temp_max subset[temp].max() temp_avg subset[temp].mean() humidity_avg subset[humidity].mean() wind_avg subset[wind].mean() total_precip subset[precip].sum() cloud_avg subset[cloud].mean() # 判断天气状况 if total_precip 10: condition 有明显降水 elif total_precip 1: condition 有零星降水 elif cloud_avg 70: condition 多云 elif cloud_avg 30: condition 晴间多云 else: condition 晴朗 text f未来{hours}小时天气概况 气温范围{temp_min:.1f}至{temp_max:.1f}摄氏度平均{temp_avg:.1f}摄氏度。 相对湿度平均{humidity_avg:.0f}%。 风速平均{wind_avg:.1f}米每秒。 云量平均{cloud_avg:.0f}%。 降水量累计{total_precip:.1f}毫米。 总体天气状况{condition}。 return text weather_desc weather_to_text(df) print(weather_desc)这个转换过程看起来简单但有几个细节要注意温度保留一位小数就够了湿度取整风速保留一位小数。精度太高反而会让大模型困惑因为它不擅长处理过于精确的数值。3.3 提示词工程让大模型说“气象话”提示词的设计是整个方案的核心。我试过很多版本最后稳定下来的结构是这样的SYSTEM_PROMPT 你是一位资深气象分析师擅长将数值预报数据转化为通俗易懂的天气预报。 你的任务是基于提供的天气数据生成一份专业的天气预报文本。 要求 1. 使用气象行业的标准术语但要让普通人能看懂 2. 重点关注温度变化趋势、降水概率和时段、风力变化 3. 如果有极端天气迹象要明确提示 4. 不要编造数据中没有的信息 5. 预报文本控制在200字以内 USER_PROMPT_TEMPLATE 请根据以下天气数据生成预报文本 {weather_text} 请生成一份适合发布在天气服务平台的预报文本。这个提示词的关键在于“不要编造数据中没有的信息”这一条。我早期版本没有加这句话结果大模型经常自己脑补出“明天下午有雷阵雨”这种数据里根本没有的内容。加了约束之后幻觉问题明显减少。3.4 调用大模型生成预报如果你用云端API代码大概长这样from openai import OpenAI client OpenAI( api_keyyour-api-key, base_urlhttps://api.your-provider.com/v1 ) def generate_forecast(weather_text): response client.chat.completions.create( modelyour-model-name, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: USER_PROMPT_TEMPLATE.format(weather_textweather_text)} ], temperature0.3, max_tokens500 ) return response.choices[0].message.content forecast generate_forecast(weather_desc) print(forecast)注意temperature参数设成了0.3这是为了降低输出的随机性。天气预报需要的是稳定、可重复的输出不是创意写作。我试过用0.7的温度同样的数据每次生成的预报文本都不一样这在业务场景里是不可接受的。如果你用本地Ollama部署代码稍微改一下import requests def generate_forecast_local(weather_text): response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: USER_PROMPT_TEMPLATE.format(weather_textweather_text)} ], stream: False, options: {temperature: 0.3} } ) return response.json()[message][content]3.5 RAG增强让大模型参考历史相似天气单纯靠实时数据大模型对天气趋势的判断能力有限。我加了一个RAG模块把历史天气案例做成向量库每次生成预报前先检索最相似的历史案例作为参考。import chromadb from sentence_transformers import SentenceTransformer # 初始化向量库 client chromadb.Client() collection client.create_collection(weather_cases) # 嵌入模型 encoder SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) def add_weather_case(case_text, metadata): 添加历史天气案例到向量库 embedding encoder.encode(case_text).tolist() collection.add( embeddings[embedding], documents[case_text], metadatas[metadata], ids[metadata[id]] ) def retrieve_similar_cases(query_text, n_results3): 检索相似的历史天气案例 query_embedding encoder.encode(query_text).tolist() results collection.query( query_embeddings[query_embedding], n_resultsn_results ) return results[documents][0] # 使用示例 similar_cases retrieve_similar_cases(weather_desc) rag_context \n\n.join(similar_cases) # 把检索结果加入提示词 enhanced_prompt f参考以下历史相似天气案例 {rag_context} 现在请根据当前数据生成预报 {weather_desc}这个RAG模块的效果在极端天气预测上特别明显。比如当前数据看起来像要下暴雨但大模型不确定检索到历史上类似气象条件下确实出现了暴雨它就会在预报中更明确地提示降水风险。4. 实操中踩过的坑与排查技巧4.1 大模型“胡说八道”的几种典型表现幻觉问题是绕不过去的。我总结了几种最常见的表现和对应的解决方法问题表现根本原因解决方法编造不存在的降水时段模型倾向于“补全”缺失信息提示词中明确禁止编造加入“仅基于提供数据”约束温度数值与输入不符模型对数字的复制能力弱在提示词中重复关键数值或改用结构化输出预报语气过于绝对模型缺乏不确定性表达训练提示词中要求使用“可能”“预计”“概率”等词汇忽略极端值模型倾向于输出平均值在数据预处理阶段单独标注极值提示词中强调关注极值我印象最深的一次是输入数据显示未来24小时降水量为0但大模型生成的预报里写了“午后有短时阵雨”。查了半天才发现是因为提示词里有一句“请生成完整的天气预报”模型觉得没有降水就不完整自己加了一个。避坑技巧在提示词里加一句“如果某项数据为零或无明显变化可以简要提及或省略不要为了完整性而编造”。4.2 数据接口的稳定性问题公开气象API最大的问题是稳定性。我遇到过接口超时、返回格式变更、数据延迟等各种情况。解决方案是加一层缓存和降级机制import json import os from datetime import datetime, timedelta CACHE_DIR weather_cache os.makedirs(CACHE_DIR, exist_okTrue) def get_weather_with_cache(lat, lon): 带缓存的天气数据获取 cache_key f{lat}_{lon}_{datetime.now().strftime(%Y%m%d%H)} cache_file os.path.join(CACHE_DIR, f{cache_key}.json) # 检查缓存 if os.path.exists(cache_file): with open(cache_file, r) as f: return json.load(f) # 缓存不存在调API try: data fetch_weather_data(lat, lon) with open(cache_file, w) as f: json.dump(data.to_dict(), f) return data except Exception as e: # API失败尝试用最近的缓存 print(fAPI调用失败{e}尝试使用历史缓存) cache_files sorted([f for f in os.listdir(CACHE_DIR) if f.startswith(f{lat}_{lon})]) if cache_files: with open(os.path.join(CACHE_DIR, cache_files[-1]), r) as f: return pd.DataFrame(json.load(f)) raise这个缓存机制看起来简单但在实际运行中救了我很多次。特别是做定时预报生成的时候API偶尔抽风不会导致整个流程崩溃。4.3 本地部署模型的性能调优如果你用Ollama本地跑模型有几个参数直接影响推理速度和效果# 查看当前运行的模型 ollama ps # 调整上下文长度默认可能不够 ollama run qwen2.5:7b --context-length 8192 # 设置GPU层数根据显存调整 OLLAMA_GPU_LAYERS35 ollama serve我实测下来7B模型在RTX 306012GB显存上量化到Q4_K_M级别推理速度大概在每秒20-30个token。生成一份200字的预报文本大概需要5-8秒这个速度对于非实时场景完全够用。如果显存不够可以试试更小的模型。Qwen2.5-3B或者Phi-3-mini在天气文案生成这种任务上表现也还可以速度能快一倍以上。4.4 预报质量的评估方法怎么判断大模型生成的预报靠不靠谱我用了几个简单的评估指标数值一致性预报文本中提到的温度、湿度等数值是否与输入数据一致逻辑自洽性预报内容是否前后矛盾比如同时说“晴朗”和“有降水”术语准确性气象术语使用是否正确比如“阵雨”和“持续性降水”不能混用可读性非气象专业的人能否看懂我写了一个简单的自动检查脚本def validate_forecast(forecast_text, weather_data): 简单校验预报文本的数值一致性 issues [] # 检查温度范围 temp_min weather_data[temp].min() temp_max weather_data[temp].max() import re temps re.findall(r(\d\.?\d*)摄氏度, forecast_text) for t in temps: t_val float(t) if t_val temp_min - 5 or t_val temp_max 5: issues.append(f温度值{t_val}超出合理范围[{temp_min:.1f}, {temp_max:.1f}]) # 检查降水描述 total_precip weather_data[precip].sum() if total_precip 0.1 and 降水 in forecast_text and 无 not in forecast_text: issues.append(数据中无降水但预报提到了降水) return issues这个校验脚本帮我发现了不少问题特别是数值不一致的情况。虽然不能覆盖所有错误类型但能拦住大部分低级错误。5. 进阶玩法多模态与智能体在天气预测中的应用5.1 卫星云图的多模态理解大模型的多模态能力在天气预测里有一个很实际的应用直接看卫星云图。传统的云图分析需要气象专家人工判读但多模态大模型可以自动识别云系类型、估计云顶温度、判断对流发展强度。我试过用支持视觉输入的模型来分析红外云图提示词大概是这样的VISION_PROMPT 这是一张红外卫星云图。请分析 1. 主要云系的位置和范围 2. 云顶亮温的分布特征亮温越低通常代表云顶越高、对流越强 3. 是否存在明显的对流云团发展 4. 云系的移动趋势判断 请用气象专业术语描述但保持通俗易懂。实测下来多模态模型对云图的基本描述是准确的但在定量分析上还有差距。比如它能看出“西北方向有对流云团发展”但很难准确估计云顶高度。所以我的用法是多模态模型做初步筛选和描述具体的定量分析还是交给传统算法。5.2 智能体架构让大模型自主调度气象工具更进阶的玩法是把大模型做成一个智能体让它自己决定什么时候调什么数据、做什么分析。我用的是一个简单的ReAct框架TOOLS [ { name: get_current_weather, description: 获取指定城市的实时天气数据, parameters: {city: 城市名称} }, { name: get_forecast, description: 获取指定城市未来几天的预报数据, parameters: {city: 城市名称, days: 天数} }, { name: search_historical, description: 检索历史相似天气案例, parameters: {query: 查询描述} } ] AGENT_PROMPT 你是一个天气分析智能体。用户会问你天气相关的问题。 你可以调用以下工具来获取数据 {tools} 请根据用户的问题决定是否需要调用工具以及调用哪个工具。 如果需要多个工具可以依次调用。这个智能体架构的好处是灵活。用户问“明天适合晾衣服吗”智能体会自动调取湿度、风速、降水概率数据然后综合判断给出建议。不需要为每个问题单独写逻辑。5.3 预报文案的个性化定制同一个天气数据给不同的人看需要不同的表达方式。给农民看要强调降水和温度对农作物的影响给上班族看要强调通勤时段的天气给户外工作者看要强调极端天气风险。我通过提示词模板来实现个性化PERSONA_TEMPLATES { farmer: 你是一位农业气象服务专家重点关注降水、温度和湿度对农作物的影响。, commuter: 你是一位城市气象服务专员重点关注早晚高峰时段的天气对通勤的影响。, outdoor: 你是一位户外运动气象顾问重点关注极端天气风险和体感温度。, general: 你是一位大众气象服务分析师用通俗易懂的语言播报天气。 } def generate_personalized_forecast(weather_text, personageneral): system_prompt PERSONA_TEMPLATES.get(persona, PERSONA_TEMPLATES[general]) # ... 调用大模型这个功能在实际使用中反馈很好。同一个数据源不同用户看到的预报文本完全不同但核心信息一致。6. 几个容易被忽略的细节问题6.1 时区处理气象数据通常用UTC时间但用户看预报用的是本地时间。如果不做时区转换预报里的“明天下午”可能对应的是完全错误的时间段。我一般在数据预处理阶段就统一转成目标时区避免后续混乱。6.2 单位换算温度有摄氏度和华氏度风速有米每秒和公里每小时气压有百帕和毫米汞柱。大模型对单位不敏感如果你输入的是华氏度但提示词里写的是摄氏度它不会主动纠正。我的做法是在数据预处理阶段统一单位并在提示词中明确标注单位。6.3 模型更新与版本管理云端API的模型版本会更新本地部署的模型也可能需要升级。每次模型变更后之前调好的提示词可能需要重新调整。我建议在代码里记录模型版本号方便回溯问题。MODEL_VERSION qwen2.5:7b-q4_K_M # 在生成的预报文本中附带版本信息方便排查6.4 成本控制如果用云端APItoken消耗是需要关注的。一份完整的天气数据转文本大概500-800字加上提示词和输出每次调用大概消耗1500-2000个token。如果每天生成100个城市的预报一个月下来token消耗量不小。我的做法是对实时性要求不高的场景用本地模型对质量要求高的场景用云端API。7. 我个人的一些实际体会这套方案我跑了大概半年多从最初的玩具项目慢慢迭代成了一个小工具。最大的感受是大模型在天气预测领域的价值不在于“预测”本身而在于“翻译”和“连接”。它把冷冰冰的数值数据翻译成人能理解的语言把分散的数据源连接成统一的分析结论。如果你也想尝试我的建议是从最简单的场景开始拿一个城市的天气数据写一个提示词让大模型生成一段预报文本。跑通之后再逐步加入RAG、多模态、智能体这些进阶功能。不要一上来就追求大而全那样很容易在细节里迷失方向。另外不要迷信大模型的输出。它生成的预报文本一定要经过校验特别是数值部分。我现在的流程里自动校验是必须的一步校验不通过的预报会打回重新生成或者人工审核。这个环节看起来麻烦但能避免很多尴尬的错误。最后分享一个提示词的小技巧在系统提示词里加一句“你是一个谨慎的气象分析师对于不确定的预报会明确说明不确定性”。这句话能显著降低模型给出过于绝对判断的概率让预报文本更符合气象服务的专业规范。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进