Function Calling

更新时间:2025-03-26 02:59:33

大模型在面对实时性问题、数学计算等问题时可能效果不佳。您可以使用 Function Calling 功能,通过引入外部工具,使得大模型可以与外部世界进行交互。

支持的模型

当前支持通义千问的大语言模型。支持的模型为:

QwQ 模型支持 Function Calling,但使用方法与以上模型略有不同,请参见QwQ 工具调用
暂不支持通义千问多模态模型。

建议您优先选择通义千问-Plus,它在效果、速度和成本上相对均衡。

  • 如果您对响应的速度与成本控制有较高要求,商业版模型建议使用通义千问-Turbo系列,开源版模型建议使用Qwen2.5 系列的小参数模型;

  • 如果您对响应的准确率有较高要求,商业版模型建议使用通义千问-Max系列,开源版模型建议使用 qwen2.5-72b-instruct 模型。

概述

如果直接向通义千问 API 提问“阿里云最近有什么新闻?”,大模型并不能准确回答:

我无法提供实时信息,因为我的数据只更新到2021年。

如果这个问题需要人类帮助大模型来解决,那么一个有效的操作步骤为:

  1. 选择工具

    由于问题是关于实时新闻的,打开浏览器工具;

  2. 提取参数

    由于提问的问题是“阿里云最近有什么新闻?”,用浏览器输入框查询“阿里云新闻”;

  3. 运行工具

    浏览器返回了许多网页,如“阿里云成为总台《2025年春节联欢晚会》云计算AI独家合作伙伴”等;

  4. 将工具的输出提供给大模型

    将网页内容输入到提示词对通义千问 API 进行提问:“这是查询到的信息:阿里云成为总台《2025年春节联欢晚会》云计算AI独家合作伙伴......。请总结并回答:阿里云最近有什么新闻?”由于向大模型提供了足够的参考信息,可以得到类似的回复:

阿里云最近的重要新闻是它成为了中央广播电视总台《2025年春节联欢晚会》的云计算AI独家合作伙伴。
...
这些信息表明,阿里云不仅在技术基础设施方面具有优势,而且也在积极探索如何将AI技术应用于大型文化活动中,以创造新的用户体验。

大模型此时可以回答关于实时新闻的问题了,然而这个过程需要人的参与(选择工具、提取参数、运行工具),无法实现自动化运行的效果。

Function Calling(工具调用)可以自动化完成以上流程,在接收到提问后可以自动选择工具、提取参数、运行工具并总结工具的输出。效果对比如下所示:

已关闭 function calling
以上组件仅供您参考,并未真实发送请求。

工作流程示意图如下所示:

image

前提条件

您需要已 获取API Key配置API Key到环境变量。如果通过 OpenAI SDK 或 DashScope SDK 进行调用,需要 安装SDK。如果您是子业务空间的成员,请确保超级管理员已为该业务空间进行 模型授权操作

如何使用

本部分以 OpenAI 兼容调用方式为例,向您分步骤介绍 Function Calling 的详细用法,备选的工具为天气查询时间查询

如果您使用 DashScope SDK,或需要直接获取完整代码,请点击下表的对应位置。

DashScope

DashScope Python SDK

DashScope Java SDK

OpenAI 兼容

OpenAI Python SDK

OpenAI Node.js SDK

1. 定义工具

工具是连接大模型与外部世界的桥梁,它是实现 Function Calling 的关键,您首先需要对工具进行定义。

1.1. 定义工具函数

您需要定义两个工具函数:天气查询工具与时间查询工具。

  • 天气查询工具

    天气查询工具接收arguments参数,arguments格式为{"location": "查询的地点"}。工具的输出为字符串,格式为:“{位置}今天是{天气}。”

    为了便于演示,此处定义的天气查询工具并不真正查询天气,会从晴天、多云、雨天随机选择。在您的实际业务中可以使用如高德天气查询等工具进行替换。
  • 时间查询工具

    时间查询工具不需要输入参数。工具的输出为字符串,格式为:“当前时间:{查询到的时间}。”

    如果您使用 Node.js,请使用以下命令安装获取时间的工具包 date-fns:
    npm install date-fns
Python
Node.js
## 步骤1:定义工具函数

# 添加导入random模块
import random
from datetime import datetime

# 模拟天气查询工具。返回结果示例:“北京今天是雨天。”
def get_current_weather(arguments):
    # 定义备选的天气条件列表
    weather_conditions = ["晴天", "多云", "雨天"]
    # 随机选择一个天气条件
    random_weather = random.choice(weather_conditions)
    # 从 JSON 中提取位置信息
    location = arguments["location"]
    # 返回格式化的天气信息
    return f"{location}今天是{random_weather}。"

# 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“
def get_current_time():
    # 获取当前日期和时间
    current_datetime = datetime.now()
    # 格式化当前日期和时间
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # 返回格式化后的当前时间
    return f"当前时间:{formatted_time}。"

# 测试工具函数并输出结果,运行后续步骤时可以去掉以下四句测试代码
print("测试工具输出:")
print(get_current_weather({"location": "上海"}))
print(get_current_time())
print("\n")
// 步骤1:定义工具函数

// 导入时间查询工具
import { format } from 'date-fns';

function getCurrentWeather(args) {
    // 定义备选的天气条件列表
    const weatherConditions = ["晴天", "多云", "雨天"];
    // 随机选择一个天气条件
    const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
    // 从 JSON 中提取位置信息
    const location = args.location;
    // 返回格式化的天气信息
    return `${location}今天是${randomWeather}。`;
}

function getCurrentTime() {
    // 获取当前日期和时间
    const currentDatetime = new Date();
    // 格式化当前日期和时间
    const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
    // 返回格式化后的当前时间
    return `当前时间:${formattedTime}。`;
}

// 测试工具函数并输出结果,运行后续步骤时可以去掉以下四句测试代码
console.log("测试工具输出:")
console.log(getCurrentWeather({location:"上海"}));
console.log(getCurrentTime());
console.log("\n")

运行工具后,得到输出:

测试工具输出:
上海今天是多云。
当前时间:2025-01-08 20:21:45。

1.2 创建 tools 数组

人类在选择工具之前,需要对工具有全面的了解,包括工具的功能、何时使用以及输入参数等。大模型也需要这些信息才能更准确地选择工具。在使用 Function Calling 时,请根据以下的 JSON 格式提供工具信息。

  • type字段固定为"function"

  • function字段为 Object 类型;

    • name字段为自定义的工具函数名称,建议使用与函数相同的名称,如get_current_weatherget_current_time

    • description字段是对工具函数功能的描述,大模型会参考该字段来选择是否使用该工具函数。

    • parameters字段是对工具函数入参的描述,类型是 Object ,大模型会参考该字段来进行入参的提取。如果工具函数不需要输入参数,则无需指定parameters参数。

      • type字段固定为"object"

      • properties字段描述了入参的名称、数据类型与描述,为 Object 类型,Key 值为入参的名称,Value 值为入参的数据类型与描述;

      • required字段指定哪些参数为必填项,为 Array 类型。

对于天气查询工具来说,工具描述信息的格式如下:

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "当你想查询指定城市的天气时非常有用。",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市或县区,比如北京市、杭州市、余杭区等。",
                }
            },
            "required": ["location"]
        }
    }
}

在发起 Function Calling 前,您需要通过 tools 参数传入工具的描述信息。tools 为 JSON Array 类型,Array 中的元素为创建好的工具描述信息。

tools 参数在发起 Function Calling时进行指定。
Python
Node.js
# 请将以下代码粘贴到步骤1代码后

## 步骤2:创建 tools 数组

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。",
                    }
                },
                "required": ["location"]
            }
        }
    }
]
tool_name = [tool["function"]["name"] for tool in tools]
print(f"创建了{len(tools)}个工具,为:{tool_name}\n")
// 请将以下代码粘贴到步骤1代码后

