用DeepSeek-V2.5和WeatherAPI打造智能天气助手:5步搞定Function Calling实战

当你在清晨醒来,想知道今天是否需要带伞时,一个能理解自然语言并实时查询天气的AI助手无疑是最贴心的伙伴。本文将带你从零开始,用DeepSeek-V2.5和WeatherAPI构建这样一个智能天气助手,重点解决实际开发中的关键问题。

1. 环境准备与API配置

在开始编码前,我们需要准备好开发环境和必要的API密钥。这里假设你已经安装了Python 3.8+版本,并熟悉基本的Python开发。

首先安装必要的依赖库:

pip install requests python-dotenv

接下来,我们需要获取两个关键的API密钥:

  1. DeepSeek API密钥:从DeepSeek开发者平台获取
  2. WeatherAPI密钥:注册WeatherAPI免费账户获取

建议将API密钥存储在环境变量中,避免硬编码在代码里。创建.env文件:

DEEPSEEK_API_KEY=your_deepseek_api_key
WEATHER_API_KEY=your_weather_api_key

2. 定义天气查询函数

核心功能是能够查询指定城市的天气。我们使用WeatherAPI的免费套餐,它提供基本的天气数据查询功能。

import requests
import os
from dotenv import load_dotenv

load_dotenv()  # 加载环境变量

def get_weather(city: str) -> str:
    """
    查询指定城市的当前天气情况
    
    参数:
        city (str): 城市名称,支持中文或英文
    
    返回:
        str: 格式化后的天气信息或错误消息
    """
    base_url = "http://api.weatherapi.com/v1/current.json"
    params = {
        'key': os.getenv('WEATHER_API_KEY'),
        'q': city,
        'aqi': 'no'  # 不查询空气质量数据
    }
    
    try:
        response = requests.get(base_url, params=params, timeout=5)
        response.raise_for_status()  # 检查HTTP错误
        
        data = response.json()
        weather = data['current']['condition']['text']
        temp_c = data['current']['temp_c']
        feelslike_c = data['current']['feelslike_c']
        humidity = data['current']['humidity']
        
        return (f"{city}当前天气:{weather},"
                f"温度:{temp_c}°C(体感{feelslike_c}°C),"
                f"湿度:{humidity}%")
                
    except requests.exceptions.RequestException as e:
        return f"无法获取{city}的天气信息:{str(e)}"

这个函数做了几项重要改进:

  • 增加了完整的类型提示和文档字符串
  • 使用环境变量管理敏感信息
  • 添加了超时处理和异常捕获
  • 返回更丰富的天气信息(体感温度、湿度)

3. 配置Function Calling工具

为了让DeepSeek知道它可以调用我们的天气查询函数,需要按照特定格式定义工具描述:

from typing import List, Dict, Any

def get_tools() -> List[Dict[str, Any]]:
    """返回DeepSeek可用的工具列表"""
    return [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "获取指定城市的当前天气情况,包括温度、体感温度和湿度",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "城市名称,如'北京'或'New York'",
                        }
                    },
                    "required": ["city"],
                },
            }
        }
    ]

关键点说明:

  • name必须与实际的函数名完全一致
  • description要清晰准确,这直接影响模型是否及如何调用该函数
  • parameters定义了函数需要的参数及其类型

4. 实现Function Calling主逻辑

现在我们可以实现核心的对话处理逻辑,处理用户输入并决定是否需要调用函数。

from openai import OpenAI
import json

