All Products
Search
Document Center

Alibaba Cloud Model Studio:Function calling

Last Updated:Dec 05, 2025

Models can struggle with tasks such as answering real-time questions or performing mathematical calculations. Function calling solves this problem by introducing external tools. This allows the model to answer questions that it otherwise could not.

How it works

Function calling enables a model to reference external tool information for its answers through a multi-step interaction between your application and the model.

  1. Initiate the first model call

    Your application sends a request to the model. The request includes the user's question and a list of tools the model can call.

  2. Receive the model's tool calling instruction

    If the model decides to call an external tool, it returns an instruction in JSON format. This instruction tells your application which function to run and which input parameters to use.

    If the model decides not to call a tool, it returns a natural language response.
  3. Run the tool in your application

    After your application receives the tool instruction, you can run the tool to obtain the output.

  4. Initiate the second model call

    After you obtain the tool's output, you can add it to the model's context (messages) and call the model again.

  5. Receive the final response from the model

    The model combines the tool's output with the user's question to generate a final response in natural language.

The following figure shows the workflow.

image

Model availability

Qwen

DeepSeek

  • deepseek-v3.2

  • deepseek-v3.2-exp (non-thinking mode)

  • deepseek-v3.1 (non-thinking mode)

  • deepseek-r1

  • deepseek-r1-0528

  • deepseek-v3

Kimi

  • kimi-k2-thinking

  • Moonshot-Kimi-K2-Instruct

Getting started

You must first create an API key and export the API key as an environment variable. If you use the OpenAI SDK or DashScope SDK to make calls, install the SDK.

This section uses a weather query scenario to demonstrate how to quickly use function calling.

OpenAI compatible

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

client = OpenAI(
    # If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
    # If you have not configured the environment variable, replace the following line with: api_key="sk-xxx", using your model Studio API key.
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # If you use a model in the China (Beijing) region, replace the base_url with: https://dashscope.aliyuncs.com/compatible-mode/v1
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",  
)
# Simulate a user question
USER_QUESTION = "What's the weather like in Singapore?"
# Define the tool list
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or county, such as Singapore or New York.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]


# Simulate a weather query tool
def get_current_weather(arguments):
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"Today in {location} it is {random_weather}."


# Encapsulate the model response function
def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen-plus",
        messages=messages,
        tools=tools,
    )
    return completion


messages = [{"role": "user", "content": USER_QUESTION}]
response = get_response(messages)
assistant_output = response.choices[0].message
if assistant_output.content is None:
    assistant_output.content = ""
messages.append(assistant_output)
# If no tool call is needed, output the content directly
if assistant_output.tool_calls is None:
    print(f"No need to call the weather query tool. Direct reply: {assistant_output.content}")
else:
    # Enter the tool calling loop
    while assistant_output.tool_calls is not None:
        tool_call = assistant_output.tool_calls[0]
        tool_call_id = tool_call.id
        func_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        print(f"Calling tool [{func_name}] with arguments: {arguments}")
        # Run the tool
        tool_result = get_current_weather(arguments)
        # Construct the tool return message
        tool_message = {
            "role": "tool",
            "tool_call_id": tool_call_id,
            "content": tool_result,  # Keep the original tool output
        }
        print(f"Tool returns: {tool_message['content']}")
        messages.append(tool_message)
        # Call the model again to get a summarized natural language reply
        response = get_response(messages)
        assistant_output = response.choices[0].message
        if assistant_output.content is None:
            assistant_output.content = ""
        messages.append(assistant_output)
    print(f"Assistant's final reply: {assistant_output.content}")
import OpenAI from 'openai';  
  
// Initialize the client  
const openai = new OpenAI({  
  apiKey: process.env.DASHSCOPE_API_KEY,  
  // If you use a model in the China (Beijing) region, replace the baseURL with: https://dashscope.aliyuncs.com/compatible-mode/v1
  baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",  
});  
  
// Define the tool list  
const tools = [  
  {  
    type: "function",  
    function: {  
      name: "get_current_weather",  
      description: "This is useful when you want to query the weather in a specific city.",  
      parameters: {  
        type: "object",  
        properties: {  
          location: {  
            type: "string",  
            description: "A city or county, such as Singapore or New York.",  
          },  
        },  
        required: ["location"],  
      },  
    },  
  },  
];  
  
// Simulate a weather query tool  
const getCurrentWeather = (args) => {  
  const weatherConditions = ["Sunny", "Cloudy", "Rainy"];  
  const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];  
  const location = args.location;  
  return `Today in ${location} it is ${randomWeather}.`;  
};  
  
// Encapsulate the model response function  
const getResponse = async (messages) => {  
  const response = await openai.chat.completions.create({  
    model: "qwen-plus",  
    messages: messages,  
    tools: tools,  
  });  
  return response;  
};  

const main = async () => {  
  const input = "What's the weather like in Singapore?";

  let messages = [  
    {  
      role: "user",  
      content: input,  
    }  
  ];  
  let response = await getResponse(messages);  
  let assistantOutput = response.choices[0].message;  
  // Make sure content is not null  
  if (!assistantOutput.content) assistantOutput.content = "";  
  messages.push(assistantOutput);  
  // Determine whether to call a tool  
  if (!assistantOutput.tool_calls) {  
    console.log(`No need to call the weather query tool. Direct reply: ${assistantOutput.content}`);  
  } else {  
    // Enter the tool calling loop  
    while (assistantOutput.tool_calls) {  
      const toolCall = assistantOutput.tool_calls[0];  
      const toolCallId = toolCall.id;  
      const funcName = toolCall.function.name;  
      const funcArgs = JSON.parse(toolCall.function.arguments);  
      console.log(`Calling tool [${funcName}] with arguments:`, funcArgs);  
      // Run the tool  
      const toolResult = getCurrentWeather(funcArgs);  
      // Construct the tool return message  
      const toolMessage = {  
        role: "tool",  
        tool_call_id: toolCallId,  
        content: toolResult,  
      };  
      console.log(`Tool returns: ${toolMessage.content}`);  
      messages.push(toolMessage);  
      // Call the model again to get a natural language summary  
      response = await getResponse(messages);  
      assistantOutput = response.choices[0].message;  
      if (!assistantOutput.content) assistantOutput.content = "";  
      messages.push(assistantOutput);  
    }  
    console.log(`Assistant's final reply: ${assistantOutput.content}`);  
  }  
};  
  
// Start the program  
main().catch(console.error);

DashScope

import os
from dashscope import Generation
import dashscope
import json
import random
# If you use a model in the China (Beijing) region, replace base_http_api_url with: https://dashscope.aliyuncs.com/api/v1
dashscope.base_http_api_url = 'https://dashscope-intl.aliyuncs.com/api/v1'

# 1. Define the tool list
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or county, such as Singapore or New York.",
                    }
                },
                "required": ["location"],
            },
        },
    }
]

# 2. Simulate a weather query tool
def get_current_weather(arguments):
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"Today in {location} it is {random_weather}."

# 3. Encapsulate the model response function
def get_response(messages):
    response = Generation.call(
        # If you have not configured the environment variable, replace the following line with api_key="sk-xxx"
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        model="qwen-plus",
        messages=messages,
        tools=tools,
        result_format="message",
    )
    return response

# 4. Initialize the conversation history
messages = [
    {
        "role": "user",
        "content": "What's the weather like in Singapore?"
    }
]

# 5. Call the model for the first time
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)

# 6. Determine if a tool call is needed
if "tool_calls" not in assistant_output or not assistant_output["tool_calls"]:
    print(f"No tool call needed. Direct reply: {assistant_output['content']}")
else:
    # 7. Enter the tool calling loop
    # Loop condition: As long as the latest model response includes a tool call request
    while "tool_calls" in assistant_output and assistant_output["tool_calls"]:
        tool_call = assistant_output["tool_calls"][0]
        # Parse the tool call information
        func_name = tool_call["function"]["name"]
        arguments = json.loads(tool_call["function"]["arguments"])
        tool_call_id = tool_call.get("id")  # Get the tool_call_id
        print(f"Calling tool [{func_name}] with arguments: {arguments}")
        # Run the corresponding tool function
        tool_result = get_current_weather(arguments)
        # Construct the tool return message
        tool_message = {
            "role": "tool",
            "content": tool_result,
            "tool_call_id": tool_call_id
        }
        print(f"Tool returns: {tool_message['content']}")
        messages.append(tool_message)
        # Call the model again to get a response based on the tool result
        response = get_response(messages)
        assistant_output = response.output.choices[0].message
        messages.append(assistant_output)
    # 8. Output the final natural language reply
    print(f"Assistant's final reply: {assistant_output['content']}")
