目录
引言
本文介绍了一个基于Python的天气查询项目,通过语音或文本输入城市名称,获取并播报该城市的天气信息。项目依赖包括pyttsx3
、SpeechRecognition
、pyaudio
和python-dotenv
等库。文件结构包括.env
配置文件、main.py
主程序文件和weather_runnable.py
天气查询模块。weather_runnable.py
通过GeoNames API获取城市经纬度,再调用OpenWeather API获取天气数据,并利用LangChain构建AI分析管道。main.py
提供语音和文本两种输入方式,并支持语音播报功能。运行示例展示了通过文本输入查询赣州天气并播报结果的过程。
这个项目是一个基于 LangChain 的语音天气查询助手,支持语音/文字输入和语音播报输出,功能结构清晰,主要包括以下几个部分:
一、项目功能概览
1、输入方式选择:
-
语音识别(使用
speech_recognition
) -
手动文字输入(命令行)
2、天气查询流程:
-
使用 GeoNames API 获取输入城市的经纬度
-
使用 OpenWeather API 获取天气数据(支持中文)
3、AI 分析与自然语言输出:
-
将结构化天气信息传入 LangChain 管道
-
使用 GPT-4o 模型对天气信息进行简洁化表达和建议输出
4、输出方式选择:
-
终端文字输出
-
可选的语音播报(使用
pyttsx3
)
二、确保依赖已安装
pip install pyttsx3 SpeechRecognition pyaudio python-dotenv
三、文件结构(建议)
your_project/ ├── .env ├── main.py └── weather_runnable.py
四、运行代码
.env
#替换为自己GeoNames的用户名
GEONAMES_USERNAME=用户名
#替换为自己的OpenWeather API
OPENWEATHER_API_KEY=自己的APIkey
weather_runnable.py
-
API 数据获取逻辑(GeoNames + OpenWeather)
-
LangChain 管道定义(数据 → Prompt → LLM → 文本输出)
#weather_runnable.py
import os
import requests
from dotenv import load_dotenv
from langchain_core.runnables import RunnableLambda
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
load_dotenv()
GEONAMES_USERNAME = os.getenv("GEONAMES_USERNAME")
OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY")
def fetch_weather(city: str) -> str:
# 1. GeoNames 查询经纬度
geonames_url = f"http://api.geonames.org/search?q={city}&maxRows=1&username={GEONAMES_USERNAME}&type=json"
geo_response = requests.get(geonames_url)
print(f"📍 GeoNames 请求: {geonames_url}")
print(f"🌐 返回内容: {geo_response.text[:200]}")
geo_data = geo_response.json()
if "geonames" not in geo_data or len(geo_data["geonames"]) == 0:
return f"❌ 无法找到城市:{city}"
geo = geo_data["geonames"][0]
lat = geo["lat"]
lon = geo["lng"]
city_name = geo["name"]
# 2. 获取天气数据
weather_url = (
f"https://api.openweathermap.org/data/2.5/weather?"
f"lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric&lang=zh_cn"
)
weather_response = requests.get(weather_url)
print(f"🌤️ 天气请求: {weather_url}")
weather_data = weather_response.json()
if "weather" not in weather_data or "main" not in weather_data:
return f"❌ 无法获取 {city_name} 的天气信息"
description = weather_data["weather"][0]["description"]
temp = weather_data["main"]["temp"]
feels_like = weather_data["main"]["feels_like"]
humidity = weather_data["main"]["humidity"]
wind_speed = weather_data["wind"]["speed"]
# 返回结构化天气数据字符串
return (
f"{city_name}当前天气情况如下:\n"
f"- 天气描述:{description}\n"
f"- 温度:{temp}°C,体感:{feels_like}°C\n"
f"- 湿度:{humidity}%\n"
f"- 风速:{wind_speed} 米/秒"
)
# LangChain 管道构建
weather = (
RunnableLambda(fetch_weather)
| ChatPromptTemplate.from_template(
"以下是某城市的天气数据,请用简洁自然的方式告诉用户需注意什么:\n\n{input}"
)
| ChatOpenAI(model="gpt-4o", temperature=0.5)
| StrOutputParser()
)
main.py
-
程序主入口
-
用户交互(语音/文字输入、TTS 播报)
-
调用异步天气查询
weather.ainvoke(...)
import asyncio
import os
import pyttsx3
import speech_recognition as sr
from dotenv import load_dotenv
from weather_runnable import weather
load_dotenv()
# 初始化语音引擎
engine = pyttsx3.init()
engine.setProperty("rate", 160)
# 语音识别输入(增加录音时间控制)
def recognize_speech() -> str:
recognizer = sr.Recognizer()
with sr.Microphone() as source:
print("🎤 请输入城市名称(你有最多 10 秒时间说话)...")
try:
audio = recognizer.listen(source, timeout=5, phrase_time_limit=10)
except sr.WaitTimeoutError:
return "⏰ 未检测到语音输入,请重试。"
try:
text = recognizer.recognize_google(audio, language="zh-CN")
print(f"🗣️ 识别结果:{text}")
return text
except sr.UnknownValueError:
return "无法识别语音,请重试。"
except sr.RequestError:
return "语音识别服务出错。"
# 语音播报输出
def speak(text: str):
engine.say(text)
engine.runAndWait()
async def run():
print("请选择输入方式:")
print("1. 🎙️ 语音输入")
print("2. ⌨️ 文字输入")
input_mode = input("请输入选项(1 或 2):")
print("\n是否启用语音播报?")
print("y → 开启朗读,n → 只打印")
tts_mode = input("请输入 y 或 n:").strip().lower()
speak_output = tts_mode == "y"
if input_mode == "1":
user_input = recognize_speech()
if "无法识别" in user_input or "出错" in user_input or "未检测到" in user_input:
print(user_input)
if speak_output:
speak(user_input)
return
elif input_mode == "2":
user_input = input("请输入城市名称(可中文):")
else:
print("❌ 无效选项。")
if speak_output:
speak("无效选项")
return
result = await weather.ainvoke(user_input)
print(f"\n🧠 AI 分析结果:\n{result}")
if speak_output:
speak(result)
if __name__ == "__main__":
asyncio.run(run())
运行结果
运行方式1(文本输入——>语音播报)
请选择输入方式:
1. 🎙️ 语音输入
2. ⌨️ 文字输入
请输入选项(1 或 2):2是否启用语音播报?
y → 开启朗读,n → 只打印
请输入 y 或 n:y
请输入城市名称(可中文):赣州
📍 GeoNames 请求: http://api.geonames.org/search?q=赣州&maxRows=1&username=shipking&type=json
🌐 返回内容: {"totalResultsCount":8021,"geonames":[{"adminCode1":"03","lng":"114.9326","geonameId":1810638,"toponymName":"Ganzhou","countryId":"1814991","fcl":"P","population":1977253,"countryCode":"CN","name":"Ga
🌤️ 天气请求: https://api.openweathermap.org/data/2.5/weather?lat=25.84664&lon=114.9326&appid=1fed2dbcd0 c15ac60e3121fe07ae0aa4&units=metric&lang=zh_cn🧠 AI 分析结果:
当前赣州天气为小雨,气温18.16°C,体感温度稍凉,为17.49°C。湿度为56%,风速4.7米/秒。建议外出时携带雨具,适当添加衣物以防着凉。
运行方式2(语音输入——>打印)
请选择输入方式:
1. 🎙️ 语音输入
2. ⌨️ 文字输入
请输入选项(1 或 2):1是否启用语音播报?
y → 开启朗读,n → 只打印
请输入 y 或 n:n
🎤 请输入城市名称(你有最多 10 秒时间说话)...
🗣️ 识别结果:北京
📍 GeoNames 请求: http://api.geonames.org/search?q=北京&maxRows=1&username=shipking&type=json
🌐 返回内容: {"totalResultsCount":4861,"geonames":[{"adminCode1":"22","lng":"116.39723","geonameId":1816670,"toponymName":"Beijing","countryId":"1814991","fcl":"P","population":18960744,"countryCode":"CN","name":"
🌤️ 天气请求: https://api.openweathermap.org/data/2.5/weather?lat=39.9075&lon=116.39723&appid=1fed2dbcd0 c15ac60e3121fe07ae0aa4&units=metric&lang=zh_cn🧠 AI 分析结果:
北京当前天气晴朗,气温约10°C,体感舒适,湿度较低,风力微弱。建议注意保湿,早晚温差可能较大,适当添衣保暖。
运行方式3(语音输入——>语音播报)
请选择输入方式:
1. 🎙️ 语音输入
2. ⌨️ 文字输入
请输入选项(1 或 2):1是否启用语音播报?
y → 开启朗读,n → 只打印
请输入 y 或 n:y
🎤 请输入城市名称(你有最多 10 秒时间说话)...
🗣️ 识别结果:北京
📍 GeoNames 请求: http://api.geonames.org/search?q=北京&maxRows=1&username=shipking&type=json
🌐 返回内容: {"totalResultsCount":4861,"geonames":[{"adminCode1":"22","lng":"116.39723","geonameId":1816670,"toponymName":"Beijing","countryId":"1814991","fcl":"P","population":18960744,"countryCode":"CN","name":"
🌤️ 天气请求: https://api.openweathermap.org/data/2.5/weather?lat=39.9075&lon=116.39723&appid=1fed2dbcd0 c15ac60e3121fe07ae0aa4&units=metric&lang=zh_cn🧠 AI 分析结果:
北京当前天气晴朗,气温接近10°C,湿度较低,体感舒适。风速较小,外出时注意早晚温差,适当增添衣物以防着凉。
运行方式4(文字输入——打印)
请选择输入方式:
1. 🎙️ 语音输入
2. ⌨️ 文字输入
请输入选项(1 或 2):2是否启用语音播报?
y → 开启朗读,n → 只打印
请输入 y 或 n:n
请输入城市名称(可中文):洛杉矶
📍 GeoNames 请求: http://api.geonames.org/search?q=洛杉矶&maxRows=1&username=shipking&type=json
🌐 返回内容: {"totalResultsCount":1,"geonames":[{"adminCode1":"CA","lng":"-118.24368","geonameId":5368361,"toponymName":"Los Angeles","countryId":"6252001","fcl":"P","population":3898747,"countryCode":"US","name":
🌤️ 天气请求: https://api.openweathermap.org/data/2.5/weather?lat=34.05223&lon=-118.24368&appid=1fed2dbc d0c15ac60e3121fe07ae0aa4&units=metric&lang=zh_cn🧠 AI 分析结果:
当前洛杉矶天气晴朗,气温较高,体感温度接近实际温度。湿度为50%,比较舒适。风速较低,几乎无风。建议外出时注意防晒,保持水分充足。
五、技术栈要点
模块 | 功能 |
---|---|
speech_recognition | 语音识别(中文) |
pyttsx3 | 本地语音播报 |
LangChain | LLM 推理流程构建 |
ChatOpenAI | 使用 OpenAI GPT-4o 模型 |
requests | 访问第三方天气与地理 API |
dotenv | 加载环境变量(API 密钥) |