// 步骤2:创建 tools 数组

const tools = [
    {
      type: "function",
      function: {
        name: "get_current_time",
        description: "当你想知道现在的时间时非常有用。",
      }
    },
    {
      type: "function",
      function: {
        name: "get_current_weather",
        description: "当你想查询指定城市的天气时非常有用。",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "城市或县区,比如北京市、杭州市、余杭区等。",
            }
          },
          required: ["location"]
        }
      }
    }
  ];
  
const toolNames = tools.map(tool => tool.function.name);
console.log(`创建了${tools.length}个工具,为:${toolNames.join(', ')}\n`);

2. 创建messages数组

直接向通义千问 API 提问类似,您同样需要维护一个 messages 数组来向大模型传入指令与上下文信息。发起 Function Calling 前,messages 数组需要包含 System Message 与 User Message。

System Message

尽管在创建 tools 数组时已经对工具的作用与何时使用工具进行了描述,但在 System Message 中强调何时调用工具通常会提高工具调用的准确率。在当前场景下,我们可以将 System Prompt 设置为:

你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数;
如果用户提问关于时间的问题,请调用‘get_current_time’函数。
请以友好的语气回答问题。

User Message

User Message 用于传入用户提问的问题。假设用户提问“上海天气”,此时的 messages 数组为:

Python
Node.js
# 步骤3:创建messages数组
# 请将以下代码粘贴到步骤2 代码后
messages = [
    {
        "role": "system",
        "content": """你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数;
     如果用户提问关于时间的问题,请调用‘get_current_time’函数。
     请以友好的语气回答问题。""",
    },
    {
        "role": "user",
        "content": "上海天气"
    }
]
print("messages 数组创建完成\n")
// 步骤3:创建messages数组
// 请将以下代码粘贴到步骤2 代码后
const messages = [
    {
        role: "system",
        content: "你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数; 如果用户提问关于时间的问题,请调用‘get_current_time’函数。请以友好的语气回答问题。",
    },
    {
        role: "user",
        content: "上海天气"
    }
];

console.log("messages 数组创建完成\n");

3. 发起 Function Calling

在完成 tools 与 messages 的创建后,您可以参考以下代码发起 Function Calling,让大模型“决策”是否需要调用工具,如果需要,大模型会输出需要调用的工具函数与入参信息。

Python
Node.js
# 步骤4:发起 function calling
# 请将以下代码粘贴到步骤3 代码后

from openai import OpenAI
import os

client = OpenAI(
    # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def function_calling():
    completion = client.chat.completions.create(
        model="qwen-plus",  # 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
        messages=messages,
        tools=tools
    )
    print("返回对象:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("正在发起function calling...")
completion = function_calling()
// 步骤4:发起 function calling
// 请将以下代码粘贴到步骤3 代码后

import OpenAI from "openai";
const openai = new OpenAI(
    {
        // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
    }
);

async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen-plus",  // 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
        messages: messages,
        tools: tools
    });
    console.log("返回对象:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

大模型通过返回对象的tool_calls参数,指定需要使用的工具函数名称为:"get_current_weather",并指定函数的入参为:"{\"location\": \"上海\"}"

{
    "content": "",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_6596dafa2a6a46f7a217da",
            "function": {
                "arguments": "{\"location\": \"上海\"}",
                "name": "get_current_weather"
            },
            "type": "function",
            "index": 0
        }
    ]
}

需要注意的是,如果提问的问题被大模型判断为无需使用工具,则不会返回tool_calls参数,并会通过content参数直接进行回复。在输入“你好”时,tool_calls参数为空,返回对象格式为:

{
    "content": "你好!有什么可以帮助你的吗?如果你有关于天气或者时间的问题,我特别擅长回答。",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}
如果没有返回 tool_calls 参数,您的程序可以在此处直接返回 content 字段,无需运行以下步骤。
如果您希望每次发起 Function Calling 后大模型都可以选择工具,请参见:强制工具调用

4. 运行工具函数

Function Calling流程中,运行工具函数是将大模型的决策转化为实际操作的关键步骤。就像在天气查询工具输入“上海”来获取天气一样,您在得到工具函数以及函数的入参后,可以运行工具函数来得到工具的输出。

运行工具函数的过程由您的计算环境而非大模型来完成。

由于大模型只可以输出字符串格式的内容,因此在运行工具函数前,您需要针对字符串格式的工具函数与入参分别进行解析。

  • 工具函数

    您需要建立一个工具函数名称工具函数实体的映射function_mapper,将返回的工具函数字符串映射到工具函数实体;

  • 入参

    Function Calling 返回的入参为 JSON 字符串,您可以使用 JSON 字符串解析工具将其解析为 JSON 对象,提取出入参信息。

Python
Node.js
# 步骤5:运行工具函数
# 请将以下代码粘贴到步骤4 代码后
import json

print("正在执行工具函数...")
# 从返回的结果中获取函数名称和入参
function_name = completion.choices[0].message.tool_calls[0].function.name
arguments_string = completion.choices[0].message.tool_calls[0].function.arguments

# 使用json模块解析参数字符串
arguments = json.loads(arguments_string)
# 创建一个函数映射表
function_mapper = {
    "get_current_weather": get_current_weather,
    "get_current_time": get_current_time
}
# 获取函数实体
function = function_mapper[function_name]
# 如果入参为空,则直接调用函数
if arguments == {}:
    function_output = function()
# 否则,传入参数后调用函数
else:
    function_output = function(arguments)
# 打印工具的输出
print(f"工具函数输出:{function_output}\n")
// 步骤5:运行工具函数
// 请将以下代码粘贴到步骤4 代码后

console.log("正在执行工具函数...");
const function_name = completion.choices[0].message.tool_calls[0].function.name;
const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments;

// 使用JSON模块解析参数字符串
const args = JSON.parse(arguments_string);

// 创建一个函数映射表
const functionMapper = {
    "get_current_weather": getCurrentWeather,
    "get_current_time": getCurrentTime
};

// 获取函数实体
const func = functionMapper[function_name];

// 如果入参为空,则直接调用函数
let functionOutput;
if (Object.keys(args).length === 0) {
    functionOutput = func();
} else {
    // 否则,传入参数后调用函数
    functionOutput = func(args);
}

// 打印工具的输出
console.log(`工具函数输出:${functionOutput}\n`);

运行以上代码可以得到如下输出:

上海今天是多云。

您可以直接将工具输出作为最终的结果。如果您希望返回结果更符合人类语气,可以参见 大模型总结工具函数输出

5. 大模型总结工具函数输出(可选)

工具函数的输出格式较为固定,如果直接返回给用户,可能会有语气生硬、不够灵活等问题。如果您希望大模型能够综合用户输入以及工具输出结果,来生成自然语言风格的回复,可以更新 messages 数组将工具输出提交到大模型,并再次发出请求。

  1. 添加 Assistant Message

    发起 Function Calling后,您可以通过completion.choices[0].message得到 Assistant Message,首先将它添加到 messages 数组中;

  2. 添加 Tool Message

    将工具的输出通过{"role": "tool", "content": "工具的输出","tool_call_id": completion.choices[0].message.tool_calls[0].id}形式添加到 messages 数组。

    请确保工具的输出为字符串格式。
Python
Node.js
# 步骤6:向大模型提交工具输出
# 请将以下代码粘贴到步骤5 代码后

messages.append(completion.choices[0].message)
print("已添加assistant message")
messages.append({"role": "tool", "content": function_output, "tool_call_id": completion.choices[0].message.tool_calls[0].id})
print("已添加tool message\n")
// 步骤6:向大模型提交工具输出
// 请将以下代码粘贴到步骤5 代码后