import com.alibaba.dashscope.aigc.generation.Generation;
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.protocol.Protocol;
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.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class Main {

    /**
     * Define the local implementation of the tool.
     * @param arguments A JSON string from the model containing the required parameters for the tool.
     * @return A string with the result of the tool's execution.
     */
    public static String getCurrentWeather(String arguments) {
        try {
            // The parameters provided by the model are in JSON format and need to be parsed manually.
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode argsNode = objectMapper.readTree(arguments);
            String location = argsNode.get("location").asText();

            // Use a random result to simulate a real API call or business logic.
            List<String> weatherConditions = Arrays.asList("Sunny", "Cloudy", "Rainy");
            String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size()));

            return "Today in " + location + " it is " + randomWeather + ".";
        } catch (Exception e) {
            // Exception handling to ensure program robustness.
            return "Failed to parse location parameter.";
        }
    }

    public static void main(String[] args) {
        try {
            // Describe (register) our tool to the model.
            String weatherParamsSchema =
                    "{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"A city or county, such as Singapore or New York.\"}},\"required\":[\"location\"]}";

            FunctionDefinition weatherFunction = FunctionDefinition.builder()
                    .name("get_current_weather") // The unique identifier of the tool, which must correspond to the local implementation.
                    .description("This is useful when you want to query the weather in a specific city.") // A clear description helps the model better decide when to use the tool.
                    .parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject())
                    .build();
            // If you use a model in the China (Beijing) region, replace the URL with: https://dashscope.aliyuncs.com/api/v1        
            Generation gen = new Generation(Protocol.HTTP.getValue(), "https://dashscope-intl.aliyuncs.com/api/v1");
            String userInput = "What's the weather like in Singapore?";

            List<Message> messages = new ArrayList<>();
            messages.add(Message.builder().role(Role.USER.getValue()).content(userInput).build());

            // Call the model for the first time. Send the user's request and the defined tool list to the model.
            GenerationParam param = GenerationParam.builder()
                    .model("qwen-plus") // Specify the model to call.
                    .apiKey(System.getenv("DASHSCOPE_API_KEY")) // Get the API key from the environment variable.
                    .messages(messages) // Pass in the current conversation history.
                    .tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // Pass in the list of available tools.
                    .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                    .build();

            GenerationResult result = gen.call(param);
            Message assistantOutput = result.getOutput().getChoices().get(0).getMessage();
            messages.add(assistantOutput); // Add the model's first reply to the conversation history as well.

            // Check the model's reply to determine if it requests a tool call.
            if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
                // Case A: The model does not call a tool, but provides a direct answer.
                System.out.println("No need to call the weather query tool. Direct reply: " + assistantOutput.getContent());
            } else {
                // Case B: The model decides to call a tool.
                // Use a while loop to handle scenarios where the model calls tools multiple times in a row.
                while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) {
                    ToolCallBase toolCall = assistantOutput.getToolCalls().get(0);

                    // Parse the specific information of the tool call (function name, parameters) from the model's reply.
                    ToolCallFunction functionCall = (ToolCallFunction) toolCall;
                    String funcName = functionCall.getFunction().getName();
                    String arguments = functionCall.getFunction().getArguments();
                    System.out.println("Calling tool [" + funcName + "] with arguments: " + arguments);

                    // Run the corresponding Java method locally based on the tool name.
                    String toolResult = getCurrentWeather(arguments);

                    // Construct a message with role "tool" that contains the tool's execution result.
                    Message toolMessage = Message.builder()
                            .role("tool")
                            .toolCallId(toolCall.getId())
                            .content(toolResult)
                            .build();
                    System.out.println("Tool returns: " + toolMessage.getContent());
                    messages.add(toolMessage); // Add the tool's return result to the conversation history as well.

                    // Call the model again.
                    param.setMessages(messages);
                    result = gen.call(param);
                    assistantOutput = result.getOutput().getChoices().get(0).getMessage();
                    messages.add(assistantOutput);
                }

                // Print the final reply generated by the model after summarization.
                System.out.println("Assistant's final reply: " + assistantOutput.getContent());
            }

        } catch (NoApiKeyException | InputRequiredException e) {
            System.err.println("Error: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The following output is returned:

Calling tool [get_current_weather] with arguments: {'location': 'Singapore'}
Tool returns: Today in Singapore it is Cloudy.
Assistant's final reply: Today in Singapore, the weather is cloudy.

Implementation guide

Function calling supports two methods for passing tool information:

  • Method 1: Pass the information through the `tools` parameter (Recommended)

    For more information, see Implementation guide. You can invoke the function by defining tools, creating the messages array, initiating function calling, running tool functions, and having the model summarize the tool function outputs.

  • Method 2: Pass the information through a System Message

    When you use the `tools` parameter, the server-side automatically adapts and assembles a suitable prompt template based on the model. Therefore, we recommend that you use the `tools` parameter. If you do not want to use the `tools` parameter with the Qwen model, see Pass tool information using a system message.

The following sections use the OpenAI compatible calling method as an example. They demonstrate how to use function calling step-by-step by passing tool information through the `tools` parameter.

Assume that your business scenario receives two types of questions: weather queries and time queries.

1. Define tools

Tools are the bridge between Models and the outside world. First, you must define the tools.

1.1. Create tool functions

You can create two tool functions: a weather query tool and a time query tool.

  • Weather query tool

    This tool accepts the arguments parameter. The format of arguments is {"location": "query_location"}. The tool's output is a string in the format "Today in {location} it is {weather}".

    For demonstration purposes, the weather query tool defined here does not actually query the weather. It randomly selects from Sunny, Cloudy, or Rainy. In a real business scenario, you can replace this with a tool such as AMap Weather Query.
  • Time query tool

    The time query tool does not require any input parameters. The tool's output is a string in the format "Current time: {queried_time}.".

    If you use Node.js, you can run npm install date-fns to install the date-fns package for obtaining the time.
## Step 1: Define tool functions

# Add import for the random module
import random
from datetime import datetime

# Simulate a weather query tool. Example output: "Today in Beijing it is Rainy."
def get_current_weather(arguments):
    # Define a list of alternative weather conditions
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    # Randomly select a weather condition
    random_weather = random.choice(weather_conditions)
    # Extract location information from JSON
    location = arguments["location"]
    # Return formatted weather information
    return f"Today in {location} it is {random_weather}."

# Tool to query the current time. Example output: "Current time: 2024-04-15 17:15:18."
def get_current_time():
    # Get the current date and time
    current_datetime = datetime.now()
    # Format the current date and time
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # Return the formatted current time
    return f"Current time: {formatted_time}."

# Test the tool functions and print the results. You can remove the following four lines of test code when running subsequent steps.
print("Testing tool output:")
print(get_current_weather({"location": "Shanghai"}))
print(get_current_time())
print("\n")
// Step 1: Define tool functions

// Import the time query tool
import { format } from 'date-fns';

function getCurrentWeather(args) {
    // Define a list of alternative weather conditions
    const weatherConditions = ["Sunny", "Cloudy", "Rainy"];
    // Randomly select a weather condition
    const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
    // Extract location information from JSON
    const location = args.location;
    // Return formatted weather information
    return `Today in ${location} it is ${randomWeather}.`;
}

function getCurrentTime() {
    // Get the current date and time
    const currentDatetime = new Date();
    // Format the current date and time
    const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
    // Return the formatted current time
    return `Current time: ${formattedTime}.`;
}

// Test the tool functions and print the results. You can remove the following four lines of test code when running subsequent steps.
console.log("Testing tool output:")
console.log(getCurrentWeather({location:"Shanghai"}));
console.log(getCurrentTime());
console.log("\n")

After you run the tool, you will see the following output:

Testing tool output:
Today in Shanghai it is Cloudy.
Current time: 2025-01-08 20:21:45.

1.2 Create the tools array

Before humans choose a tool, they need a comprehensive understanding of it, including its function, when to use it, and its input parameters. Models also need this information to select tools more accurately. You can provide the tool information in the following JSON format.

  • The type field is fixed to "function".

  • The function field is of type Object.

    • The name field is a custom tool function name. We recommend using the same name as the function, such as get_current_weather or get_current_time.

    • The description field describes the function of the tool. The model refers to this field to decide whether to use the tool function.

    • The parameters field describes the input parameters of the tool function. It is of type Object. The model refers to this field to extract input parameters. If the tool function does not require input parameters, you do not need to specify the parameters parameter.

      • The type field is fixed to "object".

      • The properties field describes the name, data type, and description of the input parameters. It is of type Object. The key is the name of the input parameter, and the value is its data type and description.

      • The required field specifies which parameters are required. It is of type Array.

For the weather query tool, the tool description information is in the following format:

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "This is useful when you want to query the weather in a specific city.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                }
            },
            "required": ["location"]
        }
    }
}

Before you initiate function calling, you must define a tool information array (`tools`) in your code. This array contains the function name, description, and parameter definition for each tool. This array is passed as a parameter in the subsequent function calling request.

# Paste the following code after the code from Step 1

## Step 2: Create the tools array

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District.",
                    }
                },
                "required": ["location"]
            }
        }
    }
]
tool_name = [tool["function"]["name"] for tool in tools]
print(f"Created {len(tools)} tools: {tool_name}\n")
// Paste the following code after the code from Step 1

// Step 2: Create the tools array

const tools = [
    {
      type: "function",
      function: {
        name: "get_current_time",
        description: "This is useful when you want to know the current time.",
        parameters: {}
      }
    },
    {
      type: "function",
      function: {
        name: "get_current_weather",
        description: "This is useful when you want to query the weather in a specific city.",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "A city or district, such as Beijing, Hangzhou, or Yuhang District.",
            }
          },
          required: ["location"]
        }
      }
    }
  ];
  
