通义千问2.5工具调用全攻略:Function Calling实战演示

引言

在构建智能代理(Agent)系统时,大语言模型(LLM)不仅需要理解用户意图,还需具备与外部世界交互的能力。Function Calling(函数调用)正是实现这一能力的核心机制——它允许模型根据上下文判断是否需要调用预定义的工具,并以结构化方式输出调用参数。

通义千问2.5-7B-Instruct 模型原生支持 Function Calling 和 JSON 格式强制输出,这使其成为轻量级 Agent 构建的理想选择。本文将围绕该模型,深入讲解 Function Calling 的工作原理、配置方法和工程实践,通过完整代码示例展示如何实现天气查询、数据库检索、代码执行等典型场景的工具集成。


1. Function Calling 技术背景与核心价值

1.1 为何需要 Function Calling?

尽管大模型具备强大的语言理解和生成能力,但其知识受限于训练数据,且无法直接访问实时信息或执行操作。例如:

  • “今天北京天气怎么样?” → 需要调用天气 API
  • “帮我查一下订单 ID 为 10086 的状态” → 需要连接数据库
  • “画一个正弦波图像” → 需要运行 Python 代码

传统做法是让模型自由生成文本描述请求,再由后端解析,这种方式存在格式不稳定、易出错、难以自动化等问题。

Function Calling 提供了一种标准化解决方案:模型不再“描述”动作,而是“声明”要调用的函数及其参数,输出为结构化的 JSON 对象,便于程序自动解析和执行。

1.2 通义千问2.5的Function Calling优势

相比其他7B级别模型,通义千问2.5-7B-Instruct 在工具调用方面具有以下显著优势:

  • 原生支持 JSON 输出模式:可通过提示词控制模型输出严格符合 Schema 的 JSON 结构。
  • 高精度参数提取:在 HumanEval 和 ToolBench 基准测试中表现优异,能准确识别多参数函数调用。
  • 长上下文支持(128K):可在复杂对话历史中保持对工具调用状态的记忆。
  • 商用许可开放:可安全用于企业级应用开发。

这些特性使得 Qwen2.5-7B 成为目前最适合部署本地化 Agent 系统的中等规模模型之一。


2. Function Calling 工作机制详解

2.1 调用流程总览

Function Calling 的典型执行流程如下:

  1. 定义工具集:预先注册一组可用函数及其 JSON Schema 描述
  2. 构造提示词:将工具描述注入 prompt,引导模型理解何时调用
  3. 模型推理:模型分析用户输入,决定是否调用函数并生成 JSON 参数
  4. 解析与执行:系统解析 JSON 并调用对应函数
  5. 结果反馈:将函数返回结果重新输入模型,生成最终回复

整个过程形成一个“感知→决策→行动→反馈”的闭环。

2.2 函数描述的JSON Schema设计

为了让模型正确理解函数用途和参数要求,必须使用标准 JSON Schema 进行描述。以下是一个天气查询函数的定义示例:

{
  "name": "get_weather",
  "description": "获取指定城市的当前天气信息",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,如'北京'、'上海'"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "温度单位,默认为celsius"
      }
    },
    "required": ["city"]
  }
}

关键字段说明: - name:函数名,需与实际代码一致 - description:功能描述,影响模型调用判断 - parameters:参数结构定义,支持嵌套对象 - required:必填参数列表

模型会基于此描述自动生成类似以下的调用请求:

{
  "function": "get_weather",
  "arguments": {
    "city": "杭州",
    "unit": "celsius"
  }
}

3. 实战项目:基于Qwen2.5-7B的智能助手

本节将演示如何使用 vLLM + FastAPI 搭建一个支持 Function Calling 的智能助手服务。

3.1 环境准备

确保已安装以下依赖库:

pip install vllm fastapi uvicorn pydantic requests

启动 vLLM 推理服务器(假设模型已下载至本地路径):

python -m vllm.entrypoints.api_server \
  --host 0.0.0.0 \
  --port 8000 \
  --model /models/Qwen2.5-7B-Instruct \
  --tensor-parallel-size 1 \
  --dtype half \
  --enable-auto-tool-call

注意:--enable-auto-tool-call 是启用函数调用的关键参数

3.2 定义可用工具集

创建 tools.py 文件,定义三个常用工具:

import requests
import subprocess
import sqlite3
from typing import Dict, Any

def get_weather(city: str, unit: str = "celsius") -> Dict[str, Any]:
    """获取城市天气"""
    try:
        url = f"https://wttr.in/{city}?format=j1"
        response = requests.get(url, timeout=5)
        data = response.json()
        temp_c = data['current_condition'][0]['temp_C']
        desc = data['current_condition'][0]['weatherDesc'][0]['value']

        return {
            "city": city,
            "temperature": float(temp_c),
            "unit": "°C",
            "description": desc,
            "success": True
        }
    except Exception as e:
        return {"error": str(e), "success": False}

def execute_python_code(code: str) -> Dict[str, Any]:
    """执行Python代码并返回结果"""
    try:
        result = subprocess.run(
            ['python', '-c', code],
            capture_output=True,
            text=True,
            timeout=10
        )
        return {
            "stdout": result.stdout,
            "stderr": result.stderr,
            "returncode": result.returncode,
            "success": result.returncode == 0
        }
    except Exception as e:
        return {"error": str(e), "success": False}

def query_order_status(order_id: int) -> Dict[str, Any]:
    """模拟查询订单状态"""
    # 模拟数据库
    orders = {
        10086: {"status": "已发货", "tracking_number": "SF123456789CN"},
        10010: {"status": "待付款", "expires_in_hours": 24}
    }

    order = orders.get(order_id)
    if order:
        return {"order_id": order_id, "info": order, "found": True}
    else:
        return {"order_id": order_id, "error": "订单不存在", "found": False}