messages.push(completion.choices[0].message);
console.log("已添加 assistant message")
messages.push({
    "role": "tool",
    "content": functionOutput,
    "tool_call_id": completion.choices[0].message.tool_calls[0].id
});
console.log("已添加 tool message\n");

此时的 messages 数组为:

[
  System Message -- 指引模型调用工具的策略
  User Message -- 用户的问题
  Assistant Message -- 模型返回的工具调用信息
  Tool Message -- 工具的输出信息(如果采用下文介绍的并行工具调用,可能有多个 Tool Message)
]

更新 messages 数组后,运行以下代码。

Python
Node.js
# 步骤7:大模型总结工具输出
# 请将以下代码粘贴到步骤6 代码后
print("正在总结工具输出...")
completion = function_calling()
// 步骤7:大模型总结工具输出
// 请将以下代码粘贴到步骤6 代码后

console.log("正在总结工具输出...");
const completion_1 = await functionCalling();

从返回的content参数可以得到回复内容:“上海今天的天气是多云。如果您有其他问题,欢迎继续提问。”

{
    "content": "上海今天的天气是多云。如果您有其他问题,欢迎继续提问。",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}

到此步骤,您已经完成了一次完整的 Function Calling 流程。

进阶用法

流式输出

为了提升用户体验和减少等待时间,您可以使用流式输出快速获取所需调用的工具函数名称。其中:

  • 工具函数名称:仅在第一个流式返回的对象中出现。

  • 入参信息:以持续的流式方式输出。

流式输出方式能让您更灵活地处理 Function Calling 的结果。您可以参考以下代码,将发起 Function Calling修改为流式输出方式。

Python
Node.js
def function_calling():
    completion = client.chat.completions.create(
        model="qwen-plus",
        messages=messages,
        tools=tools,
        stream=True
    )
    for chunk in completion:
        print(chunk.model_dump_json())

function_calling()
async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen-plus",
        messages: messages,
        tools: tools,
        stream: true
    });
    for await (const chunk of completion) {
        console.log(JSON.stringify(chunk))
    }
}

functionCalling();

从第一个返回的流式输出对象可以获得工具函数名称,入参信息则需要您进行拼接,拼接完成后再运行工具函数。

{"id":"chatcmpl-3f8155c3-e96f-95bc-a2a6-8e48537a0893","choices":[{"delta":{"content":null,"function_call":null,"refusal":null,"role":"assistant","tool_calls":[{"index":0,"id":"call_5507104cabae4f64a0fdd3","function":{"arguments":"{\"location\":","name":"get_current_weather"},"type":"function"}]},"finish_reason":null,"index":0,"logprobs":null}],"created":1736251532,"model":"qwen-plus","object":"chat.completion.chunk","service_tier":null,"system_fingerprint":null,"usage":null}
{"id":"chatcmpl-3f8155c3-e96f-95bc-a2a6-8e48537a0893","choices":[{"delta":{"content":null,"function_call":null,"refusal":null,"role":null,"tool_calls":[{"index":0,"id":"","function":{"arguments":" \"上海\"}","name":""},"type":"function"}]},"finish_reason":null,"index":0,"logprobs":null}],"created":1736251532,"model":"qwen-plus","object":"chat.completion.chunk","service_tier":null,"system_fingerprint":null,"usage":null}
{"id":"chatcmpl-3f8155c3-e96f-95bc-a2a6-8e48537a0893","choices":[{"delta":{"content":null,"function_call":null,"refusal":null,"role":null,"tool_calls":[{"index":0,"id":"","function":{"arguments":null,"name":null},"type":"function"}]},"finish_reason":"tool_calls","index":0,"logprobs":null}],"created":1736251532,"model":"qwen-plus","object":"chat.completion.chunk","service_tier":null,"system_fingerprint":null,"usage":null}

如果您需要使用大模型总结工具函数输出,添加的 Assistant Message 需要符合下方格式。

{
    "content": "",
    "refusal": None,
    "role": "assistant",
    "audio": None,
    "function_call": None,
    "tool_calls": [
        {
            "id": "call_xxx",
            "function": {
                "arguments": '{"location": "上海"}',
                "name": "get_current_weather",
            },
            "type": "function",
            "index": 0,
        }
    ],
}

您需要对以下元素进行替换:

  • id

    tool_callsid替换为第一个返回的流式输出对象中的choices[0].delta.tool_calls[0].id

  • arguments

    将入参信息拼接后,替换tool_callsarguments

  • name

    tool_callsname替换为第一个返回的流式输出对象中的choices[0].delta.tool_calls[0].function.name

指定工具调用方式

并行工具调用

上文中的问题“上海天气”只需经过一次工具调用即可得到准确回复,如果输入问题需要调用多次工具,如“四个直辖市的天气如何”或“杭州天气,以及现在几点了”,发起 Function Calling 后只会返回一个工具调用信息,以提问“四个直辖市的天气如何”为例:

{
    "content": "",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_61a2bbd82a8042289f1ff2",
            "function": {
                "arguments": "{\"location\": \"北京市\"}",
                "name": "get_current_weather"
            },
            "type": "function",
            "index": 0
        }
    ]
}

返回结果中只有北京市的入参信息。为了解决这一问题,您可以在发起 Function Calling时,将请求参数parallel_tool_calls设置为true,这样返回对象中将包含所有需要调用的工具函数与入参信息。

Python
Node.js
def function_calling():
    completion = client.chat.completions.create(
        model="qwen-plus",  # 此处以 qwen-plus 为例,可按需更换模型名称
        messages=messages,
        tools=tools,
        # 新增参数
        parallel_tool_calls=True
    )
    print("返回对象:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("正在发起function calling...")
completion = function_calling()
async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen-plus",  // 此处以qwen-plus为例,可按需更换模型名称
        messages: messages,
        tools: tools,
        parallel_tool_calls: true
    });
    console.log("返回对象:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

在返回对象的tool_calls数组中包含了四个直辖市的入参信息:

{
    "content": "",
    "role": "assistant",
    "tool_calls": [
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"北京市\"}"
            },
            "index": 0,
            "id": "call_c2d8a3a24c4d4929b26ae2",
            "type": "function"
        },
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"天津市\"}"
            },
            "index": 1,
            "id": "call_dc7f2f678f1944da9194cd",
            "type": "function"
        },
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"上海市\"}"
            },
            "index": 2,
            "id": "call_55c95dd718d94d9789c7c0",
            "type": "function"
        },
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"重庆市\"}"
            },
            "index": 3,
            "id": "call_98a0cc7fded64b3ba88251",
            "type": "function"
        }
    ]
}

强制工具调用

大模型生成内容具有不确定性,有时会选择错误的工具进行调用。如果您希望对于某一类问题,大模型能够采取制定好的工具选择策略(如强制使用某个工具、强制不使用工具),可以通过修改tool_choice参数来强制指定工具调用的策略。