const toolNames = tools.map(tool => tool.function.name);
console.log(`Created ${tools.length} tools: ${toolNames.join(', ')}\n`);

2. Create the messages array

Function calling uses the `messages` array to pass instructions and context information to the model. Before you initiate function calling, the `messages` array needs to include a system message and a user message.

System message

Although the purpose of the tools and when to use them are described when you create the tools array, emphasizing when to call the tools in the system message usually improves the accuracy of tool calling. In the current scenario, you can set the system prompt to the following:

You are a helpful assistant. If the user asks about the weather, call the 'get_current_weather' function.
If the user asks about the time, call the 'get_current_time' function.
Please answer the questions in a friendly tone.

User message

The user message is used to pass the user's question. Assuming that the user asks "Shanghai weather", the `messages` array would be as follows:

# Step 3: Create the messages array
# Paste the following code after the code from Step 2
# Example of a User Message for a text generation model
messages = [
    {
        "role": "system",
        "content": """You are a helpful assistant. If the user asks about the weather, call the 'get_current_weather' function.
     If the user asks about the time, call the 'get_current_time' function.
     Please answer the questions in a friendly tone.""",
    },
    {
        "role": "user",
        "content": "Shanghai weather"
    }
]

# Example of a User Message for the Qwen3-VL model
# messages=[
#  {
#         "role": "system",
#         "content": """You are a helpful assistant. If the user asks about the weather, call the 'get_current_weather' function.
#      If the user asks about the time, call the 'get_current_time' function.
#      Please answer the questions in a friendly tone.""",
#     },
#     {"role": "user",
#      "content": [{"type": "image_url","image_url": {"url": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"}},
#                  {"type": "text", "text": "Query the current weather for the location in the image"}]},
# ]

print("messages array created\n") 
// Step 3: Create the messages array
// Paste the following code after the code from Step 2
const messages = [
    {
        role: "system",
        content: "You are a helpful assistant. If the user asks about the weather, call the 'get_current_weather' function. If the user asks about the time, call the 'get_current_time' function. Please answer the questions in a friendly tone.",
    },
    {
        role: "user",
        content: "Shanghai weather"
    }
];
// Example of a User Message for the Qwen3-VL model,
// const messages: [{
//     role: "user",
//     content: [{type: "image_url", image_url: {"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"}},
//               {type: "text", text: "What is depicted in the image?"}]
//   }];

console.log("messages array created\n");
Because the available tools include weather and time queries, you can also ask about the current time.

3. Initiate function calling

You can pass the created tools and messages to the model to initiate a function call. The model determines whether to call a tool. If it does, it returns the function name and parameters of that tool.

For a list of supported models, see Model availability.
# Step 4: Initiate function calling
# Paste the following code after the code from Step 3
from openai import OpenAI
import os

client = OpenAI(
    # If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
    # If you have not configured the environment variable, replace the following line with: api_key="sk-xxx", using your model Studio API key.
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # If you use a model in the China (Beijing) region, replace the base_url with: https://dashscope.aliyuncs.com/compatible-mode/v1
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

def function_calling():
    completion = client.chat.completions.create(
        # This example uses qwen-plus. You can change the model name as needed. For a list of models, see https://www.alibabacloud.com/help/en/model-studio/getting-started/models
        model="qwen-plus",
        messages=messages,
        tools=tools
    )
    print("Returned object:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("Initiating function calling...")
completion = function_calling()
// Step 4: Initiate function calling
// Paste the following code after the code from Step 3
import OpenAI from "openai";
const openai = new OpenAI(
    {
        // If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
        // If you have not configured the environment variable, replace the following line with: apiKey: "sk-xxx", using your model Studio API key.
        apiKey: process.env.DASHSCOPE_API_KEY,
        // If you use a model in the China (Beijing) region, replace the baseURL with: https://dashscope.aliyuncs.com/compatible-mode/v1
        baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
    }
);

async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen-plus",  // This example uses qwen-plus. You can change the model name as needed. For a list of models, see https://www.alibabacloud.com/help/en/model-studio/getting-started/models
        messages: messages,
        tools: tools
    });
    console.log("Returned object:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

Because the user asked about the weather in Shanghai, the model specifies that the tool function to be used is named "get_current_weather", and the function's input parameter is "{\"location\": \"Shanghai\"}".

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

Note that if the model determines that no tool is needed for the question, it replies directly through the content parameter. When you input "Hello", the tool_calls parameter is empty, and the returned object is in the following format:

{
    "content": "Hello! How can I help you? I'm particularly good at answering questions about weather or time.",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}
If the tool_calls parameter is empty, your program can directly return the content without running the following steps.
If you want the model to always select a specific tool after each function calling, see Forced tool calling.

4. Run the tool function

Running the tool function is the key step that translates the model's decision into an actual operation.

The process of running the tool function is completed by your computing environment, not by the model.

Because the model can only output content in string format, you need to parse the string-formatted tool function and input parameters separately before you run the tool function.

  • Tool function

    You can create a mapping function_mapper from the tool function name to the tool function entity. This maps the returned tool function string to the tool function entity.

  • Input parameters

    The input parameters returned by function calling are a JSON string. You can use a tool to parse it into a JSON object to extract the input parameter information.

After parsing, you can pass the parameters to the tool function and run it to obtain the output result.

# Step 5: Run the tool function
# Paste the following code after the code from Step 4
import json

print("Running the tool function...")
# Get the function name and input parameters from the returned result
function_name = completion.choices[0].message.tool_calls[0].function.name
arguments_string = completion.choices[0].message.tool_calls[0].function.arguments

# Use the json module to parse the parameter string
arguments = json.loads(arguments_string)
# Create a function mapping table
function_mapper = {
    "get_current_weather": get_current_weather,
    "get_current_time": get_current_time
}
# Get the function entity
function = function_mapper[function_name]
# If the input parameters are empty, call the function directly
if arguments == {}:
    function_output = function()
# Otherwise, pass the parameters and then call the function
else:
    function_output = function(arguments)
# Print the tool's output
print(f"Tool function output: {function_output}\n")
// Step 5: Run the tool function
// Paste the following code after the code from Step 4

console.log("Running the tool function...");
const function_name = completion.choices[0].message.tool_calls[0].function.name;
const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments;

// Use the JSON module to parse the parameter string
const args = JSON.parse(arguments_string);

// Create a function mapping table
const functionMapper = {
    "get_current_weather": getCurrentWeather,
    "get_current_time": getCurrentTime
};

// Get the function entity
const func = functionMapper[function_name];

// If the input parameters are empty, call the function directly
let functionOutput;
if (Object.keys(args).length === 0) {
    functionOutput = func();
} else {
    // Otherwise, pass the parameters and then call the function
    functionOutput = func(args);
}

// Print the tool's output
console.log(`Tool function output: ${functionOutput}\n`);

The following output is returned:

Today in Shanghai it is Cloudy.
Note

In real business scenarios, the core function of many tools is to perform specific operations, such as sending emails or uploading files, rather than querying data. These tools do not output a string after execution. To help the model understand the tool's running status, we recommend that you add status descriptions such as "Email sent successfully" or "Operation failed" when you design such tools.

5. The model summarizes the tool function output

The output format of a tool function is relatively fixed. If it is returned directly to the user, it might sound stiff and inflexible. If you want the model to generate a natural language reply by combining the user input and the tool output, you can submit the tool output to the model's context and send another request to the model.

  1. Add assistant message

    After you initiate function calling, you can retrieve the assistant message through completion.choices[0].message. First, add it to the `messages` array.

  2. Add tool message

    You can add the tool's output to the messages array as {"role": "tool", "content": "the tool's output","tool_call_id": completion.choices[0].message.tool_calls[0].id}.

    Note
    • Make sure that the tool's output is in string format.

    • tool_call_id is a unique identifier generated by the system for each tool call request. The model may request to call multiple tools at once. When you return multiple tool results to the model, tool_call_id ensures that the output of a tool can be matched with its calling intent.

# Step 6: Submit the tool output to the Model
# Paste the following code after the code from Step 5

messages.append(completion.choices[0].message)
print("Assistant message added")
messages.append({"role": "tool", "content": function_output, "tool_call_id": completion.choices[0].message.tool_calls[0].id})
print("Tool message added\n")
// Step 6: Submit the tool output to the Model
// Paste the following code after the code from Step 5

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

At this point, the `messages` array is as follows:

[
  System Message -- Guides the model's tool calling strategy
  User Message -- The user's question
  Assistant Message -- The tool call information returned by the model
  Tool Message -- The tool's output information (there may be multiple Tool Messages if parallel tool calling, as described below, is used)
]

After you update the `messages` array, you can run the following code.

# Step 7: The model summarizes the tool output
# Paste the following code after the code from Step 6
print("Summarizing tool output...")
completion = function_calling()
// Step 7: The model summarizes the tool output
// Paste the following code after the code from Step 6

console.log("Summarizing tool output...");
const completion_1 = await functionCalling();

You can retrieve the reply content from content: "Today in Shanghai, the weather is cloudy. If you have any other questions, feel free to ask."

{
    "content": "Today in Shanghai, the weather is cloudy. If you have any other questions, feel free to ask.",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}

You have now completed a full function calling process.

Advanced usage

Specify the tool calling method

Parallel tool calling

A single tool call is sufficient for querying the weather in a single city. If the input question requires multiple tool calls, such as "What's the weather like in Beijing and Shanghai?" or "What's the weather in Hangzhou, and what time is it now?", only one tool call instruction is returned after you initiate function calling. For example, if you ask "What's the weather like in Beijing and Shanghai?", the following is returned:

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

The returned result contains only the input parameter information for Beijing. To solve this problem, you can set the request parameter parallel_tool_calls to true when you initiate function calling. This way, the returned object contains all the tool functions and input parameters that need to be called.

Note

Parallel tool calling is suitable for tasks without dependencies. If there are dependencies between tasks, for example, the input for Tool A depends on the output from Tool B, see Getting started and use a while loop to implement serial tool calling (calling one tool at a time).

def function_calling():
    completion = client.chat.completions.create(
        model="qwen-plus",  # This example uses qwen-plus. You can change the model name as needed.
        messages=messages,
        tools=tools,
        # New parameter
        parallel_tool_calls=True
    )
    print("Returned object:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("Initiating function calling...")
completion = function_calling()
async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen-plus",  // This example uses qwen-plus. You can change the model name as needed.
        messages: messages,
        tools: tools,
        parallel_tool_calls: true
    });
    console.log("Returned object:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

The tool_calls array in the returned object contains the input parameter information for both Beijing and Shanghai:

{
    "content": "",
    "role": "assistant",
    "tool_calls": [
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"Beijing\"}"
            },
            "index": 0,
            "id": "call_c2d8a3a24c4d4929b26ae2",
            "type": "function"
        },
        {
            "function": {
                "name": "get_current_weather",
                "arguments": "{\"location\": \"Shanghai\"}"
            },
            "index": 1,
            "id": "call_dc7f2f678f1944da9194cd",
            "type": "function"
        }
    ]
}

