万字长文详解Qwen-agent的Function Calling(函数调用)【附实战代码】

前言

在之前的文章中,我们完成了Qwen-Agent的demo运用,这篇文章中,咱们一起来实现自定义Qwen-agent的Function Calling~
最后运行效果:
在这里插入图片描述

在这里插入图片描述

Qwen 函数调用的核心组件介绍

大型语言模型理解并遵循此协议有多种方式。关键在于提示工程 (Prompt Engineering) 或模型内化的模板。Qwen预先训练了多种支持函数调用的模板,以便用户可以直接利用这一过程。

Qwen 的函数调用框架包含以下关键组件:

  1. 函数定义:以 JSON 模式描述可用函数,包括函数名称、描述、参数和预期输出。模型通过这些模式了解可用工具及其使用方法。
  2. 聊天接口:Qwen 的聊天模型处理用户查询,决定生成文本响应还是函数调用。
  3. 工具集成:函数作为外部工具实现,系统根据模型输出执行函数,并将结果返回给模型以生成最终响应。

使用函数调用进行推理

由于函数调用本质上是通过提示工程实现的,您可以手动构建Qwen模型的输入。但是,支持函数调用的框架可以帮助您完成所有繁重的工作(在实战部分,我们将介绍通过专用的函数调用模板——Qwen-Agent来进行推理)。

案例

我们同样通过一个示例来展示推理的使用方法。

场景:假设我们要询问模型某个地点的温度。通常,模型会回答无法提供实时信息。但我们有两个工具,可以分别获取城市的当前温度和指定日期的温度,我们希望模型能够利用这些工具。

为了这个示例案例,使用以下代码:

import json

def get_current_temperature(location: str, unit: str = "celsius"):
    """获取指定地点的当前温度。

    参数:
        location: 要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。
        unit: 返回温度的单位,默认为“celsius”。(可选值:["celsius", "fahrenheit"])

    返回:
        包含温度、地点和单位的字典
    """
    return {
   
        "temperature": 26.1,
        "location": location,
        "unit": unit,
    }


def get_temperature_date(location: str, date: str, unit: str = "celsius"):
    """获取指定地点和日期的温度。

    参数:
        location: 要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。
        date: 要获取温度的日期,格式为“年-月-日”。
        unit: 返回温度的单位,默认为“celsius”。(可选值:["celsius", "fahrenheit"])

    返回:
        包含温度、地点、日期和单位的字典
    """
    return {
   
        "temperature": 25.9,
        "location": location,
        "date": date,
        "unit": unit,
    }


def get_function_by_name(name):
    if name == "get_current_temperature":
        return get_current_temperature
    if name == "get_temperature_date":
        return get_temperature_date


TOOLS = [
    {
   
        "type": "function",
        "function": {
   
            "name": "get_current_temperature",
            "description": "获取指定地点的当前温度。",
            "parameters": {
   
                "type": "object",
                "properties": {
   
                    "location": {
   
                        "type": "string",
                        "description": "要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。",
                    },
                    "unit": {
   
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "返回温度的单位,默认为“celsius”。",
                        "default": "celsius"
                    },
                },
                "required": ["location"],
            },
        },
    },
    {
   
        "type": "function",
        "function": {
   
            "name": "get_temperature_date",
            "description": "获取指定地点和日期的温度。",
            "parameters": {
   
                "type": "object",
                "properties": {
   
                    "location": {
   
                        "type": "string",
                        "description": "要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。",
                    },
                    "date": {
   
                        "type": "string",
                        "description": "要获取温度的日期,格式为“年-月-日”。",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
                    },
                    "unit": {
   
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "返回温度的单位,默认为“celsius”。",
                        "default": "celsius"
                    },
                },
                "required": ["location", "date"],
            },
        },
    },
]