tool_choice参数的默认值为"auto",表示由大模型自主判断如何进行工具调用。
如果需要 大模型总结工具函数输出,请在发起总结的请求时将tool_choice参数去除,否则大模型 API 仍会返回工具调用信息。
  • 强制使用某个工具

    如果您希望对于某一类问题,Function Calling 能够强制调用某个工具,可以设定tool_choice参数为{"type": "function", "function": {"name": "the_function_to_call"}},大模型将不参与工具的选择,只参与输入参数的选择。

    假设当前场景中只包含天气查询的问题,您可以修改 function_calling 代码为:

    Python
    Node.js
    def function_calling():
        completion = client.chat.completions.create(
            model="qwen-plus",
            messages=messages,
            tools=tools,
            tool_choice={"type": "function", "function": {"name": "get_current_weather"}}
        )
        print(completion.model_dump_json())
    
    function_calling()
    async function functionCalling() {
        const response = await openai.chat.completions.create({
            model: "qwen-plus",
            messages: messages,
            tools: tools,
            tool_choice: {"type": "function", "function": {"name": "get_current_weather"}}
        });
        console.log("返回对象:");
        console.log(JSON.stringify(response.choices[0].message));
        console.log("\n");
        return response;
    }
    
    const response = await functionCalling();

    无论输入什么问题,返回对象的工具函数都会是get_current_weather

    使用该策略前请确保问题与选择的工具相关,否则可能返回不符合预期的结果。
  • 强制不使用工具

    如果您希望无论输入什么问题,Function Calling 都不会进行工具调用(返回对象中包含回复内容contenttool_calls参数为空),可以设定tool_choice参数为"none",或不传入tools参数,Function Calling 返回的tool_calls参数将始终为空。

    假设当前场景中的问题均无需调用工具,您可以修改 function_calling 代码为:

    Python
    Node.js
    def function_calling():
        completion = client.chat.completions.create(
            model="qwen-plus",
            messages=messages,
            tools=tools,
            tool_choice="none"
        )
        print(completion.model_dump_json())
    
    function_calling()
    async function functionCalling() {
        const completion = await openai.chat.completions.create({
            model: "qwen-plus",
            messages: messages,
            tools: tools,
            tool_choice: "none"
        });
        console.log("返回对象:");
        console.log(JSON.stringify(completion.choices[0].message));
        console.log("\n");
        return completion;
    }
    
    const completion = await functionCalling();

如何计费

在发起 Function Calling 时,您需要提供 tools 和 messages 参数。输入内容的计费不仅包含 messages 的 Token,还包括 tools 参数中工具描述信息转化后的 Token。

完整代码

OpenAI兼容
DashScope

您可以通过OpenAI SDKOpenAI兼容的HTTP方式调用通义千问模型,体验Function Call的功能。

Python
Node.js
HTTP

示例代码

from openai import OpenAI
from datetime import datetime
import json
import os
import random

client = OpenAI(
    # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 填写DashScope SDK的base_url
)

# 定义工具列表,模型在选择使用哪个工具时会参考工具的name和description
tools = [
    # 工具1 获取当前时刻的时间
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
            # 因为获取当前时间无需输入参数,因此parameters为空字典
            "parameters": {}
        }
    },  
    # 工具2 获取指定城市的天气
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {  
                "type": "object",
                "properties": {
                    # 查询天气时需要提供位置,因此参数设置为location
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                    }
                }
            },
            "required": [
                "location"
            ]
        }
    }
]

# 模拟天气查询工具。返回结果示例:“北京今天是雨天。”
def get_current_weather(arguments):
    # 定义备选的天气条件列表
    weather_conditions = ["晴天", "多云", "雨天"]
    # 随机选择一个天气条件
    random_weather = random.choice(weather_conditions)
    # 从 JSON 中提取位置信息
    location = arguments["location"]
    # 返回格式化的天气信息
    return f"{location}今天是{random_weather}。"

# 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“
def get_current_time():
    # 获取当前日期和时间
    current_datetime = datetime.now()
    # 格式化当前日期和时间
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # 返回格式化后的当前时间
    return f"当前时间:{formatted_time}。"

# 封装模型响应函数
def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen-plus",  # 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
        messages=messages,
        tools=tools
        )
    return completion

def call_with_messages():
    print('\n')
    messages = [
            {
                "content": input('请输入:'),  # 提问示例:"现在几点了?" "一个小时后几点" "北京天气如何?"
                "role": "user"
            }
    ]
    print("-"*60)
    # 模型的第一轮调用
    i = 1
    first_response = get_response(messages)
    assistant_output = first_response.choices[0].message
    print(f"\n第{i}轮大模型输出信息:{first_response}\n")
    if  assistant_output.content is None:
        assistant_output.content = ""
    messages.append(assistant_output)
    # 如果不需要调用工具,则直接返回最终答案
    if assistant_output.tool_calls == None:  # 如果模型判断无需调用工具,则将assistant的回复直接打印出来,无需进行模型的第二轮调用
        print(f"无需调用工具,我可以直接回复:{assistant_output.content}")
        return
    # 如果需要调用工具,则进行模型的多轮调用,直到模型判断无需调用工具
    while assistant_output.tool_calls != None:
        # 如果判断需要调用查询天气工具,则运行查询天气工具
        tool_info = {"content": "","role": "tool", "tool_call_id": assistant_output.tool_calls[0].id}
        if assistant_output.tool_calls[0].function.name == "get_current_weather":
            # 提取位置参数信息
            argumens = json.loads(assistant_output.tool_calls[0].function.arguments)
            tool_info["content"] = get_current_weather(argumens)
        # 如果判断需要调用查询时间工具,则运行查询时间工具
        elif assistant_output.tool_calls[0].function.name == 'get_current_time':
            tool_info["content"] = get_current_time()
        tool_output = tool_info["content"]
        print(f"工具输出信息:{tool_output}\n")
        print("-"*60)
        messages.append(tool_info)
        assistant_output = get_response(messages).choices[0].message
        if assistant_output.content is None:
            assistant_output.content = ""
        messages.append(assistant_output)
        i += 1
        print(f"第{i}轮大模型输出信息:{assistant_output}\n")
    print(f"最终答案:{assistant_output.content}")

if __name__ == '__main__':
    call_with_messages()

返回结果

当输入:几点了?时,程序会进行如下输出:

2024-07-25_15-37-20 (1)

以下是发起Function Call流程(模型的第一轮调用)时模型的返回信息。当输入“杭州天气”时,模型会返回tool_calls参数;当输入“你好”时,模型判断无需调用工具,模型不会返回tool_calls参数。

输入:杭州天气
输入:你好
{
    'id': 'chatcmpl-e2f045fd-2604-9cdb-bb61-37c805ecd15a',
    'choices': [
        {
            'finish_reason': 'tool_calls',
            'index': 0,
            'logprobs': None,
            'message': {
                'content': '',
                'role': 'assistant',
                'function_call': None,
                'tool_calls': [
                    {
                        'id': 'call_7a33ebc99d5342969f4868',
                        'function': {
                            'arguments': '{
                                "location": "杭州市"
                            }',
                            'name': 'get_current_weather'
                        },
                        'type': 'function',
                        'index': 0
                    }
                ]
            }
        }
    ],
    'created': 1726049697,
    'model': 'qwen-max',
    'object': 'chat.completion',
    'service_tier': None,
    'system_fingerprint': None,
    'usage': {
        'completion_tokens': 18,
        'prompt_tokens': 217,
        'total_tokens': 235
    }
}
{
    'id': 'chatcmpl-5d890637-9211-9bda-b184-961acf3be38d',
    'choices': [
        {
            'finish_reason': 'stop',
            'index': 0,
            'logprobs': None,
            'message': {
                'content': '你好!有什么可以帮助你的吗?',
                'role': 'assistant',
                'function_call': None,
                'tool_calls': None
            }
        }
    ],
    'created': 1726049765,
    'model': 'qwen-max',
    'object': 'chat.completion',
    'service_tier': None,
    'system_fingerprint': None,
    'usage': {
        'completion_tokens': 7,
        'prompt_tokens': 216,
        'total_tokens': 223
    }
}

示例代码

import OpenAI from "openai";
import { format } from 'date-fns';
import readline from 'readline';