Force tool calling

Models generate content with a degree of uncertainty and sometimes choose the wrong tool to call. If you want the model to adopt a manually set strategy for a certain type of question, such as forcing the use of a certain tool or forcing no tool use, you can modify the tool_choice parameter. The default value of the tool_choice parameter is "auto", which means the model independently decides how to perform tool calling.

When the model summarizes the tool function output, you must remove the tool_choice parameter. Otherwise, the API will still return tool call information.
  • Force a specific tool

    If you want function calling to forcibly call a certain tool for a specific type of question, you can set the tool_choice parameter to {"type": "function", "function": {"name": "the_function_to_call"}}. The model does not participate in tool selection and outputs only input parameter information.

    Assuming that the current scenario involves only weather query questions, you can modify the function_calling code as follows:

    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("Returned object:");
        console.log(JSON.stringify(response.choices[0].message));
        console.log("\n");
        return response;
    }
    
    const response = await functionCalling();

    No matter what question is asked, the tool function in the returned object is get_current_weather.

    Before you use this strategy, make sure that the question is relevant to the selected tool. Otherwise, you may receive unexpected results.
  • Force no tools

    If you want function calling to never perform a tool call for any question, which means the returned object contains reply content in content and the tool_calls parameter is empty, you can set the tool_choice parameter to "none", or do not pass the tools parameter. The tool_calls parameter returned by function calling is always empty.

    Assuming that no questions in the current scenario require a tool call, you can modify the function_calling code as follows:

    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("Returned object:");
        console.log(JSON.stringify(completion.choices[0].message));
        console.log("\n");
        return completion;
    }
    
    const completion = await functionCalling();

Multi-turn conversation

A user might ask "What's the weather in Beijing?" in the first turn, and then "What about Shanghai?" in the second. If the model's context does not contain the information from the first turn, it cannot determine which tool to call. We recommend that you maintain the `messages` array after each turn in a multi-turn conversation scenario. You can add the new User Message to this array and then initiate function calling and perform the subsequent steps. The `messages` structure is as follows:

[
  System Message -- Guides the model's tool calling strategy
  User Message -- The user's question
  Assistant Message -- The tool call information returned by the model
  Tool Message -- The tool's output information
  Assistant Message -- The model's summary of the tool call information
  User Message -- The user's second-turn question
]

Streaming output

To improve user experience and reduce waiting time, you can use streaming output to retrieve the tool function name and input parameter information in real-time. In this case:

  • Tool call parameter information: This is returned in chunks as a data stream.

  • Tool function name: This is returned in the first data chunk of the streaming response.

from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # If you use a model in the China (Beijing) region, replace this with: https://dashscope.aliyuncs.com/compatible-mode/v1
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

stream = client.chat.completions.create(
    model="qwen-plus",
    messages=[{"role": "user", "content": "What's the weather in Hangzhou?"}],
    tools=tools,
    stream=True
)

for chunk in stream:
    delta = chunk.choices[0].delta
    print(delta.tool_calls)
import { OpenAI } from "openai";

const openai = new OpenAI(
    {
        // If you use a model in the China (Beijing) region, you must use an API key from the China (Beijing) region. Obtain the key at: https://bailian.console.alibabacloud.com/?tab=model#/api-key
        // If the environment variable is not configured, replace the following line with your model Studio API key: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // If you use a model in the China (Beijing) region, replace the baseURL with: https://dashscope.aliyuncs.com/compatible-mode/v1
        baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "Useful for querying the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen-plus",
    messages: [{ role: "user", content: "Beijing weather" }],
    tools: tools,
    stream: true,
});

for await (const chunk of stream) {
    const delta = chunk.choices[0].delta;
    console.log(delta.tool_calls);
}

The output is as follows:

[ChoiceDeltaToolCall(index=0, id='call_8f08d2b0fc0c4d8fab7123', function=ChoiceDeltaToolCallFunction(arguments='{"location":', name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='', function=ChoiceDeltaToolCallFunction(arguments=' "Hangzhou"}', name=None), type='function')]
None

Run the following code to assemble the input parameters (arguments):

tool_calls = {}
for response_chunk in stream:
    delta_tool_calls = response_chunk.choices[0].delta.tool_calls
    if delta_tool_calls:
        for tool_call_chunk in delta_tool_calls:
            call_index = tool_call_chunk.index
            tool_call_chunk.function.arguments = tool_call_chunk.function.arguments or ""
            if call_index not in tool_calls:
                tool_calls[call_index] = tool_call_chunk
            else:
                tool_calls[call_index].function.arguments += tool_call_chunk.function.arguments
print(tool_calls[0].model_dump_json())
const toolCalls = {};
for await (const responseChunk of stream) {
  const deltaToolCalls = responseChunk.choices[0]?.delta?.tool_calls;
  if (deltaToolCalls) {
    for (const toolCallChunk of deltaToolCalls) {
      const index = toolCallChunk.index;
      toolCallChunk.function.arguments = toolCallChunk.function.arguments || "";
      if (!toolCalls[index]) {
        toolCalls[index] = { ...toolCallChunk };
        if (!toolCalls[index].function) {
            toolCalls[index].function = { name: '', arguments: '' };
        }
      } 
      else if (toolCallChunk.function?.arguments) {
        toolCalls[index].function.arguments += toolCallChunk.function.arguments;
      }
    }
  }
}
console.log(JSON.stringify(toolCalls[0]));

The following output is returned:

{"index":0,"id":"call_16c72bef988a4c6c8cc662","function":{"arguments":"{\"location\": \"Hangzhou\"}","name":"get_current_weather"},"type":"function"}

When the model summarizes the tool function output, the added assistant message must use the following format. Replace the elements in tool_calls with the content from the previous output.

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

For Qwen3-Omni-Flash

In the get tool information stage, the usage of the Qwen3-Omni-Flash model differs from other models in the following ways:

  • Streaming output is mandatory: Qwen3-Omni-Flash supports only streaming output. You must also set stream=True when you retrieve tool information.

  • Text-only output is recommended: The model needs only text information to retrieve tool information, such as the function name and parameters. To avoid generating unnecessary audio, we recommend that you set modalities=["text"]. When the output includes both text and audio modalities, you need to skip the audio data chunks when you retrieve tool information.

For more information about Qwen3-Omni-Flash, see omni-modal.
from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # If you use a model in the China (Beijing) region, replace this with: https://dashscope.aliyuncs.com/compatible-mode/v1
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

completion = client.chat.completions.create(
    model="qwen3-omni-flash",
    messages=[{"role": "user", "content": "What's the weather in Hangzhou?"}],

    # Set the modality of the output data. Valid values: ["text"], ["text","audio"]. We recommend setting this to ["text"].
    modalities=["text"],

    # stream must be set to True, otherwise an error will be reported.
    stream=True,
    tools=tools
)

for chunk in completion:
    # If the output includes the audio modality, change the following condition to: if chunk.choices and not hasattr(chunk.choices[0].delta, "audio"): 
    if chunk.choices:
        delta = chunk.choices[0].delta
        print(delta.tool_calls)
import { OpenAI } from "openai";

const openai = new OpenAI(
    {
        // If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
        // If you have not configured the environment variable, replace the following line with: apiKey: "sk-xxx", using your model Studio API key.
        apiKey: process.env.DASHSCOPE_API_KEY,
        // If you use a model in the China (Beijing) region, replace the baseURL with: https://dashscope.aliyuncs.com/compatible-mode/v1
        baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen3-omni-flash",
    messages: [
        {
            "role": "user",
            "content": "Hangzhou weather"
        }],
    stream: true,
    // Set the modality of the output data. Valid values: ["text"], ["text","audio"]. We recommend setting this to ["text"].
    modalities: ["text"],
    tools:tools
});


for await (const chunk of stream) {
    // If the output includes audio, replace the conditional statement with: if (chunk.choices?.length && chunk.choices[0].delta && !('audio' in chunk.choices[0].delta)) 
    if (chunk.choices?.length){
    const delta = chunk.choices[0].delta;
    console.log(delta.tool_calls);
}}

The following output is returned:

[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=None, name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=' {"location": "Hangzhou"}', name=None), type='function')]
None