MESSAGES = [
    {
   "role": "system", "content": "你是 Qwen,由阿里巴巴云创建。你是一个乐于助人的助手。\n\n当前日期:2025-05-06"},
    {
   "role": "user", "content": "上海现在的温度是多少?明天呢?"},
]

工具应使用JSON Schema进行描述,消息应包含尽可能多的有效信息。您可以在下面找到工具和消息的解释:

示例工具

工具应使用以下JSON进行描述:

TOOLS = [
    {
   
        "type": "function",
        "function": {
   
            "name": "get_current_temperature",
            "description": "获取指定地点的当前温度。",
            "parameters": {
   
                "type": "object",
                "properties": {
   
                    "location": {
   
                        "type": "string",
                        "description": "要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。",
                    },
                    "unit": {
   
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "返回温度的单位,默认为“celsius”。",
                        "default": "celsius"
                    },
                },
                "required": ["location"],
            },
        },
    },
    {
   
        "type": "function",
        "function": {
   
            "name": "get_temperature_date",
            "description": "获取指定地点和日期的温度。",
            "parameters": {
   
                "type": "object",
                "properties": {
   
                    "location": {
   
                        "type": "string",
                        "description": "要获取温度的地点,格式为“城市, 省, 国家”(例如“上海, 上海, 中国”)。",
                    },
                    "date": {
   
                        "type": "string",
                        "description": "要获取温度的日期,格式为“年-月-日”。",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
                    },
                    "unit": {
   
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "返回温度的单位,默认为“celsius”。",
                        "default": "celsius"
                    },
                },
                "required": ["location", "date"],
            },
        },
    },
]

对于每个工具,它是一个具有两个字段的JSON object:

  • type:string,用于指定工具类型,目前仅"function"有效

  • function:object,详细说明了如何使用该函数

对于每个function,它是一个具有三个字段的JSON object:

  • name:string 表示函数名称

  • description:string 描述函数用途

  • parameters:JSON Schema,用于指定函数接受的参数。请参阅链接文档以了解如何构建JSON Schema。值得注意的字段包括type、required和enum。

大多数框架使用“工具”格式,有些可能使用“函数”格式。根据命名,应该很明显应该使用哪一个。

示例消息

我们的查询是“上海现在的温度是多少?明天呢?”。由于模型不知道当前日期,更不用说明天了,我们应该在输入中提供日期。在这里,我们决定在默认系统消息“你是 Qwen,由阿里巴巴云创建。你是一个乐于助人的助手。”之后的系统消息中提供该信息。您可以在应用程序代码中将日期附加到用户消息。

MESSAGES = [
    {
   "role": "system", "content": "你是 Qwen,由阿里巴巴云创建。你是一个乐于助人的助手。\n\n当前日期:2025-05-06"},
    {
   "role": "user", "content": "上海现在的温度是多少?明天呢?"},
]

Qwen-Agent

Qwen-Agent 实际上是一个用于开发AI应用的Python智能体框架。尽管其设计用例比高效推理更高级,但它确实包含了Qwen2.5函数调用的规范实现。基于OpenAI兼容API,它可以通过模板为Qwen2.5提供了对用户透明的的函数调用能力。

值得注意的是,由于应用框架可以在幕后完成大量工作,目前Qwen2.5官方的函数调用实现非常灵活且超出了简单的模板化,这使得它难以适应那些使用能力较弱的模板引擎的其他框架。

在开始之前,让我们确保已安装了最新的库:

git clone --branch v0.0.21 https://github.com/QwenLM/Qwen-Agent.git
cd Qwen-Agent
pip install -e ./"[gui,rag,code_interpreter,mcp]"
# 本指南使用版本 v0.0.21。

准备工作

Qwen-Agent可以封装一个不支持函数调用的OpenAI兼容API。您可以使用大多数推理框架来提供此类API,或者从DashScope或Together等云提供商处获取一个。

