基于Qwen大模型,采用“ReAct“模式(Reason + Act)完成天气调用Function Calling
print(f"❌ 地名查询失败: {data.get('info')} (infocode: {data.get('infocode')})")f"风力:{w['windpower']}级,湿度:{w['humidity']}%,"return {"status": "error", "msg": data.get("info", "天气查询失败")}"content": f"城市:{w['ci
基于Qwen3-max大模型实现,用户如要使用Qwen3大模型,需要注册阿里云百炼平台账户,获取DASHSCOPE_API_KEY。
本程序在Python中,使用DashScope调用Qwen3大模型的API接口。也可以使用OpenAI兼容接口。如使用DashScope,需要安装DashScope包。
从高德平台获取天气状况信息,需要注册高德平台,获取GAODE_API_KEY。
以下为实现的代码:
import os
import json
import requests
from dashscope import Generation
# ========================
# 1. 获取环境变量
# ========================
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
GAODE_API_KEY = os.getenv("GAODE_API_KEY")
if not DASHSCOPE_API_KEY:
raise EnvironmentError("请设置环境变量:DASHSCOPE_API_KEY")
if not GAODE_API_KEY:
raise EnvironmentError("请设置环境变量:GAODE_API_KEY")
# ========================
# 2. 利用高德adcode API实现用户输入的地名和adcode的转换,否则无法识别"广州市天河区"这种格式
# ========================
def geocode_location(location_name):
"""
根据地名(如“天河区”)查询对应的 adcode 和城市名
使用高德「输入提示 API」
"""
url = "https://restapi.amap.com/v3/assistant/inputtips"
params = {
"key": GAODE_API_KEY,
"keywords": location_name,
"city": "", # 可选:限定城市,如“广州”
"offset": 1,
"page": 1,
"output": "json"
}
try:
response = requests.get(url, params=params, timeout=10)
if response.status_code != 200:
print("❌ 网络请求失败")
return None, None
data = response.json()
if data.get("status") != "1":
print(f"❌ 地名查询失败: {data.get('info')} (infocode: {data.get('infocode')})")
return None, None
tips = data.get("tips", [])
if not tips:
print("⚠️ 未找到匹配的地点")
return None, None
# 取第一个最匹配的结果
place = tips[0]
adcode = place.get("adcode")
city_name = place.get("city") or place.get("name") # 优先取 city,否则取 name
return adcode, city_name
except requests.exceptions.RequestException as e:
print("❌ 请求异常:", e)
return None, None
# ========================
# 3. 高德天气 API 封装
# ========================
def get_real_weather(location: str) -> dict:
"""
调用高德地图天气 API 获取实时天气
文档:https://lbs.amap.com/api/webservice/guide/api/weatherinfo
"""
WEATHER_URL = "https://restapi.amap.com/v3/weather/weatherInfo"
adcode, city = geocode_location(location)
if adcode:
params = {
"key": GAODE_API_KEY,
"city": adcode, #使用高德的adcode代替location
"extensions": "base", # base: 实时天气;all: 预报
"output": "json"
}
try:
response = requests.get(WEATHER_URL, params=params, timeout=10)
data = response.json()
if data["status"] == "1" and data.get("lives"):
w = data["lives"][0]
return {
"status": "success",
"data": {
"city": w["city"],
"temperature": w["temperature"],
"weather": w["weather"],
"winddirection": w["winddirection"],
"windpower": w.get("windpower", "未知"),
"humidity": w["humidity"],
"report_time": w["reporttime"]
}
}
else:
return {"status": "error", "msg": data.get("info", "天气查询失败")}
except Exception as e:
return {"status": "error", "msg": str(e)}
# ========================
# 4. 主程序 采用业内广泛采用的"ReAct"模式(Reason + Act),两次调用大模型
# ========================
def main():
print("🌤️ 欢迎使用通义千问 + 高德天气助手")
print("💡 输入地区名称,例如:广州市天河区、北京市海淀区")
user_input = input("\n📍 请输入要查询的地区:").strip()
if not user_input:
print("❌ 请输入有效地区名称。")
return
# 构建对话历史
messages = [
{
"role": "system",
"content": "你是一个天气助手。当用户查询天气时,如果要查询的地名为中国大陆的地名,你必须调用 get_real_weather 工具获取真实数据,禁止编造或猜测。如果要查询的地名根本不是中国大陆的地名,则直接告诉查询者没有这个地方。"
},
{
"role": "user",
"content": f"请查询 {user_input} 的实时天气"
}
]
# 定义工具
tools = [
{
"type": "function",
"function": {
"name": "get_real_weather",
"description": "获取指定城市的实时天气信息(温度、天气、风力等)",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或区域名称,如'北京市'、'广州市天河区'"
}
},
"required": ["location"]
}
}
}
]
try:
# 第一轮:调用模型,判断是否需要工具
response = Generation.call(
model="qwen-max",
messages=messages,
tools=tools,
tool_choice="auto", # 可改为强制调用
result_format="message", # 必须!才能正确返回 tool_calls
api_key=DASHSCOPE_API_KEY
)
if response.status_code != 200:
print(f"❌ 大模型调用失败:{response.message}")
return
output = response.output # 返回的是完整结构
# ========================
# 解析 tool_calls(嵌套在 choices 中)
# ========================
if (
isinstance(output, dict) and
'choices' in output and
len(output['choices']) > 0 and
'message' in output['choices'][0] and
'tool_calls' in output['choices'][0]['message']
):
#解析tool_calls,需要调用外部工具
tool_calls = output['choices'][0]['message']['tool_calls']
if tool_calls and tool_calls[0]['function']['name'] == 'get_real_weather':
tool_call = tool_calls[0]
func_name = tool_call['function']['name']
args_str = tool_call['function']['arguments']
# 解析 arguments(是 JSON 字符串)
try:
args = json.loads(args_str)
except Exception as e:
print(f"❌ 参数解析失败:{e}")
args = {}
# location = args.get('location', user_input)
location = args.get('location')
print(f"\n🔍 正在通过高德查询 {location} 现在的天气状况...")
weather_result = get_real_weather(location)
if weather_result["status"] == "success":
w = weather_result["data"]
# 构造 tool 响应
tool_response = {
"role": "tool",
"content": f"城市:{w['city']},天气:{w['weather']},"
f"温度:{w['temperature']}°C,风向:{w['winddirection']},"
f"风力:{w['windpower']}级,湿度:{w['humidity']}%,"
f"更新时间:{w['report_time']}",
"tool_call_id": tool_call["id"] # 必须匹配
}
# 把模型的回复和 tool 响应都加入对话
messages.append(output['choices'][0]['message']) # 模型的 tool_calls
messages.append(tool_response)
# 第二轮:让模型生成自然语言回复
final_response = Generation.call(
model="qwen-max",
messages=messages,
result_format="message",
api_key=DASHSCOPE_API_KEY
)
if final_response.status_code == 200:
final_msg = final_response.output
content = ""
if isinstance(final_msg, dict):
content = final_response["output"]["choices"][0]["message"]["content"]
if content:
print(f"\n🌤️ 天气信息:\n{content}")
else:
print("❌ 模型未生成有效回复。")
else:
print(f"❌ 最终回复生成失败:{final_response.message}")
else:
print(f"❌ 天气查询失败:{weather_result['msg']}")
else:
print("⚠️ 未调用 get_real_weather 函数。")
else:
# 没有 tool_calls,直接输出模型文本
content = ""
if isinstance(output, dict):
if 'text' in output and output['text']:
content = output['text'].strip()
elif 'choices' in output and len(output['choices']) > 0:
msg = output['choices'][0]['message'].get('content', '')
content = msg.strip()
if content:
print(f"\n🤖 回复:\n{content}")
else:
print("❌ 模型未返回有效内容。")
except Exception as e:
print(f"❌ 程序异常:{str(e)}")
# ========================
#5. 启动入口
# ========================
if __name__ == "__main__":
main()
以上代码,在Python3.13.5版本运行正常。欢迎技术交流
更多推荐
所有评论(0)