For the code to construct the input parameters (arguments), see Streaming output.

For deep thinking models

Deep thinking models think before they output tool call information, which can improve the interpretability and reliability of their decisions.

  1. Thinking process

    The model progressively analyzes the user's intent, identifies the required tools, validates the parameter legality, and plans the calling strategy.

  2. Tool calling

    The model outputs one or more function call requests in a structured format.

    Parallel tool calling is supported.

The following section shows an example of a streaming call to a deep thinking model for tool calling.

For text generation thinking models, see Deep thinking. For multimodal thinking models, see Visual understanding and Omni-modal.
tool_choice parameter supports only being set to "auto", which is the default and means the model chooses the tool independently, or "none", which forces the model not to choose a tool.

OpenAI compatible

Python

Sample code

import os
from openai import OpenAI

# Initialize the OpenAI client and configure the Alibaba Cloud DashScope service
client = OpenAI(
    # If you have not configured the environment variable, replace the following line with: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),  # Read API key from environment variable
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

# Define the list of available tools
tools = [
    # Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}  # No parameters needed
        }
    },  
    # Tool 2: Get the weather for a specified city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]  # Required parameter
            }
        }
    }
]

messages = [{"role": "user", "content": input("Please enter your question: ")}]

# Example message for the Qwen3-VL model
# messages = [{
#     "role": "user",
#     "content": [
#              {"type": "image_url","image_url": {"url": "https://img.alicdn.com/imgextra/i4/O1CN014CJhzi20NOzo7atOC_!!6000000006837-2-tps-2048-1365.png"}},
#              {"type": "text", "text": "What is the current weather at the location in the image?"}]
#     }]

completion = client.chat.completions.create(
    # This example uses qwen-plus. It can be replaced with other deep thinking models.
    model="qwen-plus",
    messages=messages,
    extra_body={
        # Enable deep thinking. This parameter is invalid for qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, and QwQ models.
        "enable_thinking": True
    },
    tools=tools,
    parallel_tool_calls=True,
    stream=True,
    # Uncomment to get token consumption information
    # stream_options={
    #     "include_usage": True
    # }
)

reasoning_content = ""  # Defines the complete thinking process
answer_content = ""     # Defines the complete reply
tool_info = []          # Stores tool call information
is_answering = False   # Determines if the thinking process has ended and the reply has begun
print("="*20+"Thinking Process"+"="*20)
for chunk in completion:
    if not chunk.choices:
        # Process usage statistics
        print("\n"+"="*20+"Usage"+"="*20)
        print(chunk.usage)
    else:
        delta = chunk.choices[0].delta
        # Process the AI's thinking process (chain of thought)
        if hasattr(delta, 'reasoning_content') and delta.reasoning_content is not None:
            reasoning_content += delta.reasoning_content
            print(delta.reasoning_content,end="",flush=True)  # Output the thinking process in real time
            
        # Process the final reply content
        else:
            if not is_answering:  # Print the title when entering the reply phase for the first time
                is_answering = True
                print("\n"+"="*20+"Reply Content"+"="*20)
            if delta.content is not None:
                answer_content += delta.content
                print(delta.content,end="",flush=True)  # Stream the reply content
            
            # Process tool call information (supports parallel tool calls)
            if delta.tool_calls is not None:
                for tool_call in delta.tool_calls:
                    index = tool_call.index  # Tool call index, for parallel calls
                    
                    # Dynamically expand the tool information storage list
                    while len(tool_info) <= index:
                        tool_info.append({})
                    
                    # Collect the tool call ID (for subsequent function calls)
                    if tool_call.id:
                        tool_info[index]['id'] = tool_info[index].get('id', '') + tool_call.id
                    
                    # Collect the function name (for subsequent routing to a specific function)
                    if tool_call.function and tool_call.function.name:
                        tool_info[index]['name'] = tool_info[index].get('name', '') + tool_call.function.name
                    
                    # Collect function arguments (in JSON string format, requires subsequent parsing)
                    if tool_call.function and tool_call.function.arguments:
                        tool_info[index]['arguments'] = tool_info[index].get('arguments', '') + tool_call.function.arguments
            
print(f"\n"+"="*19+"Tool Call Information"+"="*19)
if not tool_info:
    print("No tool calls")
else:
    print(tool_info)

Returned result

If you enter "Weather in the four municipalities", the following result is returned:

====================Thinking Process====================
Okay, the user is asking for the "weather in the four municipalities". First, I need to identify which four municipalities are being referred to. According to China's administrative divisions, the municipalities include Beijing, Shanghai, Tianjin, and Chongqing. So the user wants to know the weather conditions in these four cities.

Next, I need to check the available tools. The provided tools include the get_current_weather function, which takes a location of type string as a parameter. Each city needs to be queried separately because the function can only check one location at a time. Therefore, I need to call this function once for each municipality.

Then, I need to consider how to generate the correct tool calls. Each call should include the city name as a parameter. For example, the first call is for Beijing, the second for Shanghai, and so on. I need to make sure the parameter name is `location` and the value is the correct city name.

Also, the user probably wants the weather information for each city, so I need to ensure each function call is correct. It might require four consecutive calls, one for each city. However, based on the tool usage rules, it might need to be handled in multiple steps, or multiple calls might be generated at once. But based on the example, it seems only one function is called at a time, so it might need to be done step by step.

Finally, I need to confirm if there are any other factors to consider, such as whether the parameters are correct, the city names are accurate, and whether to handle possible error situations, like the city not existing or the API being unavailable. But for now, the four municipalities are clear, so it should be fine.
====================Reply Content====================

===================Tool Call Information===================
[{'id': 'call_767af2834c12488a8fe6e3', 'name': 'get_current_weather', 'arguments': '{"location": "Beijing"}'}, {'id': 'call_2cb05a349c89437a947ada', 'name': 'get_current_weather', 'arguments': '{"location": "Shanghai"}'}, {'id': 'call_988dd180b2ca4b0a864ea7', 'name': 'get_current_weather', 'arguments': '{"location": "Tianjin"}'}, {'id': 'call_4e98c57ea96a40dba26d12', 'name': 'get_current_weather', 'arguments': '{"location": "Chongqing"}'}]

Node.js

Sample code

import OpenAI from "openai";
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';

const openai = new OpenAI({
    apiKey: process.env.DASHSCOPE_API_KEY,
    baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
});