function getCurrentWeather(location) {
    return `${location}今天是雨天。`;
}
function getCurrentTime() {
    // 获取当前日期和时间
    const currentDatetime = new Date();
    // 格式化当前日期和时间
    const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
    // 返回格式化后的当前时间
    return `当前时间:${formattedTime}。`;
}
const openai = new OpenAI(
    {
        // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
// 工具1 获取当前时刻的时间
{
    "type": "function",
    "function": {
        "name": "getCurrentTime",
        "description": "当你想知道现在的时间时非常有用。",
        // 因为获取当前时间无需输入参数,因此parameters为空
        "parameters": {}  
    }
},  
// 工具2 获取指定城市的天气
{
    "type": "function",
    "function": {
        "name": "getCurrentWeather",
        "description": "当你想查询指定城市的天气时非常有用。",
        "parameters": {  
            "type": "object",
            "properties": {
                // 查询天气时需要提供位置,因此参数设置为location
                "location": {
                    "type": "string",
                    "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                }
            },
            "required": ["location"]
        }
    }
}
];
async function getResponse(messages) {
    const response = await openai.chat.completions.create({
        model: "qwen-plus",  // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
        messages: messages,
        tools: tools,
    });
    return response;
}
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
rl.question("user: ", async (question) => {
    const messages = [{"role": "user","content": question}];
    let i = 1;
    const firstResponse = await getResponse(messages);
    let assistantOutput = firstResponse.choices[0].message;    
    console.log(`第${i}轮大模型输出信息:${JSON.stringify(assistantOutput)}`);
    if (Object.is(assistantOutput.content,null)){
        assistantOutput.content = "";
    }
    messages.push(assistantOutput);
    if (! ("tool_calls" in assistantOutput)) {
        console.log(`无需调用工具,我可以直接回复:${assistantOutput.content}`);
        rl.close();
    } else{
        while ("tool_calls" in assistantOutput) {
            let toolInfo = {};
            if (assistantOutput.tool_calls[0].function.name == "getCurrentWeather" ) {
                toolInfo = {"role": "tool"};
                let location = JSON.parse(assistantOutput.tool_calls[0].function.arguments)["location"];
                toolInfo["content"] = getCurrentWeather(location);
            } else if (assistantOutput.tool_calls[0].function.name == "getCurrentTime" ) {
                toolInfo = {"role":"tool"};
                toolInfo["content"] = getCurrentTime();
            }
            console.log(`工具输出信息:${JSON.stringify(toolInfo)}`);
            console.log("=".repeat(100));
            messages.push(toolInfo);
            assistantOutput = (await getResponse(messages)).choices[0].message;
            if (Object.is(assistantOutput.content,null)){
                assistantOutput.content = "";
            }
            messages.push(assistantOutput);
            i += 1;
            console.log(`第${i}轮大模型输出信息:${JSON.stringify(assistantOutput)}`)
    }
    console.log("=".repeat(100));
    console.log(`最终大模型输出信息:${JSON.stringify(assistantOutput.content)}`);
    rl.close();
    }});

返回结果

输入四个直辖市的天气如何?,输出结果为:

第1轮大模型输出信息:{"content":"","role":"assistant","tool_calls":[{"function":{"name":"getCurrentWeather","arguments":"{\"location\": \"北京市\"}"},"index":0,"id":"call_d2aff21240b24c7291db6d","type":"function"}]}
工具输出信息:{"role":"tool","content":"北京市今天是雨天。"}
====================================================================================================
第2轮大模型输出信息:{"content":"","role":"assistant","tool_calls":[{"function":{"name":"getCurrentWeather","arguments":"{\"location\": \"天津市\"}"},"index":0,"id":"call_bdcfa937e69b4eae997b5e","type":"function"}]}
工具输出信息:{"role":"tool","content":"天津市今天是雨天。"}
====================================================================================================
第3轮大模型输出信息:{"content":"","role":"assistant","tool_calls":[{"function":{"name":"getCurrentWeather","arguments":"{\"location\": \"上海市\"}"},"index":0,"id":"call_bbf22d017e8e439e811974","type":"function"}]}
工具输出信息:{"role":"tool","content":"上海市今天是雨天。"}
====================================================================================================
第4轮大模型输出信息:{"content":"","role":"assistant","tool_calls":[{"function":{"name":"getCurrentWeather","arguments":"{\"location\": \"重庆市\"}"},"index":0,"id":"call_f4f8e149af01492fb60162","type":"function"}]}
工具输出信息:{"role":"tool","content":"重庆市今天是雨天。"}
====================================================================================================
第5轮大模型输出信息:{"content":"所有四个直辖市(北京市、天津市、上海市、重庆市)今天的天气都是雨天。别忘了带伞!","role":"assistant"}
====================================================================================================
最终大模型输出信息:"所有四个直辖市(北京市、天津市、上海市、重庆市)今天的天气都是雨天。别忘了带伞!"

示例代码

import requests
import os
from datetime import datetime
import json

# 定义工具列表,模型在选择使用哪个工具时会参考工具的name和description
tools = [
    # 工具1 获取当前时刻的时间
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
            "parameters": {}  # 因为获取当前时间无需输入参数,因此parameters为空字典
        }
    },  
    # 工具2 获取指定城市的天气
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {  # 查询天气时需要提供位置,因此参数设置为location
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                    }
                }
            },
            "required": [
                "location"
            ]
        }
    }
]

# 模拟天气查询工具。返回结果示例:“北京今天是晴天。”
def get_current_weather(location):
    return f"{location}今天是晴天。 "

# 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“
def get_current_time():
    # 获取当前日期和时间
    current_datetime = datetime.now()
    # 格式化当前日期和时间
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # 返回格式化后的当前时间
    return f"当前时间:{formatted_time}。"

def get_response(messages):
    # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
    api_key = os.getenv("DASHSCOPE_API_KEY")
    url = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions'
    headers = {'Content-Type': 'application/json',
            'Authorization':f'Bearer {api_key}'}
    body = {
        'model': 'qwen-plus',
        "messages": messages,
        "tools":tools
    }

    response = requests.post(url, headers=headers, json=body)
    return response.json()


def call_with_messages():
    messages = [
            {
                "content": input('请输入:'),  # 提问示例:"现在几点了?" "一个小时后几点" "北京天气如何?"
                "role": "user"
            }
    ]
    
    # 模型的第一轮调用
    first_response = get_response(messages)
    print(f"\n第一轮调用结果:{first_response}")
    assistant_output = first_response['choices'][0]['message']
    if  assistant_output['content'] is None:
        assistant_output['content'] = ""
    messages.append(assistant_output)
    if 'tool_calls' not in assistant_output:  # 如果模型判断无需调用工具,则将assistant的回复直接打印出来,无需进行模型的第二轮调用
        print(f"最终答案:{assistant_output['content']}")
        return
    # 如果模型选择的工具是get_current_weather
    elif assistant_output['tool_calls'][0]['function']['name'] == 'get_current_weather':
        tool_info = {"name": "get_current_weather", "role":"tool"}
        location = json.loads(assistant_output['tool_calls'][0]['function']['arguments'])['location']
        tool_info['content'] = get_current_weather(location)
    # 如果模型选择的工具是get_current_time
    elif assistant_output['tool_calls'][0]['function']['name'] == 'get_current_time':
        tool_info = {"name": "get_current_time", "role":"tool"}
        tool_info['content'] = get_current_time()
    print(f"工具输出信息:{tool_info['content']}")
    messages.append(tool_info)

    # 模型的第二轮调用,对工具的输出进行总结
    second_response = get_response(messages)
    print(f"第二轮调用结果:{second_response}")
    print(f"最终答案:{second_response['choices'][0]['message']['content']}")

if __name__ == '__main__':
    call_with_messages()

返回结果

当输入:杭州天气时,程序会进行如下输出:

2024-07-16_14-43-10 (1)

以下是发起Function Call流程(模型的第一轮调用)时模型的返回信息。当输入“杭州天气”时,模型会返回tool_calls参数;当输入“你好”时,模型判断无需调用工具,模型不会返回tool_calls参数。