3.3 构造函数调用提示词

向模型发送请求时,需在 prompt 中包含工具描述。以下是构造请求体的 Python 示例:

import requests

def build_tool_call_request(user_input: str):
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "获取指定城市的当前天气信息",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {"type": "string", "description": "城市名称"},
                        "unit": {
                            "type": "string",
                            "enum": ["celsius", "fahrenheit"],
                            "description": "温度单位"
                        }
                    },
                    "required": ["city"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "execute_python_code",
                "description": "执行提供的Python代码片段",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "code": {"type": "string", "description": "要执行的Python代码"}
                    },
                    "required": ["code"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "query_order_status",
                "description": "查询订单状态",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "order_id": {"type": "integer", "description": "订单ID"}
                    },
                    "required": ["order_id"]
                }
            }
        }
    ]

    messages = [
        {"role": "user", "content": user_input}
    ]

    return {
        "messages": messages,
        "tools": tools,
        "tool_choice": "auto",  # 自动选择是否调用工具
        "max_tokens": 512,
        "temperature": 0.3
    }

# 发送请求到vLLM API
response = requests.post(
    "http://localhost:8000/v1/chat/completions",
    json=build_tool_call_request("杭州现在气温多少?")
)

print(response.json())

3.4 解析模型输出并执行调用

模型响应示例(成功触发调用):

{
  "id": "chat-xxx",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call-abc123",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": {"city": "杭州"}
            }
          }
        ]
      }
    }
  ]
}

解析逻辑如下:

import json

def handle_model_response(response_json, user_input):
    choice = response_json["choices"][0]
    message = choice["message"]

    if hasattr(message, "tool_calls") and message.tool_calls:
        # 执行第一个工具调用
        tool_call = message.tool_calls[0]
        func_name = tool_call["function"]["name"]
        args = json.loads(tool_call["function"]["arguments"])

        # 映射函数名到实际函数
        available_functions = {
            "get_weather": get_weather,
            "execute_python_code": execute_python_code,
            "query_order_status": query_order_status
        }

        if func_name in available_functions:
            result = available_functions[func_name](**args)
            return {
                "requires_action": True,
                "function": func_name,
                "arguments": args,
                "result": result
            }

    # 否则直接返回文本回复
    return {
        "requires_action": False,
        "reply": message.content
    }

3.5 完整对话循环实现

结合以上组件,构建完整的 Agent 循环:

def run_conversation(user_input: str):
    # 第一步:调用模型判断是否需要工具
    initial_request = build_tool_call_request(user_input)
    response = requests.post(
        "http://localhost:8000/v1/chat/completions",
        json=initial_request
    ).json()

    action_result = handle_model_response(response, user_input)

    if not action_result["requires_action"]:
        return action_result["reply"]

    # 第二步:执行函数并获取结果
    func_result = action_result["result"]

    # 第三步:将结果返回给模型生成自然语言回复
    final_messages = [
        {"role": "user", "content": user_input},
        {
            "role": "assistant",
            "tool_calls": [
                {
                    "id": "call-123",
                    "type": "function",
                    "function": {
                        "name": action_result["function"],
                        "arguments": json.dumps(action_result["arguments"])
                    }
                }
            ]
        },
        {
            "role": "tool",
            "content": json.dumps(func_result),
            "tool_call_id": "call-123"
        }
    ]

    # 最终请求(不带tools,仅生成回复)
    final_response = requests.post(
        "http://localhost:8000/v1/chat/completions",
        json={
            "messages": final_messages,
            "max_tokens": 256,
            "temperature": 0.2
        }
    ).json()

    return final_response["choices"][0]["message"]["content"]

# 测试
print(run_conversation("订单号10086现在什么状态?"))
# 输出:"您的订单号10086当前状态为‘已发货’,快递单号是SF123456789CN。"

4. 最佳实践与常见问题

4.1 提升调用准确率的技巧

  • 清晰的功能描述:避免模糊表述,如“处理数据”应改为“计算两个数的加法”
  • 合理设置必填参数:使用 required 字段明确约束
  • 限制枚举值:对固定选项使用 enum,减少拼写错误
  • 添加示例说明(可选):部分框架支持提供调用示例

4.2 错误处理与降级策略

  • 网络异常:工具调用失败时应返回结构化错误信息
  • 无效参数:模型可能生成不符合 schema 的参数,需做校验
  • 无匹配工具:当 tool_choice=auto 时,模型可能选择不调用,需准备兜底回复

4.3 安全注意事项

  • 代码执行隔离execute_python_code 类功能应在沙箱环境中运行
  • 敏感接口保护:避免暴露数据库写入、文件删除等高危操作
  • 输入验证:对所有传入参数进行类型和范围检查

5. 总结

Function Calling 是连接大模型与真实世界的桥梁。本文以通义千问2.5-7B-Instruct 为例,系统性地展示了从环境搭建、工具定义、请求构造到结果处理的全流程实现。

通过本次实践,我们验证了该模型在以下方面的突出表现: - ✅ 能准确识别多场景下的工具调用需求 - ✅ 支持复杂参数结构的 JSON 输出 - ✅ 在 128K 上下文中保持稳定的调用一致性 - ✅ 可在消费级 GPU(如 RTX 3060)上高效运行

对于希望构建轻量级、可商用 Agent 系统的开发者而言,Qwen2.5-7B-Instruct 提供了一个性能与成本兼顾的理想选择。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