【基于 LangChain 的异步天气查询4】加入语音输入/朗读

 

目录

引言

一、项目功能概览

二、确保依赖已安装

三、文件结构(建议)

四、运行代码

.env

 weather_runnable.py

main.py

运行结果1(文本输入——>语音播报)

五、技术栈要点


引言

本文介绍了一个基于Python的天气查询项目,通过语音或文本输入城市名称,获取并播报该城市的天气信息。项目依赖包括pyttsx3SpeechRecognitionpyaudiopython-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本地语音播报
LangChainLLM 推理流程构建
ChatOpenAI使用 OpenAI GPT-4o 模型
requests访问第三方天气与地理 API
dotenv加载环境变量(API 密钥)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值