输入:杭州天气
输入:你好
{
    'choices': [
        {
            'message': {
                'content': '',
                'role': 'assistant',
                'tool_calls': [
                    {
                        'function': {
                            'name': 'get_current_weather',
                            'arguments': '{
                                "location": "杭州市"
                            }'
                        },
                        'index': 0,
                        'id': 'call_416cd81b8e7641edb654c4',
                        'type': 'function'
                    }
                ]
            },
            'finish_reason': 'tool_calls',
            'index': 0,
            'logprobs': None
        }
    ],
    'object': 'chat.completion',
    'usage': {
        'prompt_tokens': 217,
        'completion_tokens': 18,
        'total_tokens': 235
    },
    'created': 1726050222,
    'system_fingerprint': None,
    'model': 'qwen-max',
    'id': 'chatcmpl-61e30855-ee69-93ab-98d5-4194c51a9980'
}
{
    'choices': [
        {
            'message': {
                'content': '你好!有什么可以帮助你的吗?',
                'role': 'assistant'
            },
            'finish_reason': 'stop',
            'index': 0,
            'logprobs': None
        }
    ],
    'object': 'chat.completion',
    'usage': {
        'prompt_tokens': 216,
        'completion_tokens': 7,
        'total_tokens': 223
    },
    'created': 1726050238,
    'system_fingerprint': None,
    'model': 'qwen-max',
    'id': 'chatcmpl-2f2f86d1-bc4e-9494-baca-aac5b0555091'
}

您可以通过DashScope SDKHTTP方式调用通义千问模型,体验Function Call的功能。

Python
Java
HTTP

示例代码

import os
from dashscope import Generation
from datetime import datetime
import random
import json


# 定义工具列表,模型在选择使用哪个工具时会参考工具的name和description
tools = [
    # 工具1 获取当前时刻的时间
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
            "parameters": {}  # 因为获取当前时间无需输入参数,因此parameters为空字典
        }
    },
    # 工具2 获取指定城市的天气
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {
                # 查询天气时需要提供位置,因此参数设置为location
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                    }
                }
            },
            "required": [
                "location"
            ]
        }
    }
]


# 模拟天气查询工具。返回结果示例:“北京今天是晴天。”
def get_current_weather(location):
    return f"{location}今天是晴天。 "


# 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“
def get_current_time():
    # 获取当前日期和时间
    current_datetime = datetime.now()
    # 格式化当前日期和时间
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # 返回格式化后的当前时间
    return f"当前时间:{formatted_time}。"


# 封装模型响应函数
def get_response(messages):
    response = Generation.call(
        # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        model='qwen-plus',  # 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
        messages=messages,
        tools=tools,
        seed=random.randint(1, 10000),  # 设置随机数种子seed,如果没有设置,则随机数种子默认为1234
        result_format='message'  # 将输出设置为message形式
    )
    return response


def call_with_messages():
    print('\n')
    messages = [
            {
                "content": input('请输入:'),  # 提问示例:"现在几点了?" "一个小时后几点" "北京天气如何?"
                "role": "user"
            }
    ]
   
    # 模型的第一轮调用
    first_response = get_response(messages)
    assistant_output = first_response.output.choices[0].message
    print(f"\n大模型第一轮输出信息:{first_response}\n")
    messages.append(assistant_output)
    if 'tool_calls' not in assistant_output:  # 如果模型判断无需调用工具,则将assistant的回复直接打印出来,无需进行模型的第二轮调用
        print(f"最终答案:{assistant_output.content}")
        return
    # 如果模型选择的工具是get_current_weather
    elif assistant_output.tool_calls[0]['function']['name'] == 'get_current_weather':
        tool_info = {"name": "get_current_weather", "role":"tool"}
        location = json.loads(assistant_output.tool_calls[0]['function']['arguments'])['location']
        tool_info['content'] = get_current_weather(location)
    # 如果模型选择的工具是get_current_time
    elif assistant_output.tool_calls[0]['function']['name'] == 'get_current_time':
        tool_info = {"name": "get_current_time", "role":"tool"}
        tool_info['content'] = get_current_time()
    print(f"工具输出信息:{tool_info['content']}\n")
    messages.append(tool_info)

    # 模型的第二轮调用,对工具的输出进行总结
    second_response = get_response(messages)
    print(f"大模型第二轮输出信息:{second_response}\n")
    print(f"最终答案:{second_response.output.choices[0].message['content']}")

if __name__ == '__main__':
    call_with_messages()

返回结果

通过运行以上代码,您可以输入问题,得到在工具辅助条件下模型的输出结果。使用过程示例如下图所示:2024-04-29_11-22-10 (1).gif

以下是发起Function Call流程(模型的第一轮调用)时模型的返回信息。当输入“杭州天气”时,模型会返回tool_calls参数;当输入“你好”时,模型判断无需调用工具,模型不会返回tool_calls参数。

输入:杭州天气
输入:你好
{
  "status_code": 200,
  "request_id": "33cf0a53-ea38-9f47-8fce-b93b55d86573",
  "code": "",
  "message": "",
  "output": {
    "text": null,
    "finish_reason": null,
    "choices": [
      {
        "finish_reason": "tool_calls",
        "message": {
          "role": "assistant",
          "content": "",
          "tool_calls": [
            {
              "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"杭州市\"}"
              },
              "index": 0,
              "id": "call_9f62f52f3a834a8194f634",
              "type": "function"
            }
          ]
        }
      }
    ]
  },
  "usage": {
    "input_tokens": 217,
    "output_tokens": 18,
    "total_tokens": 235
  }
}
{
  "status_code": 200,
  "request_id": "4818ce03-e7c9-96de-a7bc-781649d98465",
  "code": "",
  "message": "",
  "output": {
    "text": null,
    "finish_reason": null,
    "choices": [
      {
        "finish_reason": "stop",
        "message": {
          "role": "assistant",
          "content": "你好!有什么可以帮助你的吗?"
        }
      }
    ]
  },
  "usage": {
    "input_tokens": 216,
    "output_tokens": 7,
    "total_tokens": 223
  }
}

示例代码

// Copyright (c) Alibaba, Inc. and its affiliates.
// version >= 2.12.0

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.alibaba.dashscope.aigc.conversation.ConversationParam.ResultFormat;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationOutput.Choice;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.tools.FunctionDefinition;
import com.alibaba.dashscope.tools.ToolCallBase;
import com.alibaba.dashscope.tools.ToolCallFunction;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.utils.JsonUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaVersion;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Scanner;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;


public class Main {

    public static class GetWeatherTool {
        private String location;

        public GetWeatherTool(String location) {
            this.location = location;
        }

