08-day2-实验-调用真实的网络资源-天气 API

经过本地函数,我们掌握了工具调用的核心机制。
现在,我们把 get_weather本地模拟字典升级为调用真实的免费天气 API

我们将使用 wttr.in —— 一个免费、无需 API Key、支持中文城市地理编码的天气服务。


✅ 改动点(与本地函数不同之处)

功能 原版 新版
get_weather 数据源 本地字典(仅3个城市) 真实在线 API(全球城市)
是否需要网络 ❌ 否 ✅ 是
是否需要额外依赖 ❌ 否 ✅ 需要 requests
错误处理 ✅ 增加网络/解析异常处理
城市匹配 精确字符串匹配 ✅ 通过地理编码(Geocoding)模糊匹配

📦 所需安装

pip install requests

✨ 升级后的完整代码

tee day2_qwen_tool_calling_true_api.py  <<'EOF'
# day2_qwen_tool_calling_true_api.py 
import os
import json
import requests  # ✅ 新增:用于发送 HTTP 请求
from dotenv import load_dotenv
import dashscope
from dashscope import Generation
from datetime import datetime

# 加载 API Key
load_dotenv()
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# ===== 定义工具函数 =====
def get_current_time():
    """获取本机系统当前时间(本地函数,无需网络)"""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def get_weather(city: str):
    """
    使用 wttr.in 获取天气(国内通常可访问,无需 API Key)
    示例: https://wttr.in/成都?format=4
    返回: "成都: 🌦️ +7°C"
    """
    try:
        # 构造 URL(注意:wttr.in 对中文支持良好)
        url = f"https://wttr.in/{city}?format=4&lang=zh"
        headers = {"User-Agent": "curl"}  # 避免被识别为爬虫
        
        response = requests.get(url, headers=headers, timeout=8)
        response.encoding = 'utf-8'  # 确保中文不乱码
        
        if response.status_code == 200:
            weather_str = response.text.strip()
            # 如果返回的是 "Unknown location",说明城市没找到
            if "Unknown location" in weather_str or not weather_str:
                return f"未找到城市 '{city}' 的天气信息"
            return weather_str
        else:
            return f"天气服务返回错误状态码: {response.status_code}"
            
    except requests.exceptions.Timeout:
        return "天气服务请求超时(8秒)"
    except requests.exceptions.ConnectionError:
        return "无法连接到天气服务(可能是网络限制)"
    except Exception as e:
        return f"获取天气时出错: {str(e)}"

# ===== 工具描述(保持不变)=====
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "获取当前日期和时间",
            "parameters": {
                "type": "object",
                "properties": {},
                "required": []
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的实时天气(支持全球城市)",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如'北京'、'New York'"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

# ===== 用户问题 =====
user_input = "现在几点?成都天气如何?"

# ===== 第一次调用(保持不变)=====
response = Generation.call(
    model="qwen-max",
    messages=[{"role": "user", "content": user_input}],
    tools=tools,
    tool_choice="auto"
)

message = response.output.choices[0].message

if "tool_calls" in message:
    tool_calls = message["tool_calls"]
    tool_results = []

    for tool_call in tool_calls:
        func_name = tool_call["function"]["name"]
        args_str = tool_call["function"].get("arguments", "{}")
        args = json.loads(args_str)

        if func_name == "get_current_time":
            result = get_current_time()
        elif func_name == "get_weather":
            city = args.get("city", "北京")
            result = get_weather(city)  # ✅ 现在调用真实 API!
        else:
            result = "未知工具"

        tool_results.append({
            "tool_call_id": tool_call["id"],
            "output": result
        })

    # ===== 第二次调用(保持不变)=====
    messages = [
        {"role": "user", "content": user_input},
        message,
        *[{
            "role": "tool",
            "content": tr["output"],
            "tool_call_id": tr["tool_call_id"]
        } for tr in tool_results]
    ]

    final_response = Generation.call(
        model="qwen-max",
        messages=messages,
        tools=tools
    )
    answer = final_response.output.choices[0].message["content"]
else:
    answer = message["content"]

print("最终回答:")
print(answer)
EOF

🔑 关键改动说明

  1. 新增 requests 导入
    → 用于发送 HTTP 请求到天气 API。

  2. get_weather 函数重写

    • 使用 Nominatim 将城市名转为经纬度(支持“成都”、“Chengdu”等)
    • 使用 Open-Meteo 获取真实天气数据
    • 添加 完整的异常处理(超时、网络错误、数据解析失败)
  3. 工具描述微调

    • 将描述改为 "查询指定城市的实时天气(支持全球城市)",更准确
  4. 遵守 API 规范

    • Nominatim 要求设置 User-Agent(已添加)
    • 所有请求加 timeout=5 防止卡死

🌍 测试建议

可以尝试问:

  • "巴黎现在天气怎么样?"
  • "Tokyo 的温度是多少?"
  • "杭州今天下雨吗?"(注意:Open-Meteo 只提供当前天气,不提供“是否下雨”的文字描述)

💡 注意:免费 API 有速率限制,不要频繁调用。

现在你的 Agent 已经能连接真实世界了!🎉
这正是 AI Agent 的强大之处:模型负责思考,代码负责执行

posted @ 2026-01-26 22:06  船山薪火  阅读(22)  评论(0)    收藏  举报
![image](https://img2024.cnblogs.com/blog/3174785/202601/3174785-20260125205854513-941832118.jpg)