const tools = [
    {
        type: "function",
        function: {
            name: "get_current_time",
            description: "This is useful when you want to know the current time.",
            parameters: {}
        }
    },
    {
        type: "function",
        function: {
            name: "get_current_weather",
            description: "This is useful when you want to query the weather in a specific city.",
            parameters: {
                type: "object",
                properties: {
                    location: {
                        type: "string",
                        description: "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                required: ["location"]
            }
        }
    }
];

async function main() {
    const rl = readline.createInterface({ input, output });
    const question = await rl.question("Please enter your question: "); 
    rl.close();
    
    const messages = [{ role: "user", content: question }];
    // Example message for the Qwen3-VL model
    // const messages= [{
    //     role: "user",
    //     content: [{type: "image_url", image_url: {url: "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"}},
    //               {type: "text", text: "What's the weather at the location in the image?"}]
    //   }];
    let reasoningContent = "";
    let answerContent = "";
    const toolInfo = [];
    let isAnswering = false;

    console.log("=".repeat(20) + "Thinking Process" + "=".repeat(20));
    
    try {
        const stream = await openai.chat.completions.create({
            // This example uses qwen-plus. It can be replaced with other deep thinking models.
            model: "qwen-plus",
            messages,
            // Enable deep thinking. This parameter is invalid for qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, and QwQ models.
            enable_thinking: true,
            tools,
            stream: true,
            parallel_tool_calls: true
        });

        for await (const chunk of stream) {
            if (!chunk.choices?.length) {
                console.log("\n" + "=".repeat(20) + "Usage" + "=".repeat(20));
                console.log(chunk.usage);
                continue;
            }

            const delta = chunk.choices[0]?.delta;
            if (!delta) continue;

            // Process thinking process
            if (delta.reasoning_content) {
                reasoningContent += delta.reasoning_content;
                process.stdout.write(delta.reasoning_content);
            }
            // Process reply content
            else {
                if (!isAnswering) {
                    isAnswering = true;
                    console.log("\n" + "=".repeat(20) + "Reply Content" + "=".repeat(20));
                }
                if (delta.content) {
                    answerContent += delta.content;
                    process.stdout.write(delta.content);
                }
                // Process tool calls
                if (delta.tool_calls) {
                    for (const toolCall of delta.tool_calls) {
                        const index = toolCall.index;
                        
                        // Ensure array length is sufficient
                        while (toolInfo.length <= index) {
                            toolInfo.push({});
                        }
                        
                        // Update tool ID
                        if (toolCall.id) {
                            toolInfo[index].id = (toolInfo[index].id || "") + toolCall.id;
                        }
                        
                        // Update function name
                        if (toolCall.function?.name) {
                            toolInfo[index].name = (toolInfo[index].name || "") + toolCall.function.name;
                        }
                        
                        // Update parameters
                        if (toolCall.function?.arguments) {
                            toolInfo[index].arguments = (toolInfo[index].arguments || "") + toolCall.function.arguments;
                        }
                    }
                }
            }
        }

        console.log("\n" + "=".repeat(19) + "Tool Call Information" + "=".repeat(19));
        console.log(toolInfo.length ? toolInfo : "No tool calls");

    } catch (error) {
        console.error("An error occurred:", error);
    }
}

main(); 

Returned result

If you enter "Weather in the four municipalities", the following result is returned:

Please enter your question: Weather in the four municipalities
====================Thinking Process====================
Okay, the user is asking about the weather in the four municipalities. First, I need to clarify which four municipalities in China are being referred to. Beijing, Shanghai, Tianjin, and Chongqing, right? Next, I need to call the weather query function for each city.

However, the user's question may require me to get the weather conditions for these four cities separately. Each city requires a call to the get_current_weather function, with its respective city name as the parameter. I need to make sure the parameters are correct, such as the full names of the municipalities, for example, "Beijing Municipality", "Shanghai Municipality", "Tianjin Municipality", and "Chongqing Municipality".

Then, I need to call the weather API for these four cities in sequence. Each call requires a separate tool_call. The user probably wants the current weather information for each city, so I need to ensure each call is correct. I might need to pay attention to the correct spelling and name of each city to avoid errors. For example, Chongqing is sometimes abbreviated, so the full name should be used in the parameter.

Now, I need to generate four tool_calls, one for each municipality. I will check if each parameter is correct and then arrange them in order. This way, the user will get the weather data for all four municipalities.
====================Reply Content====================

===================Tool Call Information===================
[
  {
    id: 'call_21dc802e717f491298d1b2',
    name: 'get_current_weather',
    arguments: '{"location": "Beijing"}'
  },
  {
    id: 'call_2cd3be1d2f694c4eafd4e5',
    name: 'get_current_weather',
    arguments: '{"location": "Shanghai"}'
  },
  {
    id: 'call_48cf3f78e02940bd9085e4',
    name: 'get_current_weather',
    arguments: '{"location": "Tianjin"}'
  },
  {
    id: 'call_e230a2b4c64f4e658d223e',
    name: 'get_current_weather',
    arguments: '{"location": "Chongqing"}'
  }
]

HTTP

Sample code

curl

curl -X POST https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
    "model": "qwen-plus",
    "messages": [
        {
            "role": "user", 
            "content": "What is the weather like in Hangzhou?"
        }
    ],
    "tools": [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location":{
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
  ],
  "enable_thinking": true,
  "stream": true
}'

DashScope

Python

Sample code

import dashscope
dashscope.base_http_api_url = "https://dashscope-intl.aliyuncs.com/api/v1/"
tools = [
    # Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}  # Since getting the current time requires no input parameters, parameters is an empty dictionary.
        }
    },  
    # Tool 2: Get the weather for a specified city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {  
                "type": "object",
                "properties": {
                    # A location is required to query the weather, so the parameter is set to location.
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# Define the question
messages = [{"role": "user", "content": input("Please enter your question: ")}]

# Example message for Qwen3-VL
# messages = [
# {
#     "role": "user",
#     "content": [
#     {"image": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"},
#     {"text": "Weather at the image location?"}]
# }]

# If you use the Qwen3-VL model, replace Generation with the MultiModalConversation interface.
completion = dashscope.Generation.call(
    # This example uses qwen-plus. It can be replaced with other deep thinking models.
    model="qwen-plus", 
    messages=messages,
    enable_thinking=True,
    tools=tools,
    parallel_tool_calls=True,
    stream=True,
    incremental_output=True,
    result_format="message"
)

reasoning_content = ""
answer_content = ""
tool_info = []
is_answering = False
print("="*20+"Thinking Process"+"="*20)

for chunk in completion:
    if chunk.status_code == 200:
        msg = chunk.output.choices[0].message
        
        # Process thinking process
        if 'reasoning_content' in msg and msg.reasoning_content:
            reasoning_content += msg.reasoning_content
            print(msg.reasoning_content, end="", flush=True)
        
        # Process reply content
        if 'content' in msg and msg.content:
            if not is_answering:
                is_answering = True
                print("\n"+"="*20+"Reply Content"+"="*20)
            answer_content += msg.content
            print(msg.content, end="", flush=True)
        
        # Process tool calls
        if 'tool_calls' in msg and msg.tool_calls:
            for tool_call in msg.tool_calls:
                index = tool_call['index']
                
                while len(tool_info) <= index:
                    tool_info.append({'id': '', 'name': '', 'arguments': ''})  # Initialize all fields
                
                # Incrementally update tool ID
                if 'id' in tool_call:
                    tool_info[index]['id'] += tool_call.get('id', '')
                
                # Incrementally update function information
                if 'function' in tool_call:
                    func = tool_call['function']
                    # Incrementally update function name
                    if 'name' in func:
                        tool_info[index]['name'] += func.get('name', '')
                    # Incrementally update parameters
                    if 'arguments' in func:
                        tool_info[index]['arguments'] += func.get('arguments', '')

print(f"\n"+"="*19+"Tool Call Information"+"="*19)
if not tool_info:
    print("No tool calls")
else:
    print(tool_info)

Returned result

Entering "Weather in the four municipalities" returns the following result:

Please enter your question: Weather in the four municipalities
====================Thinking Process====================
Okay, the user is asking about the weather in the four municipalities. First, I need to confirm which four municipalities in China are being referred to. Beijing, Shanghai, Tianjin, and Chongqing, right? Next, the user needs the weather conditions for each city, so I need to call the weather query function.

However, a problem arises: the user did not specify the city names, only "the four municipalities". I might need to clarify the name of each municipality and then query them separately. For example, Beijing, Shanghai, Tianjin, and Chongqing. I need to make sure each city is correct.

Then, I need to check the available tools. The user has provided the `get_current_weather` function with a `location` parameter. Therefore, I need to call this function for each municipality, passing the corresponding city name as the parameter. For example, the first call's location is Beijing, the second is Shanghai, the third is Tianjin, and the fourth is Chongqing.

However, I might need to be careful. For a municipality like Chongqing, a more specific district might sometimes be needed, but the user probably only needs the city-level weather. So, using the municipality name directly should be fine. Next, I need to generate four separate function calls, one for each municipality. This way, the user will get the weather conditions for all four cities.

Finally, I will ensure that the parameters for each call are correct and that none are missed. This will provide a complete answer to the user's question.
===================Tool Call Information===================
[{'id': 'call_2f774ed97b0e4b24ab10ec', 'name': 'get_current_weather', 'arguments': '{"location": "Beijing"}'}, {'id': 'call_dc3b05b88baa48c58bc33a', 'name': 'get_current_weather', 'arguments': '{"location": "Shanghai"}}'}, {'id': 'call_249b2de2f73340cdb46cbc', 'name': 'get_current_weather', 'arguments': '{"location": "Tianjin"}'}, {'id': 'call_833333634fda49d1b39e87', 'name': 'get_current_weather', 'arguments': '{"location": "Chongqing"}}'}]

Java

Sample code

// dashscope SDK version >= 2.19.4
import java.util.Arrays;

import com.alibaba.dashscope.exception.UploadFileException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.aigc.generation.Generation;
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.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.tools.FunctionDefinition;
import io.reactivex.Flowable;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.lang.System;
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.Collections;

public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    private static ObjectNode jsonSchemaWeather;
    private static ObjectNode jsonSchemaTime;
    
    static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";}

    static class TimeTool {
        public String call() {
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return "Current time: " + now.format(formatter) + ".";
        }
    }

    static class WeatherTool {
        private String location;

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

        public String call() {
            return this.location + " is sunny today";
        }
    }

    static {
        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);
        jsonSchemaWeather = generator.generateSchema(WeatherTool.class);
        jsonSchemaTime = generator.generateSchema(TimeTool.class);
    }
    private static void handleGenerationResult(GenerationResult message) {
        System.out.println(JsonUtils.toJson(message));
    }
    
    // Create a tool calling method for text generation models
    public static void streamCallWithMessage(Generation gen, Message userMsg)
            throws NoApiKeyException, ApiException, InputRequiredException {
        GenerationParam param = buildGenerationParam(userMsg);
        Flowable<GenerationResult> result = gen.streamCall(param);
        result.blockingForEach(message -> handleGenerationResult(message));
    }
    // Build text generation model parameters that support tool calling
    private static GenerationParam buildGenerationParam(Message userMsg) {
        FunctionDefinition fdWeather = buildFunctionDefinition(
                "get_current_weather", "Get the weather for a specified region", jsonSchemaWeather);
        FunctionDefinition fdTime = buildFunctionDefinition(
                "get_current_time", "Get the current time", jsonSchemaTime);

        return GenerationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("qwen-plus")
                .enableThinking(true)
                .messages(Arrays.asList(userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .incrementalOutput(true)
                .tools(Arrays.asList(
                        ToolFunction.builder().function(fdWeather).build(),
                        ToolFunction.builder().function(fdTime).build()))
                .build();
    }

    // Create a tool calling method for the qwen3-vl-plus model
    public static void streamCallWithMultiModalMessage(MultiModalConversation conv, MultiModalMessage userMsg)
            throws NoApiKeyException, ApiException, UploadFileException {
        MultiModalConversationParam param = buildMultiModalConversationParam(userMsg);
        Flowable<MultiModalConversationResult> result = conv.streamCall(param);
        result.blockingForEach(message -> System.out.println(JsonUtils.toJson(message)));
    }

    // Build Qwen3-vl model parameters that support tool calling
    private static MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage userMsg) {
        FunctionDefinition fdWeather = buildFunctionDefinition(
                "get_current_weather", "Get the weather for a specified region", jsonSchemaWeather);
        FunctionDefinition fdTime = buildFunctionDefinition(
                "get_current_time", "Get the current time", jsonSchemaTime);

        return MultiModalConversationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("qwen3-vl-plus")  // Use the multimodal model Qwen3-vl
                .enableThinking(true)
                .messages(Arrays.asList(userMsg))
                .tools(Arrays.asList(  // Configure the tool list
                        ToolFunction.builder().function(fdWeather).build(),
                        ToolFunction.builder().function(fdTime).build()))
                .build();
    }

    private static FunctionDefinition buildFunctionDefinition(
            String name, String description, ObjectNode schema) {
        return FunctionDefinition.builder()
                .name(name)
                .description(description)
                .parameters(JsonUtils.parseString(schema.toString()).getAsJsonObject())
                .build();
    }

    public static void main(String[] args) {
        try {
            Generation gen = new Generation();
            Message userMsg = Message.builder()
                    .role(Role.USER.getValue())
                    .content("Please tell me the weather in Hangzhou")
                    .build();
            try {
                streamCallWithMessage(gen, userMsg);
            } catch (InputRequiredException e) {
                throw new RuntimeException(e);
            }
//             If you use the Qwen3-VL model for tool calling, uncomment the following lines.
//            MultiModalConversation conv = new MultiModalConversation();
//            MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
//                    .content(Arrays.asList(Collections.singletonMap("image", "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"),
//                            Collections.singletonMap("text", "Weather at the location in the image"))).build();
//            try {
//                streamCallWithMultiModalMessage(conv,userMessage);
//            } catch (UploadFileException e) {
//                throw new RuntimeException(e);
//            }
        } catch (ApiException | NoApiKeyException e) {
            logger.error("An exception occurred: {}", e.getMessage());
        }
        System.exit(0);
    }
}