        public String callWeather() {
            // 假设location是一个JSON字符串,例如{"location": "北京"}
            // 需要提取其中的"location"的值
            try {
                // 使用Jackson库解析JSON
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode jsonNode = objectMapper.readTree(location);
                String locationName = jsonNode.get("location").asText();
                return locationName + "今天是晴天";
            } catch (Exception e) {
                // 如果解析失败,返回原始字符串
                return location + "今天是晴天";
            }
        }
    }
    public static class GetTimeTool {
        public String getCurrentTime() {
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return "当前时间:" + now.format(formatter) + "。";
        }
    }
    private static ObjectNode generateSchema(Class<?> clazz) {
        SchemaGeneratorConfigBuilder configBuilder =
                new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
        SchemaGeneratorConfig config = configBuilder.with(Option.EXTRA_OPEN_API_FORMAT_VALUES)
                .without(Option.FLATTENED_ENUMS_FROM_TOSTRING).build();
        SchemaGenerator generator = new SchemaGenerator(config);
        return generator.generateSchema(clazz);
    }
    public static void selectTool()
            throws NoApiKeyException, ApiException, InputRequiredException {
        ObjectNode jsonSchemaWeather = generateSchema(GetWeatherTool.class);
        ObjectNode jsonSchemaTime = generateSchema(GetTimeTool.class);
        FunctionDefinition fdWeather = FunctionDefinition.builder().name("get_current_weather")
                .description("获取指定地区的天气")
                .parameters(JsonUtils.parseString(jsonSchemaWeather.toString()).getAsJsonObject()).build();
        FunctionDefinition fdTime = FunctionDefinition.builder().name("get_current_time")
                .description("获取当前时刻的时间")
                .parameters(JsonUtils.parseString(jsonSchemaTime.toString()).getAsJsonObject()).build();
        Message systemMsg = Message.builder().role(Role.SYSTEM.getValue())
                .content("You are a helpful assistant. When asked a question, use tools wherever possible.")
                .build();
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入:");
        String userInput = scanner.nextLine();
        Message userMsg = Message.builder().role(Role.USER.getValue()).content(userInput).build();
        List<Message> messages = new ArrayList<>(Arrays.asList(systemMsg, userMsg));
        GenerationParam param = GenerationParam.builder()
                // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
                .model("qwen-plus")
                // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .messages(messages).resultFormat(ResultFormat.MESSAGE)
                .tools(Arrays.asList(
                        ToolFunction.builder().function(fdWeather).build(),
                        ToolFunction.builder().function(fdTime).build()
                )).build();
        Generation gen = new Generation();
        GenerationResult result = gen.call(param);
        System.out.println("首轮输出:" + JsonUtils.toJson(result));
        boolean needToolCall = true;
        while (needToolCall) {
            needToolCall = false;
            for (Choice choice : result.getOutput().getChoices()) {
                messages.add(choice.getMessage());
                if (choice.getMessage().getToolCalls() != null) {
                    for (ToolCallBase toolCall : choice.getMessage().getToolCalls()) {
                        if (toolCall.getType().equals("function")) {
                            String functionName = ((ToolCallFunction) toolCall).getFunction().getName();
                            String functionArgument = ((ToolCallFunction) toolCall).getFunction().getArguments();
                            if (functionName.equals("get_current_weather")) {
                                GetWeatherTool weatherTool = new GetWeatherTool(functionArgument);
                                String weather = weatherTool.callWeather();
                                Message toolResultMessage = Message.builder().role("tool")
                                        .content(weather).toolCallId(toolCall.getId()).build();
                                messages.add(toolResultMessage);
                                System.out.println("工具输出信息:" + weather);
                            } else if (functionName.equals("get_current_time")) {
                                GetTimeTool timeTool = new GetTimeTool();
                                String time = timeTool.getCurrentTime();
                                Message toolResultMessage = Message.builder().role("tool")
                                        .content(time).toolCallId(toolCall.getId()).build();
                                messages.add(toolResultMessage);
                                System.out.println("工具输出信息:" + time);
                            }
                            needToolCall = true;
                        }
                    }
                } else {
                    System.out.println("最终答案:" + choice.getMessage().getContent());
                    return;
                }
            }

            if (needToolCall) {
                param.setMessages(messages);
                result = gen.call(param);
                System.out.println("下一轮输出:" + JsonUtils.toJson(result));
            }
        }

        System.out.println("最终答案:" + result.getOutput().getChoices().get(0).getMessage().getContent());
    }

    public static void main(String[] args) {
        try {
            selectTool();
        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
            System.out.println(String.format("Exception: %s", e.getMessage()));
        } catch (Exception e) {
            System.out.println(String.format("Exception: %s", e.getMessage()));
        }
        System.exit(0);
    }
}

返回结果

通过运行以上代码,您可以输入问题,得到在工具辅助条件下模型的输出结果。使用过程示例如下图所示:2024-07-17_10-20-07 (1)

以下是发起Function Call流程(模型的第一轮调用)时模型的返回信息。当输入“杭州天气”时,模型会返回tool_calls参数;当输入“你好”时,模型判断无需调用工具,模型不会返回tool_calls参数。

输入:杭州天气
输入:你好
{
    "requestId": "e2faa5cf-1707-973b-b216-36aa4ef52afc",
    "usage": {
        "input_tokens": 254,
        "output_tokens": 19,
        "total_tokens": 273
    },
    "output": {
        "choices": [
            {
                "finish_reason": "tool_calls",
                "message": {
                    "role": "assistant",
                    "content": "",
                    "tool_calls": [
                        {
                            "type": "function",
                            "id": "",
                            "function": {
                                "name": "get_current_whether",
                                "arguments": "{\"location\": \"杭州\"}"
                            }
                        }
                    ]
                }
            }
        ]
    }
}
{
    "requestId": "f6ca3828-3b5f-99bf-8bae-90b4aa88923f",
    "usage": {
        "input_tokens": 253,
        "output_tokens": 7,
        "total_tokens": 260
    },
    "output": {
        "choices": [
            {
                "finish_reason": "stop",
                "message": {
                    "role": "assistant",
                    "content": "你好!有什么可以帮助你的吗?"
                }
            }
        ]
    }
}

示例代码

Python
Java
import requests
import os
from datetime import datetime
import json

# 定义工具列表,模型在选择使用哪个工具时会参考工具的name和description
tools = [
    # 工具1 获取当前时刻的时间
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
            "parameters": {}  # 因为获取当前时间无需输入参数,因此parameters为空字典
        }
    },  
    # 工具2 获取指定城市的天气
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {  # 查询天气时需要提供位置,因此参数设置为location
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                    }
                }
            },
            "required": [
                "location"
            ]
        }
    }
]

# 模拟天气查询工具。返回结果示例:“北京今天是晴天。”
def get_current_weather(location):
    return f"{location}今天是晴天。 "

# 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“
def get_current_time():
    # 获取当前日期和时间
    current_datetime = datetime.now()
    # 格式化当前日期和时间
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # 返回格式化后的当前时间
    return f"当前时间:{formatted_time}。"

def get_response(messages):
    api_key = os.getenv("DASHSCOPE_API_KEY")
    url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation'
    headers = {'Content-Type': 'application/json',
            'Authorization':f'Bearer {api_key}'}
    body = {
        'model': 'qwen-plus',
        "input": {

                        "messages": messages
        },
        "parameters": {
            "result_format": "message",
            "tools": tools
        }
    }

    response = requests.post(url, headers=headers, json=body)
    return response.json()


def call_with_messages():
    messages = [
            {
                "content": input('请输入:'),  # 提问示例:"现在几点了?" "一个小时后几点" "北京天气如何?"
                "role": "user"
            }
    ]
    
    # 模型的第一轮调用
    first_response = get_response(messages)
    print(f"\n第一轮调用结果:{first_response}")
    assistant_output = first_response['output']['choices'][0]['message']
    messages.append(assistant_output)
    if 'tool_calls' not in assistant_output:  # 如果模型判断无需调用工具,则将assistant的回复直接打印出来,无需进行模型的第二轮调用
        print(f"最终答案:{assistant_output['content']}")
        return
    # 如果模型选择的工具是get_current_weather
    elif assistant_output['tool_calls'][0]['function']['name'] == 'get_current_weather':
        tool_info = {"name": "get_current_weather", "role":"tool"}
        location = json.loads(assistant_output['tool_calls'][0]['function']['arguments'])['location']
        tool_info['content'] = get_current_weather(location)
    # 如果模型选择的工具是get_current_time
    elif assistant_output['tool_calls'][0]['function']['name'] == 'get_current_time':
        tool_info = {"name": "get_current_time", "role":"tool"}
        tool_info['content'] = get_current_time()
    print(f"工具输出信息:{tool_info['content']}")
    messages.append(tool_info)

    # 模型的第二轮调用,对工具的输出进行总结
    second_response = get_response(messages)
    print(f"第二轮调用结果:{second_response}")
    print(f"最终答案:{second_response['output']['choices'][0]['message']['content']}")