class WeatherAssistant:
    def __init__(self):
        self.client = OpenAI(
            api_key=os.getenv("DEEPSEEK_API_KEY"),
            base_url="https://api.deepseek.com/v1"
        )
        self.tools = get_tools()
    
    def process_message(self, user_input: str) -> str:
        """处理用户输入并返回响应"""
        messages = [{"role": "user", "content": user_input}]
        
        response = self.client.chat.completions.create(
            model="deepseek-v2.5",
            messages=messages,
            tools=self.tools,
            tool_choice="auto",
            temperature=0.3  # 适当降低随机性
        )
        
        response_message = response.choices[0].message
        
        # 检查是否需要调用函数
        if tool_calls := response_message.tool_calls:
            for tool_call in tool_calls:
                if tool_call.function.name == "get_weather":
                    # 解析参数
                    args = json.loads(tool_call.function.arguments)
                    weather_info = get_weather(args["city"])
                    
                    # 将函数结果添加到对话上下文中
                    messages.append({
                        "role": "tool",
                        "content": weather_info,
                        "tool_call_id": tool_call.id
                    })
            
            # 获取模型对函数结果的总结回复
            second_response = self.client.chat.completions.create(
                model="deepseek-v2.5",
                messages=messages,
            )
            return second_response.choices[0].message.content
        
        return response_message.content

这个实现有几个关键优化:

  • 封装为类,便于维护和扩展
  • 使用tool_choice="auto"让模型自主决定是否调用函数
  • 正确处理多个可能的函数调用
  • 适度的temperature设置平衡创造性和稳定性

5. 异常处理与工程化改进

在实际应用中,我们需要考虑更多边界情况和工程化细节:

5.1 增强的错误处理

def safe_process_message(assistant: WeatherAssistant, user_input: str) -> str:
    """带错误处理的对话处理"""
    try:
        return assistant.process_message(user_input)
    except json.JSONDecodeError:
        return "抱歉,处理天气数据时出现问题,请稍后再试。"
    except Exception as e:
        print(f"Error processing message: {str(e)}")
        return "系统暂时无法处理您的请求,请稍后再试。"

5.2 多城市查询优化

许多用户会同时询问多个城市的天气,我们可以改进函数支持批量查询:

def get_multiple_weather(cities: List[str]) -> str:
    """批量查询多个城市天气"""
    results = []
    for city in cities:
        weather = get_weather(city)
        results.append(f"- {weather}")
    return "\n".join(results)

# 更新工具定义
def get_tools():
    return [
        {
            "type": "function",
            "function": {
                "name": "get_multiple_weather",
                "description": "批量查询多个城市的天气情况",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "cities": {
                            "type": "array",
                            "items": {"type": "string"},
                            "description": "城市名称列表",
                        }
                    },
                    "required": ["cities"],
                },
            }
        }
    ]

5.3 缓存与限流

为避免频繁调用API,可以添加简单的缓存机制:

from functools import lru_cache
import time

@lru_cache(maxsize=100)
def get_weather_cached(city: str) -> str:
    """带缓存的天气查询,10分钟内相同城市查询返回缓存结果"""
    return get_weather(city)

6. 完整示例与测试

让我们看一个完整的交互示例:

assistant = WeatherAssistant()

queries = [
    "今天北京天气怎么样?",
    "比较一下上海和广州的天气",
    "下周纽约的天气会如何?"  # 测试无法回答的情况
]

for query in queries:
    print(f"用户: {query}")
    response = safe_process_message(assistant, query)
    print(f"助手: {response}\n")

预期输出可能类似于:

用户: 今天北京天气怎么样?
助手: 北京当前天气:晴朗,温度:23°C(体感25°C),湿度:45%

用户: 比较一下上海和广州的天气
助手: 以下是上海和广州的天气情况:
- 上海当前天气:多云,温度:28°C(体感30°C),湿度:75%
- 广州当前天气:雷阵雨,温度:31°C(体感35°C),湿度:85%

用户: 下周纽约的天气会如何?
助手: 我目前只能查询当前天气情况,无法提供未来天气预报。

7. 部署与扩展建议

完成开发后,你可以考虑:

  1. 部署为Web服务:使用FastAPI或Flask包装为REST API
  2. 添加更多功能
    • 天气预警通知
    • 历史天气数据查询
    • 多语言支持
  3. 性能优化
    • 异步处理请求
    • 更智能的缓存策略
  4. 用户界面
    • 开发聊天机器人界面
    • 集成到现有通讯工具如微信、Slack

通过这5个关键步骤,我们构建了一个实用的智能天气助手。Function Calling的强大之处在于,你可以用同样的模式集成各种API,为AI助手添加无限可能。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