Returned result

{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":6,"total_tokens":244},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"Okay, the user wants me"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":12,"total_tokens":250},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" to tell them the weather in Hangzhou. I"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":16,"total_tokens":254},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" need to first determine if"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":22,"total_tokens":260},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" there are any relevant tools available. Looking at the provided"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":28,"total_tokens":266},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" tools, I see there is a get_current"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":34,"total_tokens":272},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"_weather function with a location parameter"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":38,"total_tokens":276},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":". So I should call"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":43,"total_tokens":281},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" this function with the parameter"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":48,"total_tokens":286},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" set to Hangzhou. No"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":52,"total_tokens":290},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" other tools are needed because"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":56,"total_tokens":294},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" the user only asked"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":60,"total_tokens":298},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" about the weather. Next, I will construct"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":64,"total_tokens":302},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" the tool_call, filling in"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":68,"total_tokens":306},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" the name and parameters"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":73,"total_tokens":311},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":". I will make sure the parameter is a"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":78,"total_tokens":316},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" JSON object and location is a"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":82,"total_tokens":320},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":" string. After checking for errors"}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":88,"total_tokens":326},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":", I will return."}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":106,"total_tokens":344},"output":{"choices":[{"finish_reason":"null","message":{"role":"assistant","content":"","reasoning_content":"","tool_calls":[{"type":"function","id":"call_ecc41296dccc47baa01567","function":{"name":"get_current_weather","arguments":"{\"location\": \"Hangzhou"}}]}}]}}
{"requestId":"4edb81cd-4647-9d5d-88f9-a4f30bc6d8dd","usage":{"input_tokens":238,"output_tokens":108,"total_tokens":346},"output":{"choices":[{"finish_reason":"tool_calls","message":{"role":"assistant","content":"","reasoning_content":"","tool_calls":[{"type":"function","id":"","function":{"arguments":"\"}"}}]}}]}}

HTTP

Sample code

curl

# ======= Important Note =======
# If you use the Qwen3-VL model, modify the message parameter and replace the URL with https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
# API keys for the Singapore and China (Beijing) regions are different. To get an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
# The following is the URL for the Singapore region. If you use a model in the Beijing region, replace the URL with: https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation
# === Please delete this comment before execution ===

curl -X POST "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/text-generation/generation" \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-DashScope-SSE: enable" \
-d '{
    "model": "qwen-plus",
    "input":{
        "messages":[      
            {
                "role": "user",
                "content": "Hangzhou weather"
            }
        ]
    },
    "parameters": {
        "enable_thinking": true,
        "incremental_output": true,
        "result_format": "message",
        "tools": [{
            "type": "function",
            "function": {
                "name": "get_current_time",
                "description": "This is useful when you want to know the current time.",
                "parameters": {}
            }
        },{
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "This is useful when you want to query the weather in a specific city.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                        }
                    },
                    "required": ["location"]
                }
            }
        }]
    }
}'

Going live

Test tool calling accuracy

  • Establish an evaluation system:

    Build a test dataset that reflects real business scenarios and define clear evaluation metrics, such as tool selection accuracy, parameter extraction accuracy, and the end-to-end success rate.

  • Optimize prompts

    If testing reveals issues such as incorrect tool selection or parameters, you can perform targeted optimization on system prompts, tool descriptions, and parameter descriptions. This is the core tuning method.

  • Upgrade the model

    If prompt engineering fails to improve performance, upgrading to a more capable model version, such as qwen3-max-preview, is the most direct and effective way to improve metrics.

Dynamically control the number of tools

When the number of tools integrated into an application is large, such as dozens or even hundreds, providing the entire tool library to the model can cause the following problems:

  • Performance degradation: It becomes dramatically more difficult for the model to select the correct tool from an enormous toolset.

  • Cost and latency: Many tool descriptions consume many input tokens, which increases costs and slows down responses.

The solution is to add a tool routing or retrieval layer before calling the model. This layer quickly and accurately filters a small and relevant subset of tools from the complete tool library based on the user's current query, and then provides this subset to the model.

The following are several mainstream methods for implementing tool routing:

  • Semantic retrieval

    Convert the description information (description) of all tools into vectors using an embedding model and store them in a vector database. When a user enters a query, you can use a vector similarity search on the query vector to recall the top K most relevant tools.

  • Hybrid retrieval

    Combine the "fuzzy matching" capability of semantic retrieval with the "exact match" capability of traditional keywords or metadata tags. Adding tags or keywords fields to tools and performing vector search and keyword filtering simultaneously during retrieval can significantly improve recall precision for high-frequency or specific scenarios.

  • Lightweight LLM router

    For more complex routing logic, you can use a smaller, faster, and cheaper model, such as Qwen-Flash, as a pre-routing "router model". Its task is to output a list of relevant tool names based on the user's question.

Practical suggestions

  • Keep the candidate set concise: Regardless of the method used, we recommend providing no more than 20 tools to the main model. This number represents an optimal balance between the model's cognitive load, cost, latency, and accuracy.

  • Layered filtering strategy: You can build a funnel-style routing policy. For example, you can first use low-cost keyword or rule matching for an initial screening to filter out irrelevant tools, and then perform semantic retrieval on the remaining tools to improve efficiency and quality.

Tool security principles

When you enable tool execution capabilities for a model, security must be your top priority. The core principles are the least privilege principle and human confirmation.

  • Least privilege principle: The toolset provided to the model should strictly adhere to the least privilege principle. By default, tools should be read-only, such as querying weather or searching documents, and avoid directly providing any "write" permissions that involve state changes or resource operations.

  • Isolate dangerous tools: Do not directly provide dangerous tools to the model, such as tools for executing arbitrary code (code interpreter), operating the file system (fs.delete), performing database delete or update operations (db.drop_table), or transferring funds (payment.transfer).

  • Human in the loop: For all high-privilege or irreversible operations, you must introduce a manual review and confirmation step. The model can generate an operation request, but a human user must click the final execution "button". For example, the model can prepare an email, but the user must confirm the send operation.