if __name__ == '__main__':
    call_with_messages()
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.json.JSONArray;
import org.json.JSONObject;

public class Main {
    private static final String userAGENT = "Java-HttpURLConnection/1.0";
    public static void main(String[] args) throws Exception {
        // 用户输入问题
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入:");
        String UserInput = scanner.nextLine();
        // 初始化messages
        JSONArray messages = new JSONArray();
        // 定义系统信息system_message
        JSONObject systemMessage = new JSONObject();
        systemMessage.put("role","system");
        systemMessage.put("content","You are a helpful assistant.");
        // 根据用户的输入构造user_message
        JSONObject userMessage = new JSONObject();
        userMessage.put("role","user");
        userMessage.put("content",UserInput);
        // 将system_message和user_message依次添加到messages中
        messages.put(systemMessage);
        messages.put(userMessage);
        // 进行模型的第一轮调用,并打印出结果
        JSONObject responseJson = getResponse(messages);
        System.out.println("第一轮调用结果:"+responseJson);
        // 获取助手信息assistant_message
        JSONObject assistantMessage = responseJson.getJSONObject("output").getJSONArray("choices").getJSONObject(0).getJSONObject("message");
        // 初始化工具信息tool_message
        JSONObject toolMessage = new JSONObject();

        // 如果assistant_message没有tool_calls参数,则直接打印出assistant_message中的响应信息并返回
        if (! assistantMessage.has("tool_calls")){
            System.out.println("最终答案:"+assistantMessage.get("content"));
            return;
        }
        // 如果assistant_message有tool_calls参数,说明模型判断需要调用工具
        else {
            // 将assistant_message添加到messages中
            messages.put(assistantMessage);
            // 如果模型判断需要调用get_current_weather函数
            if (assistantMessage.getJSONArray("tool_calls").getJSONObject(0).getJSONObject("function").getString("name").equals("get_current_weather")) {
                // 获取参数arguments信息,并提取出location参数
                JSONObject argumentsJson = new JSONObject(assistantMessage.getJSONArray("tool_calls").getJSONObject(0).getJSONObject("function").getString("arguments"));
                String location = argumentsJson.getString("location");
                // 运行工具函数,得到工具的输出,并打印
                String toolOutput = getCurrentWeather(location);
                System.out.println("工具输出信息:"+toolOutput);
                // 构造tool_message信息
                toolMessage.put("name","get_current_weather");
                toolMessage.put("role","tool");
                toolMessage.put("content",toolOutput);
            }
            // 如果模型判断需要调用get_current_time函数
            if (assistantMessage.getJSONArray("tool_calls").getJSONObject(0).getJSONObject("function").getString("name").equals("get_current_time")) {
                // 运行工具函数,得到工具的输出,并打印
                String toolOutput = getCurrentTime();
                System.out.println("工具输出信息:"+toolOutput);
                // 构造tool_message信息
                toolMessage.put("name","get_current_time");
                toolMessage.put("role","tool");
                toolMessage.put("content",toolOutput);
            }
        }
        // 将tool_message添加到messages中
        messages.put(toolMessage);
        // 进行模型的第二轮调用,并打印出结果
        JSONObject secondResponse = getResponse(messages);
        System.out.println("第二轮调用结果:"+secondResponse);
        System.out.println("最终答案:"+secondResponse.getJSONObject("output").getJSONArray("choices").getJSONObject(0).getJSONObject("message").getString("content"));
    }
    // 定义获取天气的函数
    public static String getCurrentWeather(String location) {
        return location+"今天是晴天";
    }
    // 定义获取当前时间的函数
    public static String getCurrentTime() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String currentTime = "当前时间:" + now.format(formatter) + "。";
        return currentTime;
    }
    // 封装模型响应函数,输入:messages,输出:json格式化后的http响应
    public static JSONObject getResponse(JSONArray messages) throws Exception{
        // 初始化工具库
        JSONArray tools = new JSONArray();
        // 定义工具1:获取当前时间
        String jsonStringTime = "{\"type\": \"function\", \"function\": {\"name\": \"get_current_time\", \"description\": \"当你想知道现在的时间时非常有用。\", \"parameters\": {}}}";
        JSONObject getCurrentTimeJson = new JSONObject(jsonStringTime);
        // 定义工具2:获取指定地区天气
        String jsonString_weather = "{\"type\": \"function\", \"function\": {\"name\": \"get_current_weather\", \"description\": \"当你想查询指定城市的天气时非常有用。\", \"parameters\": {\"type\": \"object\", \"properties\": {\"location\": {\"type\": \"string\", \"description\": \"城市或县区,比如北京市、杭州市、余杭区等。\"}}}, \"required\": [\"location\"]}}";
        JSONObject getCurrentWeatherJson = new JSONObject(jsonString_weather);
        // 将两个工具添加到工具库中
        tools.put(getCurrentTimeJson);
        tools.put(getCurrentWeatherJson);
        String toolsString = tools.toString();
        // 接口调用URL
        String urlStr = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
        // 通过环境变量获取DASHSCOPE_API_KEY
        String apiKey = System.getenv("DASHSCOPE_API_KEY");

        URL url = new URL(urlStr);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        // 定义请求头信息
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Authorization", "Bearer " + apiKey);
        connection.setDoOutput(true);
        // 定义请求体信息
        String jsonInputString = String.format("{\"model\": \"qwen-max\", \"input\": {\"messages\":%s}, \"parameters\": {\"result_format\": \"message\",\"tools\":%s}}",messages.toString(),toolsString);

        // 获取http响应response
        try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
            wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
            wr.flush();
        }
        StringBuilder response = new StringBuilder();
        try (BufferedReader in = new BufferedReader(
                new InputStreamReader(connection.getInputStream()))) {
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
        }
        connection.disconnect();
        // 返回json格式化后的response
        return new JSONObject(response.toString());
    }
}

返回结果

当输入:杭州天气时,程序会进行如下输出:

2024-07-16_14-07-04 (1)

以下是发起Function Call流程(模型的第一轮调用)时模型的返回信息。当输入“杭州天气”时,模型会返回tool_calls参数;当输入“你好”时,模型判断无需调用工具,模型不会返回tool_calls参数。

输入:杭州天气
输入:你好
{
    'output': {
        'choices': [
            {
                'finish_reason': 'tool_calls',
                'message': {
                    'role': 'assistant',
                    'tool_calls': [
                        {
                            'function': {
                                'name': 'get_current_weather',
                                'arguments': '{
                                    "location": "杭州市"
                                }'
                            },
                            'index': 0,
                            'id': 'call_240d6341de4c484384849d',
                            'type': 'function'
                        }
                    ],
                    'content': ''
                }
            }
        ]
    },
    'usage': {
        'total_tokens': 235,
        'output_tokens': 18,
        'input_tokens': 217
    },
    'request_id': '235ed6a4-b6c0-9df0-aa0f-3c6dce89f3bd'
}
{
    'output': {
        'choices': [
            {
                'finish_reason': 'stop',
                'message': {
                    'role': 'assistant',
                    'content': '你好!有什么可以帮助你的吗?'
                }
            }
        ]
    },
    'usage': {
        'total_tokens': 223,
        'output_tokens': 7,
        'input_tokens': 216
    },
    'request_id': '42c42853-3caf-9815-96e8-9c950f4c26a0'
}

错误码

如果模型调用失败并返回报错信息,请参见错误信息进行解决。

  • 本页导读 (1)
  • 支持的模型
  • 概述
  • 前提条件
  • 如何使用
  • 1. 定义工具
  • 2. 创建messages数组
  • 3. 发起 Function Calling
  • 4. 运行工具函数
  • 5. 大模型总结工具函数输出(可选)
  • 进阶用法
  • 流式输出
  • 指定工具调用方式
  • 如何计费
  • 完整代码
  • 错误码