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
🔑 关键改动说明
-
新增
requests导入
→ 用于发送 HTTP 请求到天气 API。 -
get_weather函数重写- 使用 Nominatim 将城市名转为经纬度(支持“成都”、“Chengdu”等)
- 使用 Open-Meteo 获取真实天气数据
- 添加 完整的异常处理(超时、网络错误、数据解析失败)
-
工具描述微调
- 将描述改为
"查询指定城市的实时天气(支持全球城市)",更准确
- 将描述改为
-
遵守 API 规范
- Nominatim 要求设置
User-Agent(已添加) - 所有请求加
timeout=5防止卡死
- Nominatim 要求设置
🌍 测试建议
可以尝试问:
"巴黎现在天气怎么样?""Tokyo 的温度是多少?""杭州今天下雨吗?"(注意:Open-Meteo 只提供当前天气,不提供“是否下雨”的文字描述)
💡 注意:免费 API 有速率限制,不要频繁调用。
现在你的 Agent 已经能连接真实世界了!🎉
这正是 AI Agent 的强大之处:模型负责思考,代码负责执行。
浙公网安备 33010602011771号