User experience optimization

The function calling chain is long, and a problem at any step can degrade the user experience.

Handle tool run failures

Tool execution failures are common. You can adopt the following strategies:

  • Maximum retry count: Set a reasonable retry limit, for example, 3, to avoid long user wait times or wasted system resources due to continuous failures.

  • Provide fallback responses: When retries are exhausted or an unresolvable error is encountered, you should return a clear and friendly prompt to the user, for example: "Sorry, I can't find the relevant information at the moment. The service might be busy. Please try again later."

Cope with processing delays

High latency can reduce user satisfaction and should be addressed through frontend interaction and backend optimization.

  • Set a timeout: Set an independent and reasonable timeout for each step of function calling. Once a timeout occurs, the operation should be immediately interrupted and feedback should be provided.

  • Provide instant feedback: When you start a function calling process, we recommend that you display a prompt on the interface, such as "Querying the weather for you..." or "Searching for relevant information...", to provide the user with real-time feedback on the processing progress.

Billing description

In addition to the tokens in the `messages` array, tool descriptions are appended to the prompt as input tokens, which are also subject to billing.

Pass tool information through a system message

We recommend that you use the `tools` parameter to pass tool information to the model. For more information, see the Implementation guide section. To pass tool information using a system message, use the prompt template in the following code to ensure optimal model performance:

OpenAI compatible

Python

Sample code

import os
from openai import OpenAI
import json

client = OpenAI(
    # If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
    # If you have not configured the environment variable, replace the following line with: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # If you use a model in the China (Beijing) region, replace the base_url with: https://dashscope.aliyuncs.com/compatible-mode/v1
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

# Custom System prompt, which can be modified according to your needs
custom_prompt = "You are an intelligent assistant responsible for calling various tools to help users solve problems. You can select the appropriate tools and call them correctly based on the user's needs."

tools = [
    # Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}
        }
    },  
    # Tool 2: Get the weather for a specified city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# Iterate through the tools list to build a description for each tool
tools_descriptions = []
for tool in tools:
    tool_json = json.dumps(tool, ensure_ascii=False)
    tools_descriptions.append(tool_json)

# Combine all tool descriptions into a single string
tools_content = "\n".join(tools_descriptions)

system_prompt = f"""{custom_prompt}

# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
{tools_content}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{{"name": <function-name>, "arguments": <args-json-object>}}
</tool_call>"""

messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": "What time is it?"}
]

completion = client.chat.completions.create(
    model="qwen-plus",
    messages=messages,
)
print(completion.model_dump_json())

Node.js

Sample code

import OpenAI from "openai";

const client = new OpenAI({
    // If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
    // If you have not configured the environment variable, replace the following line with: apiKey: "sk-xxx",
    apiKey: process.env.DASHSCOPE_API_KEY,
    // If you use a model in the China (Beijing) region, replace the baseURL with: https://dashscope.aliyuncs.com/compatible-mode/v1
    baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
});

// Custom System prompt
const customPrompt = "You are an intelligent assistant responsible for calling various tools to help users solve problems. You can select the appropriate tools and call them correctly based on the user's needs.";

const tools = [
    // Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}
        }
    },
    // Tool 2: Get the weather for a specified city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

// Iterate through the tools list to build a description for each tool
const toolsDescriptions = [];
for (const tool of tools) {
    const toolJson = JSON.stringify(tool, null, 2);
    toolsDescriptions.push(toolJson);
}

// Combine all tool descriptions into a single string
const toolsContent = toolsDescriptions.join("\n");

const systemPrompt = `${customPrompt}

# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
${toolsContent}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>`;

const messages = [
    {"role": "system", "content": systemPrompt},
    {"role": "user", "content": "What time is it?"}
];

async function main() {
    try {
        const completion = await client.chat.completions.create({
            model: "qwen-plus",
            messages: messages,
        });
        
        console.log(JSON.stringify(completion, null, 2));
    } catch (error) {
        console.error("Error:", error);
    }
}

main(); 

DashScope

Python

Sample code

import os
from dashscope import Generation
import json
# If you use a model in the China (Beijing) region, replace base_http_api_url with: https://dashscope.aliyuncs.com/api/v1
dashscope.base_http_api_url = 'https://dashscope-intl.aliyuncs.com/api/v1'

# Custom System prompt
custom_prompt = "You are an intelligent assistant responsible for calling various tools to help users solve problems. You can select the appropriate tools and call them correctly based on the user's needs."

tools = [
    # Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "This is useful when you want to know the current time.",
            "parameters": {}
        }
    },  
    # Tool 2: Get the weather for a specified city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "This is useful when you want to query the weather in a specific city.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "A city or district, such as Beijing, Hangzhou, or Yuhang District."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# Iterate through the tools list to build a description for each tool
tools_descriptions = []
for tool in tools:
    tool_json = json.dumps(tool, ensure_ascii=False)
    tools_descriptions.append(tool_json)

# Combine all tool descriptions into a single string
tools_content = "\n".join(tools_descriptions)

system_prompt = f"""{custom_prompt}

# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
{tools_content}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{{"name": <function-name>, "arguments": <args-json-object>}}
</tool_call>"""

messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": "What time is it?"}
]

response = Generation.call(
    # If you use a model in the China (Beijing) region, you need to use an API Key for the China (Beijing) region. Get the key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
    # If you have not configured the environment variable, replace the following line with: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    model="qwen-plus",
    messages=messages,
    result_format="message",  # Set the output to message format
)

print(response)

Java

Sample code

// 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.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.utils.JsonUtils;
import com.alibaba.dashscope.protocol.Protocol;

public class Main {
    public static void main(String[] args) {
        try {
            callToolWithCustomPrompt();
        } 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);
    }

    public static void callToolWithCustomPrompt()
            throws NoApiKeyException, ApiException, InputRequiredException {

        // Customize the system prompt.
        String customPrompt = "You are an intelligent assistant that specializes in calling various tools to help users solve problems. You can select the appropriate tools based on the user's needs and call them correctly.";

        // Build the tool descriptions.
        String[] toolsDescriptions = {
                // Tool 1: Get the current time.
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_time\",\n" +
                        "        \"description\": \"This is useful when you want to know the current time.\",\n" +
                        "        \"parameters\": {}\n" +
                        "    }\n" +
                        "}",
                // Tool 2: Get the weather for a specified city.
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_weather\",\n" +
                        "        \"description\": \"This is useful when you want to query the weather for a specified city.\",\n" +
                        "        \"parameters\": {\n" +
                        "            \"type\": \"object\",\n" +
                        "            \"properties\": {\n" +
                        "                \"location\": {\n" +
                        "                    \"type\": \"string\",\n" +
                        "                    \"description\": \"A city or district, such as Beijing, Hangzhou, or Yuhang District.\"\n" +
                        "                }\n" +
                        "            },\n" +
                        "            \"required\": [\"location\"]\n" +
                        "        }\n" +
                        "    }\n" +
                        "}"
        };

        // Combine all tool descriptions into a single string.
        String toolsContent = String.join("\n", toolsDescriptions);

        // Build the system prompt.
        String systemPrompt = String.format("%s\n\n" +
                        "# Tools\n\n" +
                        "You may call one or more functions to assist with the user query.\n\n" +
                        "You are provided with function signatures within <tools></tools> XML tags:\n" +
                        "<tools>\n%s\n</tools>\n\n" +
                        "For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n"
                        +
                        "<tool_call>\n" +
                        "{\"name\": <function-name>, \"arguments\": <args-json-object>}\n" +
                        "</tool_call>",
                customPrompt, toolsContent);

        // Build the message list.
        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content(systemPrompt)
                .build();

        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content("What time is it?")
                .build();

        List<Message> messages = new ArrayList<>(Arrays.asList(systemMsg, userMsg));

        // Build the request parameters.
        GenerationParam param = GenerationParam.builder()
                .model("qwen-plus")
                // If you use a model in the China (Beijing) region, you must use an API key from the China (Beijing) region. You can obtain an API key from: https://bailian.console.alibabacloud.com/?tab=model#/api-key
                // If you have not configured the environment variable, replace the following line with .apiKey("sk-xxx") and use your model Studio API key.
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .messages(messages)
                .resultFormat(ResultFormat.MESSAGE)
                .build();

        // Call the generation API. If you use a model in the China (Beijing) region, replace the URL with https://dashscope.aliyuncs.com/api/v1.
        Generation gen = new Generation(Protocol.HTTP.getValue(), "https://dashscope-intl.aliyuncs.com/api/v1");
        GenerationResult result = gen.call(param);

        // Print the result.
        System.out.println(JsonUtils.toJson(result));
    }
}
After you run the preceding code, you can use an XML parser to extract the tool calling information from between the <tool_call> and </tool_call> tags. This information includes the function name and input parameters.

Error codes

If a call fails, see Error messages for troubleshooting.