09-day2-实验-调用真实的网络资源-linux软件清华镜像站

🧪 实验名称:基于 Qwen 大模型的函数调用与外部工具集成

调用真实的网络资源-linux软件清华镜像站

一、实验目标

通过本实验,学生将能够:

  1. 理解大语言模型(LLM)的“工具调用”(Function Calling)机制
  2. 配置 DashScope API 并调用 Qwen-Max 模型
  3. 实现自定义 Python 函数作为 LLM 可调用的外部工具
  4. 构建交互式多轮对话系统,集成时间、天气、软件下载等实用功能
  5. 掌握网络请求的健壮性设计(超时、异常处理、fallback 策略)

二、实验原理

1. Function Calling 工作流程

大模型本身不具备实时数据获取能力。通过 Function Calling,模型可:

  • 分析用户意图
  • 决定是否需要调用外部工具
  • 生成结构化工具调用请求(tool_calls
  • 接收工具执行结果后,生成自然语言回答

典型流程:

用户输入 → LLM(第一阶段)→ 是否需工具? 
    ↓ 是
执行本地/网络函数 → 返回结果 → LLM(第二阶段)→ 生成最终回答
    ↓ 否
直接返回模型生成内容

2. 工具描述规范(OpenAI 兼容格式)

每个工具需定义:

  • name:函数名(必须与 Python 函数一致)
  • description:功能说明(帮助模型决策)
  • parameters:输入参数(JSON Schema 格式)

3. 两阶段调用必要性

  • 第一阶段:模型决定“做什么”
  • 第二阶段:模型基于真实数据“怎么说”

三、程序要点

▶ 1:整体结构

  • 工具函数定义区(get_current_time, get_weather, get_nginx_download_link
  • 工具注册区(tools 列表)
  • 主交互循环(含两阶段模型调用)

▶ 2:三个工具函数

工具模块 类型 核心功能 关键特性 容错/健壮性设计
时间工具 本地函数 获取当前系统时间 • 调用 datetime.now()• 无需外部依赖 100% 可靠(不依赖网络或外部服务)• 无异常风险
天气工具 网络函数 查询指定城市的当前天气 • 调用 https://wttr.in/城市?format=4&lang=zh• 支持中文输出 • 设置 User-Agent 避免被拦截• 请求超时限制(timeout=8 秒)• 捕获网络错误(如连接失败、DNS 错误)• 处理响应编码问题(自动解码 UTF-8)
Nginx 下载链接工具 混合函数(本地逻辑 + 网络探测) 返回 Nginx 1.25.4 的下载链接 固定版本1.25.4(不查最新版)• 优先使用清华镜像源: https://mirrors.tuna.tsinghua.edu.cn/nginx/nginx-1.25.4.tar.gz• 备用官方源: https://nginx.org/download/nginx-1.25.4.tar.gz • 使用 requests.head() 快速检测源可用性(不下载全文)• 若主源不可达(超时/404/连接失败),自动切换至备用源• 若双源均失败,返回明确错误信息

设计原则

  • 本地优先(时间)→ 网络智能降级(天气、Nginx)
  • 所有网络调用均有 超时 + 异常捕获,避免程序卡死
  • 国内用户友好(清华镜像加速)

▶ 3:注册工具、主循环

(1) 构建 tools 列表

  • 严格遵循 OpenAI 工具描述格式
  • 注意参数是否必填(required

(2) 实现两阶段调用逻辑

  • 第一次调用:tool_choice="auto"
  • 解析 tool_calls,执行对应函数
  • 构造包含 role: "tool" 的消息列表
  • 第二次调用:传入工具结果,获取最终回答

(3) 添加交互与异常处理

  • 支持 quit / exit 退出
  • 捕获 KeyboardInterrupt(Ctrl+C)
  • 全局异常兜底,避免程序崩溃

✅ 完整交互式代码

tee  day2_qwen_tool_calling_true_api.py   <<'EOF'
# day2_qwen_tool_calling_true_api.py 
import os
import json
import requests
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 获取天气"""
    try:
        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()
            if "Unknown location" in weather_str or not weather_str:
                return f"未找到城市 '{city}' 的天气信息"
            return weather_str
        else:
            return f"天气服务返回错误状态码: {response.status_code}"
    except Exception as e:
        return f"获取天气失败: {str(e)}"

def get_nginx_download_link():
    """
    返回 nginx 1.25 系列的下载链接(主线版本)
    来源:官方 + 清华镜像(双重保障)
    """
    official_url = "http://nginx.org/download/nginx-1.25.4.tar.gz"
    tuna_url = "https://mirrors.tuna.tsinghua.edu.cn/nginx/nginx-1.25.4.tar.gz"
    
    # 检测清华镜像是否可访问(快速超时)
    try:
        resp = requests.head(tuna_url, timeout=3)
        if resp.status_code == 200:
            return f"✅ nginx 1.25.4 下载链接(清华镜像):\n{tuna_url}"
    except:
        pass
    
    # fallback 到官方
    return f"✅ nginx 1.25.4 下载链接(官方):\n{official_url}"

# ===== 工具描述 =====
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"}},
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_nginx_download_link",
            "description": "获取 nginx 1.25 版本的下载链接(固定版本,非最新)",
            "parameters": {"type": "object", "properties": {}, "required": []}
        }
    }
]

# ===== 交互式主循环 =====
print("🤖 欢迎使用 Qwen 实用助手!")
print("你可以问:现在几点?北京天气?nginx 下载链接?")
print("输入 'quit' 或 'exit' 退出。\n")

while True:
    try:
        user_input = input("💬 你: ").strip()
        if not user_input:
            continue
        if user_input.lower() in ["quit", "exit", "退出"]:
            print("👋 再见!")
            break

        # 第一次调用
        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"]
                if func_name == "get_current_time":
                    result = get_current_time()
                elif func_name == "get_weather":
                    args = json.loads(tool_call["function"].get("arguments", "{}"))
                    city = args.get("city", "北京")
                    result = get_weather(city)
                elif func_name == "get_nginx_download_link":
                    result = get_nginx_download_link()
                else:
                    result = f"未知工具: {func_name}"

                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(f"\n🧠 助手: {answer}\n{'─' * 50}")

    except KeyboardInterrupt:
        print("\n👋 被用户中断,再见!")
        break
    except Exception as e:
        print(f"\n❌ 程序出错: {str(e)}\n{'─' * 50}")
		
EOF

🧪 测试效果

💬 你: nginx 下载链接?

🧠 助手: ✅ nginx 1.25.4 下载链接(清华镜像):
https://mirrors.tuna.tsinghua.edu.cn/nginx/nginx-1.25.4.tar.gz

──────────────────────────────────────────
💬 你: 现在几点?

🧠 助手: 当前时间是 2026-01-25 15:30:22。

──────────────────────────────────────────
💬 你: mysql

🧠 助手: 您提到 "mysql",请问您需要了解关于 MySQL 的哪方面信息?例如:

- MySQL 的安装和配置
- 基本的 SQL 语句使用
- 数据库设计与优化
- 备份与恢复
- 安全设置
- 性能调优
- 与其他软件的集成

请具体说明您的需求或问题,以便我能为您提供更准确的帮助。
──────────────────────────────────────────────────
💬 你: mysql 下载链接

🧠 助手: 您想要下载 MySQL 的哪个版本?另外,请指明您需要的是社区版(Community Edition)还是企业版(Enterprise Edition)。如果您不确定具体版本,我可以为您提供 MySQL 官方网站上最新的社区版下载链接。请告诉我更多详细信息。如果没有特别要求,我将直接提供最新稳定版的 MySQL 社区版下载链接。
由于我没有直接获取MySQL官方下载链接的函数工具,我将为您提供一个通用的方法来找到MySQL的下载页面:

1. 访问 MySQL 官方网站: https://www.mysql.com/
2. 导航到 Downloads (下载) 页面。
3. 选择您要下载的 MySQL 版本和类型(社区版或企业版)。

对于最新稳定版的 MySQL 社区版,您可以直接访问以下链接:
- MySQL Community Server: https://dev.mysql.com/downloads/mysql/

如果您需要特定版本,请访问上面提供的链接并从版本列表中选择。您也可以告诉我具体的版本号,我可以尝试帮您查找正确的下载链接。

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