假设在http://localhost:23333/v1处有一个OpenAI兼容API,Qwen-Agent提供了一个快捷函数get_chat_model,用于获取具有函数调用支持的模型推理类:

from qwen_agent.llm import get_chat_model

llm = get_chat_model({
   
    'model': '/share/new_models/qwen3/Qwen3-8B/',
    "model_server": "http://localhost:23333/v1",
    "api_key": "EMPTY",
})

在上述代码中,model_server是其他OpenAI兼容API客户端常用的api_base。建议您提供api_key(但不要以明文形式出现在代码中),即使API服务器不检查它,在这种情况下,您可以将其设置为任何值。

对于模型输入,应使用系统、用户和助手历史记录的通用消息结构:

messages = MESSAGES[:]
# [
#    {"role": "system", "content": "你是 Qwen,由阿里巴巴云创建。你是一个乐于助人的助手。\n\n当前日期:2025-05-06"},
#    {"role": "user", "content": "上海现在的温度是多少?明天呢?"}
# ]

我们在系统消息中添加当前日期,以便使用户消息中的”明天”有明确的参照点。如果需要,也可以将其添加到用户消息中。

目前,Qwen-Agent使用“函数”而非“工具”。这需要对我们工具描述进行一些小的更改,即提取函数字段:

functions = [tool["function"] for tool in TOOLS]

工具调用和工具结果

为了与模型交互,应使用chat方法:

for responses in llm.chat(
    messages=messages,
    functions=functions,
    extra_generate_cfg=dict(parallel_function_calls=True),
):
    pass
messages.extend(responses)
# 打印最终响应
print(json.dumps(messages, ensure_ascii=False, indent=2))
#这行代码的作用是将 messages 列表转换为 JSON 格式的字符串,在转换过程中允许非 ASCII 字符直接输出,并且为生成的 JSON 字符串添加缩进,最后将转换后的 JSON 字符串打印到控制台。

在上述代码中,chat方法接收messages、functions以及一个extra_generate_cfg参数。你可以在extra_generate_cfg中放入诸如temperature和top_p等采样参数。这里,我们添加了Qwen-Agent提供的特殊控制parallel_function_calls。顾名思义,它将启用并行函数调用,这意味着模型可能为单次请求生成多个函数调用,按照其判断进行。

chat方法返回一个列表的生成器,每个列表可能包含多条消息。因为我们启用了parallel_function_calls,我们应该在响应中得到:
在这里插入图片描述
即:

[
  {
   
    "role": "system",
    "content": "你是 Qwen,由阿里巴巴云创建。你是一个乐于助人的助手。\n\n当前日期:2025-05-06"
  },
  {
   
    "role": "user",
    "content": "上海现在的温度是多少?明天呢?"
  },
  {
   
    "role": "assistant",
    "content": "<think>\n好的,用户问的是上海现在的温度以及明天的温度。首先,我需要确定用户需要两个不同的温度数据:当前温度和明天同一时间的温度。当前温度可以用get_current_temperature函数来获取,而明天的温度需要使用get_temperature_date函数,并且日期参数要设置为明天的日期。\n\n首先,调用get_current_temperature函数,参数是location为“上海, 上海, 中国”,单位默认是摄氏度。接下来,确定明天的日期。今天是2025年5月6日,所以明天是5月7日。然后调用get_temperature_date函数,参数是location同样为“上海, 上海, 中国”,date设置为“2025-05-07”,单位同样保持默认的摄氏度。需要确保日期格式正确,符合YYYY-MM-DD的模式。这样就能获取到用户所需的两个温度数据了。\n</think>\n\n",
    "reasoning_content": ""
  },
  {
   
    "role": "assistant",
    "content": "",
    "reasoning_content": "",
    "function_call": {
   
      "name": "get_current_temperature",
      "arguments": "{
   \"location\": \"上海, 上海, 中国\"}"
    }
  },
  {
   
    "role": "assistant",
    "content": "",
    "reasoning_content"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值