Todos los productos
Search
Centro de documentos

Alibaba Cloud Model Studio:Llamada a funciones

Última actualización: Oct 23, 2026

Los modelos de lenguaje grandes (LLM) no pueden acceder a datos en tiempo real ni a sistemas externos. Function Calling permite a los modelos llamar a herramientas externas, como APIs, bases de datos y funciones definidas por el usuario. Esto permite que un modelo recupere información o realice acciones más allá de sus capacidades integradas.

Funcionamiento

Function Calling funciona mediante una interacción de varios pasos entre su aplicación y el LLM:

  1. Realizar la primera llamada al modelo

    La aplicación envía la pregunta del usuario y una lista de herramientas disponibles al LLM.

  2. Recibir instrucciones de llamada a herramientas del modelo

    Si el modelo decide llamar a una herramienta externa, devuelve una instrucción JSON que especifica el nombre de la función y los parámetros de entrada.

    Si el modelo decide no llamar a una herramienta, devuelve una respuesta en lenguaje natural.

  3. Ejecutar la herramienta en la aplicación

    La aplicación ejecuta la herramienta especificada y obtiene la salida.

  4. Realizar la segunda llamada al modelo

    Agregue la salida de la herramienta al arreglo messages y llame al modelo nuevamente.

  5. Recibir la respuesta final del modelo

    El modelo combina la salida de la herramienta con la pregunta del usuario para generar una respuesta en lenguaje natural.

La siguiente figura muestra el flujo de trabajo.

imagen

Modelos admitidos

Qwen

  • Modelos de generación de texto
    • Qwen-Max: serie Qwen3.8-Max, serie Qwen3.7-Max, serie Qwen3.6-Max, serie Qwen3-Max y serie Qwen-Max
    • Qwen-Plus: serie Qwen3.7-Plus, serie Qwen3.6-Plus, serie Qwen3.5-Plus y serie Qwen-Plus.
    • Qwen-Flash: serie Qwen3.7-Flash, serie Qwen3.6-Flash, serie Qwen3.5-Flash y serie Qwen-Flash
    • Qwen-Coder: serie Qwen3-Coder, serie Qwen2.5-Coder y serie Qwen-Coder
    • Qwen-Turbo: serie Qwen-Turbo
    • Serie de código abierto Qwen3.6
    • Serie de código abierto Qwen3.5
    • Serie de código abierto Qwen3
    • Serie de código abierto Qwen2.5
    • Serie de código abierto Qwen3.8
  • Modelos multimodales
    • Qwen-VL: serie Qwen3-VL-Plus y serie Qwen3-VL-Flash
    • Qwen-Omni: Qwen3.8-Omni-Flash, serie Qwen3.5-Omni-Plus, serie Qwen3.5-Omni-Flash y serie Qwen3-Omni-Flash
    • Qwen-Omni-Realtime: Qwen3.8-Omni-Flash-Realtime, serie Qwen3.5-Omni-Plus-Realtime y serie Qwen3.5-Omni-Flash-Realtime
    • Serie de código abierto Qwen3-VL
  • Modelos de chat de voz
    • Qwen-Audio-Realtime: Qwen-Audio-3.1-Realtime-Plus, serie Qwen-Audio-3.0-Realtime-Plus y serie Qwen-Audio-3.0-Realtime-Flash

DeepSeek

  • deepseek-v4-pro
  • deepseek-v4.1-flash-1017

GLM

ImportanteCuando utilice Function Calling con modelos de la serie GLM, debe incluir extra_body={"tool_stream": True} en su solicitud. De lo contrario, el modelo no devolverá tool_calls y la llamada a la herramienta no funcionará. Para glm-5.3, tool_stream tiene como valor predeterminado true, por lo que no necesita pasarlo explícitamente.

  • glm-5.3
  • glm-5.2
  • glm-5.1
  • glm-5

Kimi

  • kimi-k2.7-code
  • kimi-k2.6
  • kimi-k2.5

MiniMax

MiniMax-M2.5

Introducción

Antes de comenzar, obtenga una clave API y configúrela como variable de entorno. Si utiliza el SDK de OpenAI o el SDK de DashScope, también debe instalar el SDK.

El siguiente ejemplo muestra el flujo completo de Function Calling para un escenario de consulta meteorológica.

Compatible con OpenAI

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

client = OpenAI(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.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": "Useful for 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 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"The weather in {location} today is {random_weather}."

# Encapsulate the model response function
def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen3.8-max",
        extra_body={"enable_thinking": False},
        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, print the content directly
if assistant_output.tool_calls is None:
    print(f"No tool call needed. Direct response: {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}], 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 response
        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 response: {assistant_output.content}")
import OpenAI from 'openai';

// Initialize the client
const openai = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY,
  // Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
  baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
});

// Define the tool list
const tools = [
  {
    type: "function",
    function: {
      name: "get_current_weather",
      description: "Useful for 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 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 `The weather in ${location} today is ${randomWeather}.`;
};

// Encapsulate the model response function
const getResponse = async (messages) => {
  const response = await openai.chat.completions.create({
    model: "qwen3.8-max",
    enable_thinking: false,
    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 if a tool call is needed
  if (!assistantOutput.tool_calls) {
    console.log(`No tool call needed. Direct response: ${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}], 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 response: ${assistantOutput.content}`);
  }
};

// Start the program
main().catch(console.error);

DashScope

import os
from dashscope import MultiModalConversation
import dashscope
import json
import random

# The following URL is for the China (Beijing) region. Replace {WorkspaceId} with your actual workspace ID. URLs vary by region.
dashscope.base_http_api_url = 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1'

# 1. Define the tool list
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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 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"The weather in {location} today is {random_weather}."

# 3. Encapsulate the model response function
def get_response(messages):
    response = MultiModalConversation.call(
        # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-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"),
        # This example uses the multimodal model qwen3.8-max. To call a text-only model such as qwen-plus, use the text-only model API. For more information, see https://www.alibabacloud.com/help/model-studio/qwen-api-via-dashscope
        model="qwen3.8-max",
        enable_thinking=False,
        messages=messages,
        tools=tools,
        result_format="message",
    )
    return response

# 4. Initialize the conversation history
messages = [
    {
        "role": "user",
        "content": [{"text": "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 response: {assistant_output['content']}")
else:
    # 7. Enter the tool calling loop
    # Loop condition: as long as the latest model response contains 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}], 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. Print the final natural language response
    content = assistant_output["content"]
    if isinstance(content, list) and content:
        content = content[0].get("text", "") if isinstance(content[0], dict) else str(content[0])
    print(f"Assistant's final response: {content}")
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.common.Role;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.exception.UploadFileException;
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.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class Main {

    /**
     * Extracts plain text from the content of a MultiModalMessage.
     * The content format is List<Map<String, String>>, for example: [{text=The weather is sunny}]
     */
    @SuppressWarnings("unchecked")
    public static String getTextContent(Object content) {
        if (content instanceof List) {
            for (Object item : (List<?>) content) {
                if (item instanceof Map) {
                    Object text = ((Map<String, Object>) item).get("text");
                    if (text != null) return text.toString();
                }
            }
        }
        return content != null ? content.toString() : "";
    }

    /**
     * Defines 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();

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

            return "The weather in " + location + " today 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 tools to the model.
            String weatherParamsSchema =
                    "{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"A city or district, such as Singapore or New York.\"}},\"required\":[\"location\"]}";

            FunctionDefinition weatherFunction = FunctionDefinition.builder()
                    .name("get_current_weather") // The unique name of the tool, which must correspond to the local implementation.
                    .description("Useful for 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();
            // The following URL is for the China (Beijing) region. Replace {WorkspaceId} with your actual workspace ID. URLs vary by region.
            MultiModalConversation conv = new MultiModalConversation(Protocol.HTTP.getValue(), "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1");
            String userInput = "What's the weather like in Singapore?";

            List<MultiModalMessage> messages = new ArrayList<>();
            messages.add(MultiModalMessage.builder().role(Role.USER.getValue())
                    .content(Arrays.asList(Collections.singletonMap("text", userInput))).build());

            // First call to the model. Send the user's request and the defined tool list to the model.
            MultiModalConversationParam param = MultiModalConversationParam.builder()
                    .model("qwen3.8-max") //This example uses the multimodal model qwen3.8-max. To call a text-only model such as qwen-plus, use the text-only model API. For more information, see https://www.alibabacloud.com/help/model-studio/qwen-api-via-dashscope
                    .enableThinking(false)
                    .apiKey(System.getenv("DASHSCOPE_API_KEY")) // Get the API key from the environment variable. API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
                    .messages(messages) // Pass the current conversation history.
                    .tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // Pass the list of available tools.
                    .build();

            MultiModalConversationResult result = conv.call(param);
            MultiModalMessage assistantOutput = result.getOutput().getChoices().get(0).getMessage();
            messages.add(assistantOutput); // Add the model's first response to the conversation history.

            // Check the model's response to determine if it requests a tool call.
            if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
                // Case A: The model does not call a tool and provides a direct answer.
                System.out.println("No tool call needed. Direct response: " + getTextContent(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 response.
                    ToolCallFunction functionCall = (ToolCallFunction) toolCall;
                    String funcName = functionCall.getFunction().getName();
                    String arguments = functionCall.getFunction().getArguments();
                    System.out.println("Calling tool [" + funcName + "], arguments: " + arguments);

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

                    // Construct a message with the role "tool" that contains the tool's execution result.
                    MultiModalMessage toolMessage = MultiModalMessage.builder()
                            .role("tool")
                            .toolCallId(toolCall.getId())
                            .content(Arrays.asList(Collections.singletonMap("text", toolResult)))
                            .build();
                    System.out.println("Tool returns: " + toolResult);
                    messages.add(toolMessage); // Add the tool's return result to the conversation history.

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

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

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

Después de ejecutar el código, se muestra la siguiente salida:

Calling tool [get_current_weather], arguments: {'location': 'Singapore'}
Tool returns: The weather in Singapore today is Cloudy.
Assistant's final response: The weather in Singapore today is cloudy.

Cómo usar

Function Calling admite dos formas de pasar información de herramientas:

  • Método 1: Pasar información mediante el parámetro tools (recomendado)

    Para más información, consulte Cómo utilizar. Siga los pasos para definir herramientas, crear un arreglo de mensajes, realizar una Function Calling, ejecutar la función de herramienta y hacer que el LLM resuma la salida de la función de herramienta.

  • Método 2: Pasar información mediante un mensaje del sistema

    Pasar información a través del parámetro tools proporciona los mejores resultados porque el servidor se adapta automáticamente a la plantilla de prompt óptima. Si está utilizando un modelo Qwen y no want usar el parámetro tools, consulte Pasar información de herramientas a través de un mensaje del sistema.

Las siguientes secciones utilizan la API compatible con OpenAI como ejemplo para describir el uso detallado de Function Calling con el parámetro tools.

Suponga un escenario empresarial que recibe dos tipos de preguntas: consultas meteorológicas y consultas de hora.

1. Definir herramientas

Las herramientas conectan los LLM a servicios externos. Primero debe definir las herramientas.

1.1. Crear funciones de herramienta

Cree dos funciones de herramienta: una herramienta de consulta meteorológica y una herramienta de consulta de hora.

  • Herramienta de consulta meteorológica

    Esta herramienta recibe el parámetro arguments. El formato de arguments es {"location": "queried location"}. La salida de la herramienta es una cadena con el formato: "{location} today is {weather}".

    Con fines de demostración, la herramienta de consulta meteorológica definida aquí no consulta realmente el clima. Selecciona aleatoriamente entre soleado, nublado o lluvioso. En un escenario empresarial real, puede reemplazar esto con una herramienta como Amap Weather.

  • Herramienta de consulta de hora

    La herramienta de consulta de hora no requiere ningún parámetro de entrada. La salida de la herramienta es una cadena con el formato: "Current time: {queried time}.".

    Si utiliza Node.js, ejecute npm install date-fns para instalar el paquete date-fns para obtener la hora.

## Step 1: Define tool functions

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

# Simulate a weather query tool. Example output: "The weather in Beijing today 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 the formatted weather information
    return f"The weather in {location} today is {random_weather}."

# A 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 the 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 the formatted weather information
    return `The weather in ${location} today 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 the subsequent steps.
console.log("Testing tool output:")
console.log(getCurrentWeather({location:"Shanghai"}));
console.log(getCurrentTime());
console.log("\n")

Después de ejecutar la herramienta, se muestra la siguiente salida:

Testing tool output:
The weather in Shanghai today is Cloudy.
Current time: 2025-01-08 20:21:45.

1.2. Crear el arreglo de herramientas

Antes de que los humanos puedan elegir una herramienta, necesitan entender su función, escenarios de uso y parámetros de entrada. Lo mismo aplica para los LLM. El modelo selecciona la herramienta adecuada basándose en esta información. Proporcione la información de la herramienta en el siguiente formato JSON.

  • El campo type se establece en "function".

  • El campo function es un objeto.

    • El campo name es un nombre personalizado de función de herramienta. Recomendamos usar el mismo nombre que la función, como get_current_weather o get_current_time.

    • El campo description describe las capacidades de la función de herramienta. El LLM consulta este campo para decidir si debe utilizar la función de herramienta.

    • El campo parameters describe los parámetros de entrada de la función de herramienta. Es un objeto. El LLM consulta este campo para extraer los parámetros de entrada. Si la función de herramienta no requiere parámetros de entrada, usted no necesita especificar el campo parameters.

      • El campo type se establece en "object".
      • El campo properties describe el nombre, el tipo de datos y la descripción de los parámetros de entrada. Es un objeto. La clave es el nombre del parámetro de entrada, y el valor es el tipo de datos y la descripción del parámetro de entrada.
      • El campo required especifica qué parámetros son obligatorios. Es un arreglo.

Para la herramienta de consulta meteorológica, el formato de la información de descripción de la herramienta es el siguiente:

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Useful for 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."
                }
            },
            "required": ["location"]
        }
    }
}

Antes de realizar una Function Calling, defina un arreglo de información de herramientas (tools) en su código. Este arreglo incluye el nombre de la función, la descripción y la definición de parámetros para cada herramienta. El arreglo se pasa como parámetro en las solicitudes posteriores.

# Paste the following code after the Step 1 code

## Step 2: Create the tools array

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Useful for when you want to know the current time.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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.",
                    }
                },
                "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 Step 1 code

// Step 2: Create the tools array

const tools = [
    {
      type: "function",
      function: {
        name: "get_current_time",
        description: "Useful for when you want to know the current time.",
        parameters: {}
      }
    },
    {
      type: "function",
      function: {
        name: "get_current_weather",
        description: "Useful for 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.",
            }
          },
          required: ["location"]
        }
      }
    }
  ];

const toolNames = tools.map(tool => tool.function.name);
console.log(`Created ${tools.length} tools: ${toolNames.join(', ')}\n`);

2. Crear el arreglo messages

Function Calling pasa instrucciones y contexto al LLM a través del arreglo messages. Antes de realizar una llamada, el arreglo messages debe contener un System Message y un User Message.

Mensaje del sistema

Aunque la función y los escenarios de uso de las herramientas se han descrito cuando usted creó el arreglo de herramientas, enfatizar aún más cuándo llamar a las herramientas en el mensaje del sistema generalmente mejora la precisión de la llamada a herramientas. Para el escenario actual, puede configurar el System Prompt en:

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.

Mensaje del usuario

El User Message se utiliza para transmitir la pregunta del usuario. Suponiendo que el usuario pregunte "Clima en Shanghái", el arreglo messages en este punto es:

# Step 3: Create the messages array
# Paste the following code after the Step 2 code
# Example 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": "Weather in Shanghai"
    }
]

# Example User Message for a multimodal 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 Step 2 code
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: "Weather in Shanghai"
    }
];
// Example User Message for a multimodal 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");

Dado que las herramientas disponibles incluyen consultas meteorológicas y de hora, también puede preguntar por la hora actual.

3. Realizar una llamada de Function Calling

Pase los elementos tools y messages creados al LLM para realizar una Function Calling. El LLM determina si debe llamar a una herramienta. Si lo hace, devuelve el nombre de la función de la herramienta y los parámetros.

Para ver los modelos compatibles, consulte Modelos compatibles.

# Step 4: Make a function calling
# Paste the following code after the Step 3 code
from openai import OpenAI
import os

client = OpenAI(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)

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

print("Making a function calling...")
completion = function_calling()
// Step 4: Make a function calling
// Paste the following code after the Step 3 code
import OpenAI from "openai";
const openai = new OpenAI(
    {
        // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
        // If you have not configured the environment variable, replace the following line with your Model Studio API key: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
        baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
    }
);

async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen3.8-max",  // This example uses qwen3.8-max. You can change the model name as needed. For a list of models, see https://www.alibabacloud.com/help/model-studio/getting-started/models
        enable_thinking: false,
        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();

Dado que el usuario preguntó sobre el clima en Shanghái, el LLM especifica el nombre de la función de herramienta a usar como "get_current_weather" y el parámetro de entrada de la función como "{\"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
        }
    ]
}

Tenga en cuenta que si el LLM determina que no se necesita ninguna herramienta para la pregunta, responderá directamente a través del parámetro content. Cuando usted ingresa "Hola", el parámetro tool_calls está vacío, y el formato del objeto devuelto es:

{
    "content": "Hello! How can I help you? I'm particularly good at answering questions about the weather or time.",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}

Si el parámetro tool_calls está vacío, su programa puede devolver directamente el content sin ejecutar los siguientes pasos.

Si usted want que el LLM seleccione una herramienta específica cada vez que realiza una Function Calling, consulte Llamada forzada a herramienta.

4. Ejecutar la función de herramienta

Ejecutar la función de herramienta traduce la decisión del modelo en una operación real.

El proceso de ejecución de la función de herramienta lo completa su entorno informático, no el LLM.

El LLM solo genera una cadena. Antes de ejecutar la función de herramienta, usted necesita analizar por separado el nombre de la función de herramienta y sus parámetros de entrada.

  • Función de herramienta

    Cree un mapeo function_mapper desde el nombre de la función de herramienta hacia la entidad de la función de herramienta para asignar la cadena de función de herramienta devuelta a la entidad de la función de herramienta.

  • Parámetros de entrada

    Los parámetros de entrada devueltos por Function Calling son una cadena JSON. Utilice una herramienta para analizarla en un objeto JSON y extraer la información de los parámetros de entrada.

Después del análisis, pase los parámetros a la función de herramienta y ejecútela para obtener el resultado de salida.

# Step 5: Run the tool function
# Paste the following code after the Step 4 code
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 parameter is 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 Step 4 code

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 parameter is 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`);

Después de ejecutar el código, se muestra la siguiente salida:

The weather in Shanghai today is Cloudy.

NotaEn escenarios empresariales reales, muchas herramientas realizan acciones específicas (como enviar correos electrónicos o cargar archivos) en lugar de consultar datos, y no generan una cadena. Recomendamos agregar información de descripción de estado (como "Correo electrónico enviado correctamente" u "Operación fallida") para dichas herramientas para ayudar al LLM a comprender el estado de ejecución.

5. Permitir que el LLM resuma la salida de la función de herramienta

El formato de salida de la función de herramienta es relativamente fijo. Devolverlo directamente al usuario podría sonar robótico. Envíe la salida de la herramienta al contexto del modelo y llame al modelo nuevamente para generar una respuesta en estilo de lenguaje natural.

  1. Agregar un mensaje de asistente

    Después de que usted realiza una Function Calling, obtiene un Assistant Message a través de completion.choices[0].message. Primero, agréguelo al arreglo messages.

  2. Agregar un mensaje de herramienta

    Agregue la salida de la herramienta al arreglo messages en el formato {"role": "tool", "content": "tool output", "tool_call_id": completion.choices[0].message.tool_calls[0].id}.

    Nota

    • Asegúrese de que la salida de la herramienta esté en formato de cadena.
    • tool_call_id es un identificador único generado por el sistema para cada solicitud de llamada a herramienta. El modelo puede solicitar llamar a varias herramientas a la vez. Al devolver múltiples resultados de herramientas al modelo, tool_call_id garantiza que el resultado de salida de la herramienta pueda coincidir con su intención de llamada.
# Step 6: Submit the tool output to the LLM
# Paste the following code after the Step 5 code

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 LLM
// Paste the following code after the Step 5 code

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");

En este punto, el arreglo messages es:

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

Después de actualizar el arreglo messages, ejecute el siguiente código.

# Step 7: Let the LLM summarize the tool output
# Paste the following code after the Step 6 code
print("Summarizing the tool output...")
completion = function_calling()
// Step 7: Let the LLM summarize the tool output
// Paste the following code after the Step 6 code

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

Usted puede recuperar el contenido de la respuesta desde content: "El clima en Shanghái hoy está nublado. Si tiene alguna otra pregunta, no dude en consultar".

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

Ahora ha completado un flujo completo de Function Calling.

Uso avanzado

Especificar el método de llamada a herramientas

Llamada a herramientas en paralelo

kimi-k3 no admite controlar mediante parallel_tool_calls si las llamadas a herramientas son paralelas: establecerlo en false puede seguir devolviendo varios tool_calls en un mismo turno. Recorra y procese todas las llamadas a herramientas que devuelva realmente el modelo.

Una consulta del clima de una sola ciudad requiere solo una llamada a herramienta. Si una pregunta requiere múltiples llamadas a herramientas, como "¿Cómo está el clima en Beijing y Shanghái?" o "¿Cómo está el clima en Hangzhou y qué hora es ahora?", el modelo puede devolver varias llamadas a herramientas en una sola respuesta después de que usted realice una llamada de función, ya que parallel_tool_calls tiene el valor predeterminado true. Por ejemplo, si pregunta "¿Cómo está el clima en Beijing y Shanghái?", el arreglo tool_calls en el objeto devuelto contiene la información de parámetros de entrada tanto para Pekín como para Shanghái:

{
    "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"
        }
    ]
}

Para que el modelo devuelva solo una llamada a herramienta cada vez, establezca el parámetro de solicitud parallel_tool_calls en false cuando realice una llamada de función:

def function_calling():
    completion = client.chat.completions.create(
        model="qwen3.8-max",  # This example uses qwen3.8-max. You can change the model name as needed.
        extra_body={"enable_thinking": False},
        messages=messages,
        tools=tools,
        # Con False, solo se devuelve una llamada a herramienta cada vez
        parallel_tool_calls=False
    )
    print("Returned object:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

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

const completion = await functionCalling();

El resultado devuelto contiene solo los parámetros de entrada para Beijing:

{
    "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
        }
    ]
}

NotaLa llamada paralela a herramientas es adecuada para tareas que no tienen dependencias. Si existen dependencias entre tareas (la entrada de la herramienta A está relacionada con la salida de la herramienta B), consulte Introducción para implementar llamadas a herramientas en serie (llamar a una herramienta a la vez) a través de un bucle while.

Llamada forzada a herramientas

Los LLM generan contenido con un grado de incertidumbre y pueden elegir la herramienta incorrecta. Para forzar el uso o la desactivación de una herramienta específica para un determinado tipo de pregunta, puede modificar el parámetro tool_choice. El valor predeterminado del parámetro tool_choice es "auto", lo que significa que el LLM decide autónomamente cómo realizar una llamada a herramienta.

Cuando el LLM resuma la salida de la función de herramienta, elimine el parámetro tool_choice. De lo contrario, la API seguirá devolviendo información de llamada a la herramienta.

  • Forzar el uso de una herramienta específica

    Si want que Function Calling fuerce la llamada a una herramienta específica para un determinado tipo de pregunta, puede establecer el parámetro tool_choice en {"type": "function", "function": {"name": "the_function_to_call"}}. El LLM no participará en la selección de herramientas y solo generará la información del parámetro de entrada.

    Suponiendo que el escenario actual solo involucra preguntas de consulta meteorológica, puede modificar el código function_calling a:

def function_calling():
    completion = client.chat.completions.create(
        model="qwen3.8-max",
        extra_body={"enable_thinking": False},
        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: "qwen3.8-max",
        enable_thinking: false,
        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();

Sin importar qué pregunta se ingrese, la función de herramienta en el objeto devuelto será get_current_weather.

Antes de utilizar esta estrategia, asegúrese de que la pregunta esté relacionada con la herramienta seleccionada. De lo contrario, podría devolver resultados inesperados.

Forzar el uso de al menos una herramienta

Para algunas preguntas que requieren una herramienta, el LLM puede decidir que no se necesita ninguna llamada. Para forzar a Function Calling a realizar siempre una llamada a herramienta (el parámetro tool_calls en el objeto devuelto no está vacío), puede establecer el parámetro tool_choice en "required". Function Calling entonces siempre devolverá información de la herramienta y de los parámetros de entrada.

Los modelos de la serie Qwen no admiten configurar tool_choice como "required": en modo sin pensamiento, required no puede garantizar que siempre se llame a una herramienta, y en modo de pensamiento no se admite "required".

Suponiendo que todas las preguntas en el escenario actual requieren una llamada a herramienta, puede modificar el código function_calling a:

def function_calling():
    completion = client.chat.completions.create(
        model="kimi/kimi-k3",
        messages=messages,
        tools=tools,
        tool_choice="required"
    )
    print(completion.model_dump_json())

function_calling()
async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "kimi/kimi-k3",
        messages: messages,
        tools: tools,
        tool_choice: "required"
    });
    console.log("Returned object:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

Sin importar qué pregunta se ingrese, el parámetro tool_calls en el objeto devuelto nunca estará vacío.

Antes de utilizar esta estrategia, asegúrese de que la pregunta esté relacionada con las herramientas. De lo contrario, podría devolver resultados inesperados.

  • Forzar no usar herramientas

    Si necesita que Function Calling nunca realice una llamada a herramienta (el objeto devuelto contiene contenido de respuesta en content y el parámetro tool_calls está vacío), puede establecer el parámetro tool_choice en "none", o no pasar el parámetro tools. El parámetro tool_calls devuelto por Function Calling siempre estará vacío.

    Suponiendo que ninguna pregunta en el escenario actual requiere una llamada a herramienta, puede modificar el código function_calling a:

def function_calling():
    completion = client.chat.completions.create(
        model="qwen3.8-max",
        extra_body={"enable_thinking": False},
        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: "qwen3.8-max",
        enable_thinking: false,
        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();

Conversación multironda

Un usuario podría preguntar "Clima en Beijing" en el primer turno, y luego "¿Qué tal en Shanghái?" en el segundo. Si el contexto del modelo carece de la información del primer turno, el modelo no puede determinar qué herramienta llamar. En un escenario de conversación de múltiples turnos, mantenga el arreglo messages completo después de cada turno. Agregue el nuevo mensaje de usuario a este arreglo y luego realice una llamada de función y los pasos subsiguientes. La estructura de messages es la siguiente:

[
  System Message -- Guides the model's tool calling strategy
  User Message -- The user's question
  Assistant Message -- The tool calling 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
]

Salida por secuencias

El uso de salida en streaming le permite obtener el nombre de la función de herramienta y la información de los parámetros de entrada en tiempo real, lo que mejora la experiencia del usuario. En este caso:

  • La información de parámetros para la llamada a la herramienta se devuelve en fragmentos como un flujo de datos.
  • El nombre de la función de herramienta se devuelve en el primer fragmento de datos de la respuesta de transmisión.
from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

stream = client.chat.completions.create(
    model="qwen3.8-max",
    extra_body={"enable_thinking": False},
    messages=[{"role": "user", "content": "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(
    {
        // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
        // If you have not configured the environment variable, replace the following line with your Model Studio API key: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // China (Beijing) region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
        baseURL: "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "Useful for 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."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen3.8-max",
    enable_thinking: false,
    messages: [{ role: "user", content: "Weather in Beijing" }],
    tools: tools,
    stream: true,
});

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

Después de ejecutar el código, se muestra la siguiente salida:

[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

Ejecute el siguiente código para ensamblar la información del parámetro de entrada (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]));

Se muestra la siguiente salida:

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

En el paso donde el LLM resume la salida de la función de herramienta, el Assistant Message agregado debe cumplir con el formato siguiente. Simplemente reemplace los elementos en tool_calls a continuación con el contenido anterior.

{
    "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,
        }
    ],
}

Llamada a herramientas con la API Responses

Los ejemplos anteriores se basan en las API de OpenAI Chat Completions y DashScope. Si usted utiliza la API de OpenAI Responses, el proceso general es el mismo, pero el formato de la API presenta las siguientes diferencias:

DimensiónChat CompletionsAPI de Responses

Formato de definición de herramienta

{
    "type": "function",
    "function": {
        "name":...,
        "parameters":...
    }
}
{
    "type": "function",
    "name":...,
    "parameters":...
}

Salida de llamada de herramienta

response.choices[0].message.tool_calls

Elementos en response.output donde type es function_call

Devolución de resultado de herramienta

{
    "role": "tool",
    "tool_call_id":...,
    "content":...
}
{
    "type": "function_call_output",
    "call_id":...,
    "output":...
}

Respuesta final

response.choices[0].message.content

response.output_text

from openai import OpenAI
import json
import os
import random

# Initialize the client
client = OpenAI(
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.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",
        "name": "get_current_weather",
        "description": "Useful for 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 Singapore or London.",
                }
            },
            "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"The weather in {location} today is {random_weather}."

# Encapsulate the model response function
def get_response(input_data):
    response = client.responses.create(
        model="qwen3.8-max",
        extra_body={"enable_thinking": False},
        input=input_data,
        tools=tools,
    )
    return response

# Maintain the conversation context
conversation = [{"role": "user", "content": USER_QUESTION}]

response = get_response(conversation)
function_calls = [item for item in response.output if item.type == "function_call"]
# If no tool call is needed, print the content directly
if not function_calls:
    print(f"Assistant's final response: {response.output_text}")
else:
    # Enter the tool calling loop
    while function_calls:
        for fc in function_calls:
            func_name = fc.name
            arguments = json.loads(fc.arguments)
            print(f"Calling tool [{func_name}], arguments: {arguments}")
            # Run the tool
            tool_result = get_current_weather(arguments)
            print(f"Tool returns: {tool_result}")
            # Append the tool call and result as a pair to the context
            conversation.append(
                {
                    "type": "function_call",
                    "name": fc.name,
                    "arguments": fc.arguments,
                    "call_id": fc.call_id,
                }
            )
            conversation.append(
                {
                    "type": "function_call_output",
                    "call_id": fc.call_id,
                    "output": tool_result,
                }
            )
        # Call the model again with the full context
        response = get_response(conversation)
        function_calls = [
            item for item in response.output if item.type == "function_call"
        ]
    print(f"Assistant's final response: {response.output_text}")
import OpenAI from "openai";

// Initialize the client
const openai = new OpenAI({
  // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
  // If you have not configured the environment variable, replace the following line with your Model Studio API key: apiKey: "sk-xxx",
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL:
    "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
});

// Define the tool list
const tools = [
  {
    type: "function",
    name: "get_current_weather",
    description: "Useful for 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 Singapore or London.",
        },
      },
      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 `The weather in ${location} today is ${randomWeather}.`;
};

// Encapsulate the model response function
const getResponse = async (inputData) => {
  const response = await openai.responses.create({
    model: "qwen3.8-max",
    enable_thinking: false,
    input: inputData,
    tools: tools,
  });
  return response;
};

const main = async () => {
  const userQuestion = "Weather in Singapore";

  // Maintain the conversation context
  const conversation = [{ role: "user", content: userQuestion }];

  let response = await getResponse(conversation);
  let functionCalls = response.output.filter(
    (item) => item.type === "function_call"
  );
  // If no tool call is needed, print the content directly
  if (functionCalls.length === 0) {
    console.log(`Assistant's final response: ${response.output_text}`);
  } else {
    // Enter the tool calling loop
    while (functionCalls.length > 0) {
      for (const fc of functionCalls) {
        const funcName = fc.name;
        const args = JSON.parse(fc.arguments);
        console.log(`Calling tool [${funcName}], arguments:`, args);
        // Run the tool
        const toolResult = getCurrentWeather(args);
        console.log(`Tool returns: ${toolResult}`);
        // Append the tool call and result as a pair to the context
        conversation.push({
          type: "function_call",
          name: fc.name,
          arguments: fc.arguments,
          call_id: fc.call_id,
        });
        conversation.push({
          type: "function_call_output",
          call_id: fc.call_id,
          output: toolResult,
        });
      }
      // Call the model again with the full context
      response = await getResponse(conversation);
      functionCalls = response.output.filter(
        (item) => item.type === "function_call"
      );
    }
    console.log(`Assistant's final response: ${response.output_text}`);
  }
};

// Start the program
main().catch(console.error);

Llamada a herramientas para modelos omnimodales

Los modelos omnimodales admiten la llamada a herramientas. Los métodos de llamada para la serie Qwen-Omni y la serie Qwen-Omni-Realtime son diferentes.

Serie Qwen-Omni

Utilice qwen3.8-omni-flash para llamadas a herramientas basadas en texto mediante Chat Completions o Responses. Los ejemplos de Python y JavaScript a continuación utilizan transmisión por secuencias de Chat Completions y leen deltas de texto y llamadas a herramientas con el pensamiento desactivado. Para entradas de audio/video, consulte Qwen3.8-Omni-Flash.

Los ejemplos establecen reasoning_effort="none" para desactivar el pensamiento. Para llamadas a herramientas que requieren pensamiento, omita el parámetro para usar el valor predeterminado xhigh, y lea reasoning_content, content y tool_calls por separado. En turnos posteriores, incluya el mensaje anterior del asistente con su razonamiento, respuesta y llamadas a herramientas en el historial, seguido de los resultados de las herramientas. preserve_thinking está activado de forma predeterminada. Consulte Pasar contenido de razonamiento.

Primero instale el SDK de OpenAI, configure DASHSCOPE_API_KEY y establezca DASHSCOPE_BASE_URL en el endpoint de Chat Completions de su espacio de trabajo. Utilice la misma región para la clave de API y el endpoint.

from openai import OpenAI
import os

client = OpenAI(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    api_key=os.getenv("DASHSCOPE_API_KEY"),

    base_url=os.environ["DASHSCOPE_BASE_URL"],
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

completion = client.chat.completions.create(
    model="qwen3.8-omni-flash",
    reasoning_effort="none",
    messages=[{"role": "user", "content": "Weather in Hangzhou?"}],
    stream=True,
    tools=tools
)

for chunk in completion:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta
    if delta.content:
        print("Text:", delta.content)
    if delta.tool_calls:
        print(delta.tool_calls)
import { OpenAI } from "openai";

const openai = new OpenAI(
    {
        // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
        // If you have not configured the environment variable, replace the following line with your Model Studio API key: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,

        baseURL: process.env.DASHSCOPE_BASE_URL
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "Useful for 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."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen3.8-omni-flash",
    reasoning_effort: "none",
    messages: [
        {
            "role": "user",
            "content": "Weather in Hangzhou"
        }],
    stream: true,
    tools:tools
});

for await (const chunk of stream) {
    const delta = chunk.choices?.[0]?.delta;
    if (!delta) continue;
    if (delta.content) console.log("Text:", delta.content);
    if (delta.tool_calls) console.log(delta.tool_calls);
}

A continuación se ilustran los deltas de llamadas a herramientas en Python. La salida real varía, y el nombre de la función y los argumentos pueden llegar en fragmentos separados:

[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')]

Para el código que ensambla la información de los parámetros de entrada (arguments), consulte Salida en streaming.

Las llamadas existentes a Qwen3.5-Omni-Plus, Qwen3.5-Omni-Flash y Qwen3-Omni-Flash requieren stream=True. Use modalities=["text"] al obtener información de herramientas; omita los fragmentos de audio cuando Qwen3.5-Omni también genere audio.

Serie Qwen-Omni-Realtime

Qwen3.8-Omni-Flash-Realtime, Qwen3.5-Omni-Plus-Realtime y Qwen3.5-Omni-Flash-Realtime admiten Function Calling personalizado para conversaciones de voz. El siguiente flujo de trabajo utiliza el SDK de DashScope o el protocolo WebSocket nativo. Para la conexión y configuración del SDK, consulte la guía en tiempo real.

Para las herramientas MCP ejecutadas en el servidor y el flujo de aprobación en Qwen3.8-Omni-Flash-Realtime, consulte Interacción MCP.

Flujo de trabajo:

Después de establecer una conexión WebSocket, pase la definición de herramienta mediante session.update para entrar en el siguiente flujo de interacción:

Fase 1: Entrada de voz y llamada a herramientas
  1. El usuario hace una pregunta por voz. El cliente recopila el audio y lo envía al servidor (correspondiente al método append_audio()). Después de que el VAD del servidor detecta el fin del habla, realiza la inferencia del modelo y determina que es necesario llamar a una herramienta.
  2. El servidor devuelve la información de la llamada a la herramienta al cliente (correspondiente al evento response.function_call_arguments.done), incluyendo el nombre de la función (name), los parámetros de entrada de la función (arguments) y el identificador de llamada (call_id). Un ejemplo es el siguiente:
{
    "type": "response.function_call_arguments.done",
    "response_id": "resp_JnTOsWXlFhKcFohZbtfz6",
    "item_id": "item_Rhcms7CauTNsQprV5S4Hr",
    "output_index": 0,
    "name": "get_current_weather",
    "call_id": "call_2be200f4cafe419b9530dd",
    "arguments": "{\"location\": \"Hangzhou\"}"
}
  1. El cliente ejecuta localmente la función de herramienta correspondiente basándose en el nombre de la función y los parámetros de entrada para obtener el resultado de la ejecución.
Fase 2: El cliente envía los resultados de la herramienta y activa la respuesta final
  1. El cliente envía el resultado de la ejecución de la herramienta de vuelta al servidor (correspondiente al evento conversation.item.create), incluyendo el identificador de llamada (call_id) y el resultado de la ejecución (output). Un ejemplo es el siguiente:
{
    "type": "conversation.item.create",
    "item": {
        "type": "function_call_output",
        "call_id": "call_2be200f4cafe419b9530dd",
        "output": "The weather in Hangzhou today is sunny, with a temperature of 25°C and a light breeze."
    }
}
  1. El cliente continúa enviando un evento response.create para activar al servidor a generar la respuesta de voz final basada en el resultado de la ejecución de la herramienta.
  2. El cliente recibe la voz y el texto devueltos por el servidor (correspondientes a los eventos response.audio.delta y response.audio_transcript.delta) y reproduce la respuesta de voz al usuario.

La serie Qwen-Omni-Realtime no admite los parámetros tool_choice y parallel_tool_calls.

Para más información sobre Qwen-Omni-Realtime, consulte Tiempo real (Qwen-Omni-Realtime), Eventos del cliente y Eventos del lado del servidor.

import os
import uuid
import threading
import traceback
import json
import base64
import signal
import sys
import time
from typing import Dict, Any, Optional, List
import pyaudio
import queue
import contextlib
import dashscope
from dashscope.audio.qwen_omni import *

# ==================== Constant Definitions ====================
VOICE = 'Tina'
MODEL = "qwen3.5-omni-plus-realtime"
# To access the Beijing region, replace WS_URL with: wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime
WS_URL = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime"
# Configure the API key. If you have not set the environment variable, replace the following line with your API key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
AUDIO_SAMPLE_RATE = 16000
AUDIO_CHUNK_SIZE = 3200
OUTPUT_AUDIO_SAMPLE_RATE = 24000

# ==================== Tool Definitions ====================
def get_train_price(src: str, dst: str) -> str:
    """Query train ticket prices"""
    return f"The train ticket price from {src} to {dst} is 100-200 CNY."

def get_flight_price(src: str, dst: str) -> str:
    """Query flight ticket prices"""
    return f"The flight ticket price from {src} to {dst} is 200-300 USD."

def get_current_weather(location: str) -> str:
    """Query the weather in a specific city"""
    return f"The weather in {location} today is changing from haze to sunny, with a temperature of 4/-4°C and a light breeze."

# Unified OpenAI format tool definitions
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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.",
                    }
                },
                "required": ["location"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_flight_price",
            "description": "Useful for when you want to query flight ticket prices.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "The departure city of the flight, such as Beijing or Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "The arrival city of the flight, such as Beijing or Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_train_price",
            "description": "Useful for when you want to query train ticket prices.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "The departure city of the train, such as Beijing or Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "The arrival city of the train, such as Beijing or Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
]

# Mapping of tool names to functions
TOOL_FUNCTIONS = {
    "get_current_weather": get_current_weather,
    "get_flight_price": get_flight_price,
    "get_train_price": get_train_price,
}

# ==================== Tool Call Handling ====================
def handle_tool_call(tool_call_response: Dict[str, Any]) -> Dict[str, Any]:
    """
    Handles a tool call request

    Args:
        tool_call_response: Tool call information containing name, arguments, and call_id

    Returns:
        Updated tool call response containing the output field
    """
    try:
        function_name = tool_call_response['name']
        tool_call_arguments = json.loads(tool_call_response['arguments'])

        print(f'[Tool Call] Start processing: name={function_name}, args={tool_call_arguments}')

        # Find the corresponding function
        if function_name not in TOOL_FUNCTIONS:
            tool_call_response['output'] = f"Client did not find the tool: {function_name}"
            print(f'[Tool Call] Error: Tool not found {function_name}')
            return tool_call_response

        # Call the function
        func = TOOL_FUNCTIONS[function_name]
        result = func(**tool_call_arguments)
        tool_call_response['output'] = result

        print(f'[Tool Call] Completed: {result}')
        return tool_call_response

    except Exception as e:
        error_msg = f"Tool call failed: {str(e)}"
        tool_call_response['output'] = error_msg
        print(f'[Tool Call] Exception: {error_msg}')
        traceback.print_exc()
        return tool_call_response

def send_tool_call_response(conversation: OmniRealtimeConversation, response: Dict[str, Any]) -> None:
    """Sends the tool call result to the server"""
    conversation.create_item({
        "id": 'item_' + uuid.uuid4().hex,
        "type": "function_call_output",
        "call_id": response['call_id'],
        "output": response["output"],
    })

# ==================== PCM Audio Player ====================
class PCMPlayer:
    """
    PCM Audio Player

    Uses a dual-thread architecture for real-time audio playback:
    - Decoding thread: Decodes base64-encoded audio data into raw PCM data
    - Playback thread: Writes PCM data to the audio output device

    Supports dynamically adding audio data, canceling playback, saving audio files, etc.
    """

    def __init__(self, pya: pyaudio.PyAudio, sample_rate=24000, chunk_size_ms=100, save_file=False):
        """
        Initializes the PCM player

        Args:
            pya: pyaudio.PyAudio instance
            sample_rate: Audio sampling rate (Hz), default 24000
            chunk_size_ms: Audio chunk size (milliseconds), affects playback cancellation latency, default 100ms
            save_file: Whether to save the played audio to a file (result.pcm), default False
        """

        self.pya = pya
        self.sample_rate = sample_rate
        self.chunk_size_bytes = chunk_size_ms * sample_rate * 2 // 1000
        self.player_stream = pya.open(format=pyaudio.paInt16,
                                       channels=1,
                                       rate=sample_rate,
                                       output=True)

        self.raw_audio_buffer: queue.Queue = queue.Queue()
        self.b64_audio_buffer: queue.Queue = queue.Queue()
        self.status_lock = threading.Lock()
        self.status = 'playing'
        self.decoder_thread = threading.Thread(target=self.decoder_loop)
        self.player_thread = threading.Thread(target=self.player_loop)
        self.decoder_thread.start()
        self.player_thread.start()
        self.complete_event: threading.Event = None
        self.save_file = save_file
        if self.save_file:
            self.out_file = open('result.pcm', 'wb')

    def decoder_loop(self):
        """Decoding thread: Decodes base64 audio data into raw PCM data"""
        while self.status != 'stop':
            recv_audio_b64 = None
            with contextlib.suppress(queue.Empty):
                recv_audio_b64 = self.b64_audio_buffer.get(timeout=0.1)
            if recv_audio_b64 is None:
                continue
            recv_audio_raw = base64.b64decode(recv_audio_b64)
            # push raw audio data into queue by chunk
            for i in range(0, len(recv_audio_raw), self.chunk_size_bytes):
                chunk = recv_audio_raw[i:i + self.chunk_size_bytes]
                self.raw_audio_buffer.put(chunk)
                if self.save_file:
                    self.out_file.write(chunk)

    def player_loop(self):
        """Playback thread: Writes PCM data to the audio output device"""
        while self.status != 'stop':
            recv_audio_raw = None
            with contextlib.suppress(queue.Empty):
                recv_audio_raw = self.raw_audio_buffer.get(timeout=0.1)
            if recv_audio_raw is None:
                if self.complete_event:
                    self.complete_event.set()
                continue
            # write chunk to pyaudio audio player, wait until finish playing this chunk.
            self.player_stream.write(recv_audio_raw)

    def cancel_playing(self):
        """Cancel playback: Clear all buffer queues"""
        self.b64_audio_buffer.queue.clear()
        self.raw_audio_buffer.queue.clear()

    def add_data(self, data):
        """Add base64-encoded audio data to the playback queue"""
        self.b64_audio_buffer.put(data)

    def wait_for_complete(self):
        """Wait for playback to complete"""
        self.complete_event = threading.Event()
        self.complete_event.wait()
        self.complete_event = None

    def shutdown(self):
        """Shut down the player and release resources"""
        self.status = 'stop'
        self.decoder_thread.join()
        self.player_thread.join()
        self.player_stream.close()
        if self.save_file:
            self.out_file.close()

# ==================== Audio Manager ====================
class AudioManager:
    """Manages audio input and output resources"""

    def __init__(self):
        self.pya: Optional[pyaudio.PyAudio] = None
        self.mic_stream: Optional[pyaudio.Stream] = None
        self.player: Optional[PCMPlayer] = None

    def initialize(self) -> None:
        """Initialize audio devices"""
        print('Initializing audio devices...')
        self.pya = pyaudio.PyAudio()
        self.mic_stream = self.pya.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=AUDIO_SAMPLE_RATE,
            input=True
        )
        self.player = PCMPlayer(self.pya, sample_rate=OUTPUT_AUDIO_SAMPLE_RATE)
        print('Audio devices initialized')

    def read_audio_chunk(self) -> Optional[bytes]:
        """Read an audio data chunk"""
        if not self.mic_stream:
            return None
        try:
            return self.mic_stream.read(AUDIO_CHUNK_SIZE, exception_on_overflow=False)
        except Exception as e:
            print(f'[Error] Failed to read audio data: {e}')
            return None

    def cleanup(self) -> None:
        """Clean up audio resources"""
        print('Cleaning up audio resources...')
        if self.player:
            self.player.shutdown()
        if self.mic_stream:
            self.mic_stream.close()
        if self.pya:
            self.pya.terminate()
        print('Audio resources cleaned up')

# ==================== Callback Handler ====================
class OmniCallback(OmniRealtimeCallback):
    """Omni real-time conversation callback handler"""

    def __init__(self, audio_manager: AudioManager):
        self.audio_manager = audio_manager
        self.tool_calls: Dict[str, Dict[str, Any]] = {}
        self.all_response_text: str = ''
        self.last_package_time: float = 0
        self.is_first_text: bool = True
        self.is_first_audio: bool = True
        self.conversation: Optional[OmniRealtimeConversation] = None

    def set_conversation(self, conversation: OmniRealtimeConversation) -> None:
        """Set the conversation instance reference"""
        self.conversation = conversation

    def on_open(self) -> None:
        """Callback on connection establishment"""
        print('Connection established')
        self.audio_manager.initialize()
        self.last_package_time = time.time() * 1000
        self.is_first_text = True
        self.is_first_audio = True
        self.tool_calls = {}
        self.all_response_text = ''

    def on_close(self, close_status_code: int, close_msg: str) -> None:
        """Callback on connection closure"""
        print(f'Connection closed: code={close_status_code}, msg={close_msg}')
        self.audio_manager.cleanup()
        sys.exit(0)

    def on_event(self, response: Dict[str, Any]) -> None:
        """Handle event callbacks"""
        try:
            event_type = response.get('type', '')

            # Session created
            if event_type == 'session.created':
                print(f'Session started: {response["session"]["id"]}')

            # Speech-to-text completed
            elif event_type == 'conversation.item.input_audio_transcription.completed':
                print(f'User question: {response.get("transcript", "")}')

            # Incremental text response
            elif event_type in ('response.audio_transcript.delta', 'response.text.delta'):
                if self.is_first_text:
                    self.is_first_text = False
                    latency = time.time() * 1000 - self.last_package_time
                    print(f'Time to first token (VAD end): {latency:.0f} ms')

                text = response.get('delta', '')
                self.all_response_text += text

            # Incremental audio response
            elif event_type == 'response.audio.delta':
                if self.is_first_audio:
                    self.is_first_audio = False
                    latency = time.time() * 1000 - self.last_package_time
                    print(f'Time to first audio (VAD end): {latency:.0f} ms')

                audio_interval = time.time() * 1000 - self.last_package_time
                print(f'Audio interval: {audio_interval:.0f} ms')
                self.last_package_time = time.time() * 1000

                recv_audio_b64 = response.get('delta', '')
                if self.audio_manager.player:
                    self.audio_manager.player.add_data(recv_audio_b64)

            # VAD detected speech start
            elif event_type == 'input_audio_buffer.speech_started':
                print('====== VAD detected speech start ======')
                if self.audio_manager.player:
                    self.audio_manager.player.cancel_playing()

            # VAD detected speech end
            elif event_type == 'input_audio_buffer.speech_stopped':
                print('====== VAD detected speech end ======')
                self.last_package_time = time.time() * 1000
                self.is_first_text = True
                self.is_first_audio = True
                self.tool_calls = {}

            # Function call arguments completed
            elif event_type == 'response.function_call_arguments.done':
                print('====== Received tool call request ======')
                call_id = response.get('call_id', '')
                self.tool_calls[call_id] = response.copy()
                self.tool_calls[call_id]['processed'] = False

            # Response completed
            elif event_type == 'response.done':
                print('====== Response completed ======')
                print(f'Full response: {self.all_response_text}')

                if self.conversation:
                    response_id = self.conversation.get_last_response_id()
                    text_delay = self.conversation.get_last_first_text_delay()
                    audio_delay = self.conversation.get_last_first_audio_delay()

                    # Print detailed metrics only when all are available
                    if response_id is not None and text_delay is not None and audio_delay is not None:
                        print(f'[Metric] Response ID: {response_id}, '
                              f'Time to first token: {text_delay:.0f}ms, '
                              f'Time to first audio: {audio_delay:.0f}ms')
                    else:
                        print('[Metric] Metric information is temporarily unavailable (possibly a response after a tool call)')

                self.all_response_text = ''

        except Exception as e:
            print(f'[Error] Exception handling event: {e}')
            traceback.print_exc()

    def process_pending_tool_calls(self) -> bool:
        """
        Processes pending tool calls

        Returns:
            Whether there are new tool calls that need a response
        """
        has_pending = False

        for call_id, tool_call in self.tool_calls.items():
            if not tool_call.get('processed', False):
                has_pending = True
                tool_call['processed'] = True

                # Handle the tool call
                result = handle_tool_call(tool_call)

                # Send the result to the server
                if self.conversation:
                    send_tool_call_response(self.conversation, result)

        return has_pending

# ==================== Main Program ====================
def main():
    """Main function"""
    print('Initializing Omni real-time conversation...')

    # Create an audio manager
    audio_manager = AudioManager()

    # Create a callback handler
    callback = OmniCallback(audio_manager)

    # Create a conversation instance
    conversation = OmniRealtimeConversation(
        api_key=dashscope.api_key,
        url=WS_URL,
        model=MODEL,
        callback=callback,
    )

    # Set the conversation reference in the callback
    callback.set_conversation(conversation)

    # Establish the connection
    conversation.connect()

    # Configure session parameters
    omni_output_modalities = [MultiModality.AUDIO, MultiModality.TEXT]

    conversation.update_session(
        output_modalities=omni_output_modalities,
        voice=VOICE,
        input_audio_format=AudioFormat.PCM_16000HZ_MONO_16BIT,
        output_audio_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
        enable_input_audio_transcription=True,
        enable_turn_detection=True,
        turn_detection_type='server_vad',
        tools=TOOLS,
    )

    # Set up signal handling
    def signal_handler(sig, frame):
        print('\nReceived Ctrl+C, stopping...')
        conversation.close()
        audio_manager.cleanup()
        print('Omni real-time conversation stopped')
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    print("Press Ctrl+C to stop the conversation...\n")

    # Main loop: continuously send audio and check for tool calls
    try:
        while True:
            # Process pending tool calls
            has_tool_calls = callback.process_pending_tool_calls()

            if has_tool_calls:
                print("*** Tool call completed, creating new response ***")
                conversation.create_response(
                    instructions=None,
                    output_modalities=omni_output_modalities
                )
                print('====== Tool call processing completed ======\n')

            # Read and send audio data
            audio_data = audio_manager.read_audio_chunk()
            if audio_data:
                audio_b64 = base64.b64encode(audio_data).decode('ascii')
                conversation.append_audio(audio_b64)
            else:
                break

    except KeyboardInterrupt:
        signal_handler(signal.SIGINT, None)
    except Exception as e:
        print(f'[Error] Main loop exception: {e}')
        traceback.print_exc()
    finally:
        conversation.close()
        audio_manager.cleanup()

if __name__ == '__main__':
    main()
import com.alibaba.dashscope.audio.omni.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

public class Main {

    public static void main(String[] args) {
        try {
            // Initialize components
            AudioPlayer audioPlayer = new AudioPlayer();
            ToolRegistry toolRegistry = new ToolRegistry();
            ConversationHandler handler = new ConversationHandler(audioPlayer, toolRegistry);

            // Create and configure the session
            OmniRealtimeParam param = OmniRealtimeParam.builder()
                    .model("qwen3.5-omni-plus-realtime")
                    .apikey(System.getenv("DASHSCOPE_API_KEY"))
                    // To access the Beijing region, replace the url with: wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime
                    .url("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime")
                    .build();

            OmniRealtimeConversation conversation = new OmniRealtimeConversation(param, handler);
            conversation.connect();

            // Configure session parameters
            configureSession(conversation, toolRegistry);

            // Start audio capture
            startAudioCapture(conversation, handler);

            // Clean up resources
            cleanup(conversation, audioPlayer);

        } catch (NoApiKeyException e) {
            System.err.println("API KEY not found: Please set the DASHSCOPE_API_KEY environment variable");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void configureSession(OmniRealtimeConversation conversation, ToolRegistry toolRegistry) {
        HashMap<String, Object> additionalConfig = new HashMap<>();
        additionalConfig.put("tools", toolRegistry.buildToolsDefinition());

        conversation.updateSession(OmniRealtimeConfig.builder()
                .modalities(Arrays.asList(OmniRealtimeModality.AUDIO, OmniRealtimeModality.TEXT))
                .voice("Tina")
                .enableTurnDetection(true)
                .enableInputAudioTranscription(true)
                .parameters(additionalConfig)
                .build());

        System.out.println("Tool calling is enabled. Please start speaking (Press Ctrl+C to exit)...");
    }

    private static void startAudioCapture(OmniRealtimeConversation conversation, ConversationHandler handler)
            throws LineUnavailableException {
        AudioFormat format = new AudioFormat(16000, 16, 1, true, false);
        TargetDataLine mic = AudioSystem.getTargetDataLine(format);
        mic.open(format);
        mic.start();

        ByteBuffer buffer = ByteBuffer.allocate(3200);
        while (!handler.getShouldStop().get()) {
            int bytesRead = mic.read(buffer.array(), 0, buffer.capacity());
            if (bytesRead > 0) {
                conversation.appendAudio(Base64.getEncoder().encodeToString(buffer.array()));

                // Check and process pending tool calls
                if (handler.hasPendingToolCalls()) {
                    System.out.println("*** create response after call tools");
                    handler.processPendingToolCalls(conversation);
                    conversation.createResponse(null, Arrays.asList(OmniRealtimeModality.AUDIO, OmniRealtimeModality.TEXT));
                    System.out.println("======TOOL CALL END======");
                }
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException ignored) {}
        }

        mic.close();
    }

    private static void cleanup(OmniRealtimeConversation conversation, AudioPlayer audioPlayer) {
        try {
            conversation.close(1000, "Normal exit");
            audioPlayer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Audio Player - Responsible for sequential playback of audio data
     */
    static class AudioPlayer {
        private final SourceDataLine line;
        private final Queue<byte[]> audioQueue = new ConcurrentLinkedQueue<>();
        private final Thread playerThread;
        private final AtomicBoolean shouldStop = new AtomicBoolean(false);

        public AudioPlayer() throws LineUnavailableException {
            AudioFormat format = new AudioFormat(24000, 16, 1, true, false);
            line = AudioSystem.getSourceDataLine(format);
            line.open(format);
            line.start();

            playerThread = new Thread(this::playLoop, "AudioPlayer");
            playerThread.start();
        }

        private void playLoop() {
            while (!shouldStop.get()) {
                byte[] audio = audioQueue.poll();
                if (audio != null) {
                    line.write(audio, 0, audio.length);
                } else {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ignored) {}
                }
            }
        }

        public void play(String base64Audio) {
            audioQueue.add(Base64.getDecoder().decode(base64Audio));
        }

        public void close() {
            shouldStop.set(true);
            try {
                playerThread.join(1000);
            } catch (InterruptedException ignored) {}
            line.drain();
            line.close();
        }
    }

    /**
     * Tool Registry - Manages available tools and their implementations
     */
    static class ToolRegistry {
        private final Map<String, Function<JsonObject, String>> tools = new ConcurrentHashMap<>();
        private final Map<String, JsonObject> pendingToolCalls = new ConcurrentHashMap<>();

        public ToolRegistry() {
            registerDefaultTools();
        }

        private void registerDefaultTools() {
            registerTool("get_current_weather", this::getCurrentWeather);
            registerTool("get_flight_price", this::getFlightPrice);
            registerTool("get_train_price", this::getTrainPrice);
        }

        public void registerTool(String name, Function<JsonObject, String> handler) {
            tools.put(name, handler);
        }

        /**
         * Build tool definitions (OpenAI format)
         */
        public List<Map<String, Object>> buildToolsDefinition() {
            List<Map<String, Object>> definitions = new ArrayList<>();

            definitions.add(createFunctionDefinition(
                    "get_current_weather",
                    "Useful for when you want to query the weather in a specific city.",
                    createParamsSchema(
                            Collections.singletonMap("location",
                                    createProperty("string", "A city or district, such as Beijing, Hangzhou, or Yuhang.")),
                            Collections.singletonList("location")
                    )
            ));

            Map<String, Object> flightProps = new HashMap<>();
            flightProps.put("src", createProperty("string", "The departure city of the flight, such as Beijing or Hangzhou."));
            flightProps.put("dst", createProperty("string", "The arrival city of the flight, such as Beijing or Hangzhou."));
            definitions.add(createFunctionDefinition(
                    "get_flight_price",
                    "Useful for when you want to query flight ticket prices.",
                    createParamsSchema(flightProps, Arrays.asList("src", "dst"))
            ));

            Map<String, Object> trainProps = new HashMap<>();
            trainProps.put("src", createProperty("string", "The departure city of the train, such as Beijing or Hangzhou."));
            trainProps.put("dst", createProperty("string", "The arrival city of the train, such as Beijing or Hangzhou."));
            definitions.add(createFunctionDefinition(
                    "get_train_price",
                    "Useful for when you want to query train ticket prices.",
                    createParamsSchema(trainProps, Arrays.asList("src", "dst"))
            ));

            return definitions;
        }

        private Map<String, Object> createFunctionDefinition(String name, String description, Map<String, Object> parameters) {
            Map<String, Object> function = new HashMap<>();
            function.put("name", name);
            function.put("description", description);
            function.put("parameters", parameters);

            Map<String, Object> tool = new HashMap<>();
            tool.put("type", "function");
            tool.put("function", function);
            return tool;
        }

        private Map<String, Object> createParamsSchema(Map<String, Object> properties, List<String> required) {
            Map<String, Object> schema = new HashMap<>();
            schema.put("type", "object");
            schema.put("properties", properties);
            schema.put("required", required);
            return schema;
        }

        private Map<String, Object> createProperty(String type, String description) {
            Map<String, Object> prop = new HashMap<>();
            prop.put("type", type);
            prop.put("description", description);
            return prop;
        }

        /**
         * Add a tool call to the pending queue
         */
        public void addPendingToolCall(String callId, JsonObject toolCall) {
            pendingToolCalls.put(callId, toolCall);
        }

        /**
         * Check if there are pending tool calls
         */
        public boolean hasPendingToolCalls() {
            return !pendingToolCalls.isEmpty();
        }

        /**
         * Process all pending tool calls
         */
        public void processPendingToolCalls(OmniRealtimeConversation conversation) {
            if (pendingToolCalls.isEmpty()) {
                return;
            }

            for (Map.Entry<String, JsonObject> entry : pendingToolCalls.entrySet()) {
                String callId = entry.getKey();
                JsonObject toolCall = entry.getValue();

                String result = executeTool(toolCall);
                sendToolResult(conversation, callId, result);
            }

            pendingToolCalls.clear();
        }

        private String executeTool(JsonObject toolCall) {
            String functionName = toolCall.get("name").getAsString();
            JsonObject arguments = new Gson().fromJson(
                    toolCall.get("arguments").getAsString(),
                    JsonObject.class
            );

            System.out.println("[Tool Call] start handling: " + functionName + ", args: " + arguments);

            Function<JsonObject, String> handler = tools.get(functionName);
            if (handler == null) {
                return "Client did not find this tool. Call failed.";
            }

            String result = handler.apply(arguments);
            System.out.println("[Tool Call] response: " + result);
            return result;
        }

        private void sendToolResult(OmniRealtimeConversation conversation, String callId, String output) {
            JsonObject item = new JsonObject();
            item.addProperty("id", "item_" + UUID.randomUUID().toString().replace("-", ""));
            item.addProperty("type", "function_call_output");
            item.addProperty("call_id", callId);
            item.addProperty("output", output);

            conversation.createItem(item);
        }

        // ===== Tool Implementations =====

        private String getCurrentWeather(JsonObject args) {
            String location = args.get("location").getAsString();
            return "The weather in " + location + " today is changing from haze to sunny, with a temperature of 4/-4°C and a light breeze.";
        }

        private String getFlightPrice(JsonObject args) {
            String src = args.get("src").getAsString();
            String dst = args.get("dst").getAsString();
            return "The flight ticket price from " + src + " to " + dst + " is 200-300 USD.";
        }

        private String getTrainPrice(JsonObject args) {
            String src = args.get("src").getAsString();
            String dst = args.get("dst").getAsString();
            return "invalid apikey error";
        }
    }

    /**
     * Conversation Handler - Handles WebSocket events
     */
    static class ConversationHandler extends OmniRealtimeCallback {
        private final AudioPlayer audioPlayer;
        private final ToolRegistry toolRegistry;
        private final AtomicBoolean shouldStop = new AtomicBoolean(false);
        private final AtomicReference<StringBuilder> responseTextRef = new AtomicReference<>(new StringBuilder());

        private long lastPackageTime = 0;
        private boolean isFirstText = true;
        private boolean isFirstAudio = true;

        public ConversationHandler(AudioPlayer audioPlayer, ToolRegistry toolRegistry) {
            this.audioPlayer = audioPlayer;
            this.toolRegistry = toolRegistry;
        }

        public AtomicBoolean getShouldStop() {
            return shouldStop;
        }

        @Override
        public void onOpen() {
            System.out.println("Connection established");
        }

        @Override
        public void onClose(int code, String reason) {
            System.out.println("Connection closed");
            shouldStop.set(true);
        }

        @Override
        public void onEvent(JsonObject message) {
            String type = message.get("type").getAsString();

            switch (type) {
                case "session.created":
                    handleSessionCreated(message);
                    break;
                case "conversation.item.input_audio_transcription.completed":
                    handleTranscriptionCompleted(message);
                    break;
                case "response.audio_transcript.delta":
                case "response.text.delta":
                    handleTextDelta(message);
                    break;
                case "response.audio.delta":
                    handleAudioDelta(message);
                    break;
                case "input_audio_buffer.speech_started":
                    handleSpeechStarted();
                    break;
                case "input_audio_buffer.speech_stopped":
                    handleSpeechStopped();
                    break;
                case "response.function_call_arguments.done":
                    handleFunctionCall(message);
                    break;
                case "response.done":
                    handleResponseDone();
                    break;
                default:
                    break;
            }
        }

        private void handleSessionCreated(JsonObject message) {
            String sessionId = message.get("session").getAsJsonObject().get("id").getAsString();
            System.out.println("start session: " + sessionId);
        }

        private void handleTranscriptionCompleted(JsonObject message) {
            System.out.println("question: " + message.get("transcript").getAsString());
        }

        private void handleTextDelta(JsonObject message) {
            if (isFirstText) {
                isFirstText = false;
                System.out.println("first text latency from vad end: " +
                        (System.currentTimeMillis() - lastPackageTime) + " ms");
            }
            String text = message.get("delta").getAsString();
            responseTextRef.get().append(text);
        }

        private void handleAudioDelta(JsonObject message) {
            if (isFirstAudio) {
                isFirstAudio = false;
                System.out.println("first audio latency from vad end: " +
                        (System.currentTimeMillis() - lastPackageTime) + " ms");
            }
            System.out.println("audio interval: " + (System.currentTimeMillis() - lastPackageTime) + " ms");
            lastPackageTime = System.currentTimeMillis();
            audioPlayer.play(message.get("delta").getAsString());
        }

        private void handleSpeechStarted() {
            System.out.println("======VAD Speech Start======");
        }

        private void handleSpeechStopped() {
            System.out.println("======VAD Speech End======");
            lastPackageTime = System.currentTimeMillis();
            isFirstText = true;
            isFirstAudio = true;
        }

        private void handleFunctionCall(JsonObject message) {
            System.out.println("======TOOL CALL======");
            String callId = message.get("call_id").getAsString();
            toolRegistry.addPendingToolCall(callId, message);
        }

        private void handleResponseDone() {
            System.out.println("======RESPONSE DONE======");
            System.out.println("all response text: " + responseTextRef.get());
            responseTextRef.set(new StringBuilder());
        }

        /**
         * Check if there are pending tool calls
         */
        public boolean hasPendingToolCalls() {
            return toolRegistry.hasPendingToolCalls();
        }

        /**
         * Process all pending tool calls
         */
        public void processPendingToolCalls(OmniRealtimeConversation conversation) {
            toolRegistry.processPendingToolCalls(conversation);
        }
    }
}
import asyncio
import json
import base64
import os
import pyaudio
import websockets

# ==================== Constant Definitions ====================
API_KEY = os.getenv("DASHSCOPE_API_KEY")
# To access the Beijing region, replace with:
# wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime
URL = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime"
MODEL = "qwen3.5-omni-plus-realtime"
VOICE = "Ethan"

# ==================== Tool Definitions ====================
def get_current_weather(location):
    """Query the weather in a specific city"""
    return f"The weather in {location} today is changing from haze to sunny, with a temperature of 4/-4°C and a light breeze."

def get_flight_price(src, dst):
    """Query flight ticket prices"""
    return f"The flight ticket price from {src} to {dst} is 200-300 USD."

def get_train_price(src, dst):
    """Query train ticket prices"""
    return f"The train ticket price from {src} to {dst} is 100-200 CNY."

# Mapping of tool names to functions
TOOL_FUNCTIONS = {
    "get_current_weather": get_current_weather,
    "get_flight_price": get_flight_price,
    "get_train_price": get_train_price,
}

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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.",
                    }
                },
                "required": ["location"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_flight_price",
            "description": "Useful for when you want to query flight ticket prices.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "The departure city of the flight, such as Beijing or Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "The arrival city of the flight, such as Beijing or Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_train_price",
            "description": "Useful for when you want to query train ticket prices.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "The departure city of the train, such as Beijing or Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "The arrival city of the train, such as Beijing or Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
]

# ==================== Tool Call Handling ====================
def handle_tool_call(name, arguments_str):
    """
    Handles a tool call request

    Args:
        name: Tool function name
        arguments_str: JSON formatted input parameter string

    Returns:
        Tool execution result string
    """
    try:
        arguments = json.loads(arguments_str)
        print(f'[Tool Call] Start processing: name={name}, args={arguments}')

        func = TOOL_FUNCTIONS.get(name)
        if func is None:
            result = f"Client did not find the tool: {name}"
            print(f'[Tool Call] Error: {result}')
            return result

        result = func(**arguments)
        print(f'[Tool Call] Completed: {result}')
        return result

    except Exception as e:
        error_msg = f"Tool call failed: {str(e)}"
        print(f'[Tool Call] Exception: {error_msg}')
        return error_msg

# ==================== Main Program ====================
async def main():
    """Main function: Establishes a WebSocket connection and conducts a voice conversation"""
    pya = pyaudio.PyAudio()
    speaker = pya.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)

    # Establish WebSocket connection
    headers = {
        "Authorization": f"bearer {API_KEY}",
        "X-DashScope-OmniRealtime": "true",
    }
    async with websockets.connect(
        f"{URL}?model={MODEL}", additional_headers=headers,
    ) as ws:
        await ws.recv()

        # Configure session parameters
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "modalities": ["text", "audio"],
                "voice": VOICE,
                "input_audio_format": "pcm16",
                "output_audio_format": "pcm16",
                "instructions": "You are a personal assistant named Xiaoyun",
                "turn_detection": {"type": "server_vad"},
                "input_audio_transcription": {"model": "qwen3-asr-flash-realtime"},
                "tools": TOOLS,
            },
        }))
        await ws.recv()

        # Audio capture coroutine
        async def send_audio():
            mic = pya.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True)
            try:
                while True:
                    data = mic.read(3200, exception_on_overflow=False)
                    await ws.send(json.dumps({
                        "type": "input_audio_buffer.append",
                        "audio": base64.b64encode(data).decode(),
                    }))
                    await asyncio.sleep(0.01)
            except asyncio.CancelledError:
                mic.close()

        pending = {}
        all_response_text = ""
        send_task = asyncio.create_task(send_audio())
        print("Tool calling is enabled. Speak into the microphone (Ctrl+C to exit)...")

        # Event handling loop
        async for raw in ws:
            msg = json.loads(raw)
            t = msg["type"]

            # Session created
            if t == "session.created":
                print(f"Session started: {msg['session']['id']}")

            # Play audio
            elif t == "response.audio.delta":
                speaker.write(base64.b64decode(msg["delta"]))

            # Incremental text response
            elif t in ("response.audio_transcript.delta", "response.text.delta"):
                all_response_text += msg.get("delta", "")

            # User speech-to-text
            elif t == "conversation.item.input_audio_transcription.completed":
                print(f"[User] {msg['transcript']}")

            # VAD detected speech start
            elif t == "input_audio_buffer.speech_started":
                print("====== VAD detected speech start ======")

            # VAD detected speech end
            elif t == "input_audio_buffer.speech_stopped":
                print("====== VAD detected speech end ======")

            # Received tool call request
            elif t == "response.function_call_arguments.done":
                print("====== Received tool call request ======")
                pending[msg["call_id"]] = {
                    "name": msg["name"],
                    "arguments": msg["arguments"],
                }

            # Response completed
            elif t == "response.done":
                if pending:
                    # Run pending tool calls
                    for cid, info in pending.items():
                        result = handle_tool_call(info["name"], info["arguments"])
                        # Send tool execution result
                        await ws.send(json.dumps({
                            "type": "conversation.item.create",
                            "item": {
                                "type": "function_call_output",
                                "call_id": cid,
                                "output": result,
                            },
                        }))
                    pending.clear()
                    # Trigger the server to continue generating a response
                    await ws.send(json.dumps({
                        "type": "response.create",
                        "response": {"modalities": ["text", "audio"]},
                    }))
                    print("====== Tool call processing completed ======")
                else:
                    # Normal response completed, print the full response
                    if all_response_text:
                        print(f"[Model] {all_response_text}")
                    all_response_text = ""

        send_task.cancel()
        speaker.close()
        pya.terminate()

asyncio.run(main())

Llamada a herramientas para modelos de pensamiento profundo

Los modelos de pensamiento profundo realizan inferencias antes de generar la información de llamada a la herramienta, lo que mejora la interpretabilidad y la fiabilidad de las decisiones.

  1. Proceso de pensamiento

    El modelo analiza la intención del usuario, identifica las herramientas necesarias, verifica la validez de los parámetros y planifica la estrategia de llamada paso a paso.

  2. Llamada a herramientas

    El modelo genera una o más solicitudes de llamada a función en un formato estructurado.

    Se admite la llamada a herramientas en paralelo.

A continuación se muestra un ejemplo de una llamada a herramienta utilizando un modelo de pensamiento profundo con transmisión.

Para obtener más información sobre los modelos de pensamiento de generación de texto, consulte Pensamiento profundo. Para obtener más información sobre los modelos de pensamiento multimodal, consulte Comprensión de imágenes y videos y No en tiempo real (Qwen-Omni).

En modo de pensamiento (enable_thinking=True), el parámetro tool_choice no admite ser establecido en "required" o en un objeto (por ejemplo, {"type": "function", "function": {...}}). Establecer tool_choice en cualquiera de estos valores mientras el modo de pensamiento está activado provoca que la solicitud falle con el error The tool_choice parameter does not support being set to required or object in thinking mode. Si necesita llamadas a herramientas MCP confiables mientras el modo de pensamiento está activado, utilice la API de Responses para conectarse a MCP en su lugar.

Compatible con OpenAI

Python

Código de ejemplo

import os
from openai import OpenAI

# Initialize the OpenAI client and configure the Alibaba Cloud DashScope service
client = OpenAI(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),  # Read the API key from the environment variable
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.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": "Useful for when you want to know the current time.",
            "parameters": {}  # No parameters required
        }
    },
    # Tool 2: Get the weather in a specific city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "required": ["location"]  # Required parameter
            }
        }
    }
]

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

# Example message for a multimodal 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": "Based on the location in the image, what is the current weather there?"}]
#     }]

completion = client.chat.completions.create(
    # This example uses qwen3.8-max. You can replace it with other deep thinking models.
    model="qwen3.8-max",
    messages=messages,
    extra_body={
        # Enable deep thinking.
        "enable_thinking": True
    },
    tools=tools,
    parallel_tool_calls=True,
    stream=True,
    # Uncomment to get token consumption information
    # stream_options={
    #     "include_usage": True
    # }
)

reasoning_content = ""  # Define the complete thinking process
answer_content = ""     # Define the complete response
tool_info = []          # Store tool call information
is_answering = False   # Determine if the thinking process has ended and the response has begun
print("="*20+"Thinking Process"+"="*20)
for chunk in completion:
    if not chunk.choices:
        # Process usage statistics information
        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 response content
        else:
            if not is_answering:  # Print the title when entering the response phase for the first time
                is_answering = True
                print("\n"+"="*20+"Response Content"+"="*20)
            if delta.content is not None:
                answer_content += delta.content
                print(delta.content,end="",flush=True)  # Stream the response content

            # Process tool call information (supports parallel tool calling)
            if delta.tool_calls is not None:
                for tool_call in delta.tool_calls:
                    index = tool_call.index  # Tool call index, for parallel calling

                    # 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 specific functions)
                    if tool_call.function and tool_call.function.name:
                        tool_info[index]['name'] = tool_info[index].get('name', '') + tool_call.function.name

                    # Collect function parameters (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 call")
else:
    print(tool_info)

Resultado devuelto

Ingrese "Clima en los cuatro municipios" para obtener el siguiente resultado:

====================Thinking Process====================
Okay, the user is asking about the weather in the four municipalities. First, I need to clarify which four municipalities they are. 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 parameter of type string. Each city needs to be queried separately because the function can only query 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. This might require making 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 according to 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 I need to handle possible error situations, like a city not existing or the API being unavailable. But for now, the four municipalities are clear, so it should be fine.
====================Response 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

Código de ejemplo

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

const openai = new OpenAI({
    // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    apiKey: process.env.DASHSCOPE_API_KEY,
    // Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
});

const tools = [
    {
        type: "function",
        function: {
            name: "get_current_time",
            description: "Useful for when you want to know the current time.",
            parameters: {}
        }
    },
    {
        type: "function",
        function: {
            name: "get_current_weather",
            description: "Useful for 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."
                    }
                },
                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 a multimodal 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 like in the location shown 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 qwen3.8-max. You can replace it with other deep thinking models.
            model: "qwen3.8-max",
            messages,
            // Enable deep thinking.
            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 the thinking process
            if (delta.reasoning_content) {
                reasoningContent += delta.reasoning_content;
                process.stdout.write(delta.reasoning_content);
            }
            // Process the response content
            else {
                if (!isAnswering) {
                    isAnswering = true;
                    console.log("\n" + "=".repeat(20) + "Response Content" + "=".repeat(20));
                }
                if (delta.content) {
                    answerContent += delta.content;
                    process.stdout.write(delta.content);
                }
                // Process the tool call
                if (delta.tool_calls) {
                    for (const toolCall of delta.tool_calls) {
                        const index = toolCall.index;

                        // Make sure the array is long enough
                        while (toolInfo.length <= index) {
                            toolInfo.push({});
                        }

                        // Update the tool ID
                        if (toolCall.id) {
                            toolInfo[index].id = (toolInfo[index].id || "") + toolCall.id;
                        }

                        // Update the function name
                        if (toolCall.function?.name) {
                            toolInfo[index].name = (toolInfo[index].name || "") + toolCall.function.name;
                        }

                        // Update the 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 call");

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

main();

Resultado devuelto

Ingrese "Clima en los cuatro municipios" para obtener el siguiente resultado:

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 are the four municipalities of China. Beijing, Shanghai, Tianjin, and Chongqing, right? Next, I need to call the weather query function for each city.

But the user's question might require me to get the weather conditions for these four cities separately. I need to call the get_current_weather function once for each city, with their respective city names as parameters. I need to make sure the parameters are correct, such as the full names of the municipalities, like "Beijing", "Shanghai", "Tianjin", and "Chongqing".

Then, I need to call the weather API for these four cities in order. 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 names 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'll 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.
====================Response 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

Código de ejemplo

curl

# Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
curl -X POST https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
    "model": "qwen3.8-max",
    "messages": [
        {
            "role": "user",
            "content": "What's the weather like in Hangzhou?"
        }
    ],
    "tools": [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Useful for when you want to know the current time.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "required": ["location"]
            }
        }
    }
  ],
  "enable_thinking": true,
  "stream": true
}'

DashScope

Python

Código de ejemplo

import dashscope
from dashscope import MultiModalConversation

# Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
dashscope.base_http_api_url = "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/"

tools = [
    # Tool 1: Get the current time
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Useful for 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 in a specific city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

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

# Example message for a multimodal model
# messages = [
# {
#     "role": "user",
#     "content": [
#     {"image": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"},
#     {"text": "What's the weather like in the location shown in the image?"}]
# }]

completion = MultiModalConversation.call(
    # This example uses qwen3.8-max. You can replace it with other deep thinking models.
    model="qwen3.8-max",
    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 the thinking process
        if 'reasoning_content' in msg and msg.reasoning_content:
            reasoning_content += msg.reasoning_content
            print(msg.reasoning_content, end="", flush=True)

        # Process the response content
        if 'content' in msg and msg.content:
            if not is_answering:
                is_answering = True
                print("\n"+"="*20+"Response Content"+"="*20)
            answer_content += msg.content
            print(msg.content, end="", flush=True)

        # Process the tool call
        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 the tool ID
                if 'id' in tool_call:
                    tool_info[index]['id'] += tool_call.get('id', '')

                # Incrementally update the function information
                if 'function' in tool_call:
                    func = tool_call['function']
                    # Incrementally update the function name
                    if 'name' in func:
                        tool_info[index]['name'] += func.get('name', '')
                    # Incrementally update the 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 call")
else:
    print(tool_info)

Resultado devuelto

Ingrese "Clima en los cuatro municipios" para obtener el siguiente resultado:

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 are the four municipalities of China. 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, the problem is that the user did not specify the city names, just "the four municipalities". I might need to explicitly state 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, which takes 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, sometimes a more specific district is needed, but the user might only want 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 need to make sure the parameters for each call are correct and that none are missed. This will ensure the user's question is fully answered.
===================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

Código de ejemplo

// 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://{WorkspaceId}.ap-southeast-1.maas.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 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 the text generation model
    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 specific area", jsonSchemaWeather);
        FunctionDefinition fdTime = buildFunctionDefinition(
                "get_current_time", "Get the current time", jsonSchemaTime);

        return GenerationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("qwen3.8-max")
                .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 multimodal 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 multimodal model parameters that support tool calling
    private static MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage userMsg) {
        FunctionDefinition fdWeather = buildFunctionDefinition(
                "get_current_weather", "Get the weather for a specific area", 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 {
            MultiModalConversation conv = new MultiModalConversation();
            MultiModalMessage userMsg = MultiModalMessage.builder().role(Role.USER.getValue())
                    .content(Arrays.asList(Collections.singletonMap("text", "Please tell me the weather in Hangzhou"))).build();
            try {
                streamCallWithMultiModalMessage(conv, userMsg);
            } catch (UploadFileException e) {
                throw new RuntimeException(e);
            }
//             Uncomment the following lines when using a text generation model for tool calling
//            Generation gen = new Generation();
//            Message userMessage = Message.builder()
//                    .role(Role.USER.getValue())
//                    .content("Please tell me the weather in Hangzhou")
//                    .build();
//            try {
//                streamCallWithMessage(gen, userMessage);
//            } catch (InputRequiredException e) {
//                throw new RuntimeException(e);
//            }
        } catch (ApiException | NoApiKeyException e) {
            logger.error("An exception occurred: {}", e.getMessage());
        }
        System.exit(0);
    }
}

Resultado devuelto

{"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 asked 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 there are"}}]}}
{"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":"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 other"}}]}}
{"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":"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"}}]}}
{"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":"asked 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"}}]}}
{"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":"in 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 need to 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"}}]}}
{"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":"errors, I will return it."}}]}}
{"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

Código de ejemplo

curl

# ======= Important =======
# If you use a text-only generation model, replace the url with https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation
# API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
# The following URL is for the Singapore region. Replace {WorkspaceId} with your actual workspace ID. URLs vary by region.
# === Delete this comment before running ===

curl -X POST "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-DashScope-SSE: enable" \
-d '{
    "model": "qwen3.8-max",
    "input":{
        "messages":[
            {
                "role": "user",
                "content": [{"text": "Weather in Hangzhou"}]
            }
        ]
    },
    "parameters": {
        "enable_thinking": true,
        "incremental_output": true,
        "result_format": "message",
        "tools": [{
            "type": "function",
            "function": {
                "name": "get_current_time",
                "description": "Useful for when you want to know the current time.",
                "parameters": {}
            }
        },{
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "Useful for 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."
                        }
                    },
                    "required": ["location"]
                }
            }
        }]
    }
}'

Puesta en marcha

Probar la precisión de la llamada a herramientas

  • Establecer un sistema de evaluación:

    Construya un conjunto de datos de prueba que refleje escenarios comerciales del mundo real y defina métricas de evaluación claras, como la precisión en la selección de herramientas, la precisión en la extracción de parámetros y la tasa de éxito de extremo a extremo.

  • Optimización de prompts

    Basándose en los problemas identificados durante las pruebas, como selecciones incorrectas de herramientas o parámetros, usted puede optimizar los prompts del sistema, las descripciones de herramientas y las descripciones de parámetros.

  • Actualización del modelo

    Si el ajuste de prompts no logra mejorar el rendimiento, actualizar a una versión de modelo más potente, como qwen3.6-plus , es el método más directo y efectivo.

Controlar dinámicamente el número de herramientas

Cuando una aplicación integra decenas o incluso cientos de herramientas, proporcionarlas todas al modelo puede causar los siguientes problemas:

  • Degradación del rendimiento: La dificultad del modelo para seleccionar la herramienta correcta de un gran conjunto de herramientas aumenta drásticamente.
  • Costo y latencia: Muchas descripciones de herramientas consumirán una gran cantidad de tokens de entrada, lo que provocará un aumento de costos y respuestas más lentas.

Solución: Agregar una capa de enrutamiento/recuperación de herramientas antes de llamar al modelo. Esta capa filtra la biblioteca de herramientas basándose en la consulta del usuario para proporcionar al modelo un subconjunto pequeño y relevante de herramientas.

Métodos principales para implementar el enrutamiento de herramientas:
  • Recuperación semántica

    Convierta las descripciones de herramientas (description) en vectores utilizando un modelo de embedding y almacénelos en una base de datos vectorial. Cuando un usuario envíe una consulta, usted puede realizar una búsqueda de similitud vectorial sobre el vector de consulta para recuperar las K herramientas más relevantes.

  • Recuperación híbrida

    Este método combina la coincidencia difusa de la recuperación semántica con la coincidencia exacta de palabras clave tradicionales o etiquetas de metadatos. Para hacer esto, agregue campos de tags o keywords a las herramientas. Durante la recuperación, realizar tanto búsqueda vectorial como filtrado de palabras clave puede mejorar significativamente la precisión de recuperación, especialmente para escenarios específicos o de alta frecuencia.

  • Enrutador LLM ligero

    Para una lógica de enrutamiento más compleja, usted puede usar un modelo más pequeño, rápido y económico, como Qwen-Flash, como modelo enrutador. La tarea de este modelo es generar una lista de nombres de herramientas relevantes basándose en la consulta del usuario.

Consejos prácticos
  • Mantenga el conjunto de candidatos conciso: Independientemente del método utilizado, recomendamos proporcionar no más de 20 herramientas al modelo principal. Esto proporciona un equilibrio óptimo entre la carga cognitiva del modelo, el costo, la latencia y la precisión.
  • Estrategia de filtrado por capas: Puede crear una estrategia de enrutamiento tipo embudo. Por ejemplo, primero puede utilizar coincidencias de palabras clave o reglas de bajo costo para filtrar herramientas claramente irrelevantes. Luego, puede realizar una recuperación semántica en las herramientas restantes para mejorar la eficiencia y la calidad.

Principios de seguridad de herramientas

Al otorgar capacidades de ejecución de herramientas a un LLM, la seguridad es la consideración principal. Los principios fundamentales son el privilegio mínimo y la confirmación humana.

  • Principio de menor privilegio: El conjunto de herramientas proporcionado al modelo debe adherirse estrictamente al principio de menor privilegio. De forma predeterminada, las herramientas deben ser de solo lectura, como herramientas para consultar el clima o buscar documentos. Evite proporcionar cualquier permiso de "escritura" que implique cambios de estado u operaciones de recursos.
  • Aísle las herramientas peligrosas: No proporcione herramientas peligrosas directamente al LLM, como herramientas para ejecutar código arbitrario (code interpreter), operar el sistema de archivos (fs.delete), realizar operaciones de eliminación o actualización de bases de datos (db.drop_table) o manejar transacciones financieras (payment.transfer).
  • Participación humana: Se requiere un proceso de revisión y confirmación manual para todas las operaciones de alto privilegio o irreversibles. El modelo puede generar una solicitud de operación, pero el botón final de "ejecutar" debe ser pulsado por un usuario humano. Por ejemplo, el modelo puede preparar un correo electrónico, pero el usuario debe confirmar la operación de envío.

Optimización de la experiencia del usuario

El proceso de llamada a función implica múltiples pasos, y un problema en cualquier paso puede afectar negativamente la experiencia del usuario.

Gestionar fallos en la ejecución de herramientas

Los fallos en la ejecución de herramientas son comunes. Puede adoptar las siguientes estrategias:

  • Reintentos máximos: Establezca un límite de reintentos razonable, como 3, para evitar largas esperas del usuario o desperdicio de recursos del sistema debido a fallos continuos.
  • Proporcione respuestas alternativas: Si se agotan los reintentos o se encuentra un error irresoluble, devuelva un mensaje claro y amigable al usuario, como: "Lo siento, no puedo encontrar la información relevante en este momento. El servicio podría estar ocupado. Por favor, inténtelo de nuevo más tarde".

Gestionar la latencia de procesamiento

Una alta latencia puede reducir la satisfacción del usuario. Puede implementar optimizaciones tanto en el frontend como en el backend.

  • Establecer un tiempo de espera: Configure un tiempo de espera independiente y razonable para cada paso del proceso de llamada a funciones. Si ocurre un tiempo de espera, la operación debe interrumpirse inmediatamente y se debe proporcionar retroalimentación al usuario.
  • Proporcionar retroalimentación instantánea: Cuando comienza una llamada a función, recomendamos mostrar un aviso en la interfaz, como "Consultando el clima para usted..." o "Buscando información relevante...". Esto brinda al usuario retroalimentación en tiempo real sobre el progreso.

Facturación

Además de los tokens en el arreglo messages, las descripciones de herramientas también se facturan como tokens de entrada.

Pasar información de herramientas a través de un mensaje del sistema

Recomendamos pasar información de herramientas al modelo de lenguaje grande (LLM) utilizando el parámetro tools, como se describe en la sección Cómo usar. Para pasar información de herramientas a través de un mensaje del sistema, utilice la plantilla de prompt en el siguiente código para un rendimiento óptimo del modelo:

Compatible con OpenAI

Python

Código de ejemplo

import os
from openai import OpenAI
import json

client = OpenAI(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.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": "Useful for when you want to know the current time.",
            "parameters": {}
        }
    },
    # Tool 2: Get the weather in a specific city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "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="qwen3.8-max",
    extra_body={"enable_thinking": False},
    messages=messages,
)
print(completion.model_dump_json())

Node.js

Código de ejemplo

import OpenAI from "openai";

const client = new OpenAI({
    // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    // If you have not configured the environment variable, replace the following line with your Model Studio API key: apiKey: "sk-xxx",
    apiKey: process.env.DASHSCOPE_API_KEY,
    // Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
    baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.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": "Useful for when you want to know the current time.",
            "parameters": {}
        }
    },
    // Tool 2: Get the weather in a specific city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "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: "qwen3.8-max",
            enable_thinking: false,
            messages: messages,
        });

        console.log(JSON.stringify(completion, null, 2));
    } catch (error) {
        console.error("Error:", error);
    }
}

main();

DashScope

Python

Código de ejemplo

import os
from dashscope import MultiModalConversation
import json
# Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
dashscope.base_http_api_url = 'https://{WorkspaceId}.ap-southeast-1.maas.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": "Useful for when you want to know the current time.",
            "parameters": {}
        }
    },
    # Tool 2: Get the weather in a specific city
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Useful for 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."
                    }
                },
                "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": [{"text": system_prompt}]},
    {"role": "user", "content": [{"text": "What time is it?"}]}
]

response = MultiModalConversation.call(
    # API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    model="qwen3.8-max",
    enable_thinking=False,
    messages=messages,
    result_format="message",  # Set the output to message format
)

print(response)

Java

Código de ejemplo

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.UploadFileException;
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 | UploadFileException 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, UploadFileException {

        // Custom System prompt
        String 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.";

        // Build tool descriptions
        String[] toolsDescriptions = {
                // Tool 1: Get the current time
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_time\",\n" +
                        "        \"description\": \"Useful for when you want to know the current time.\",\n" +
                        "        \"parameters\": {}\n" +
                        "    }\n" +
                        "}",
                // Tool 2: Get the weather in a specific city
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_weather\",\n" +
                        "        \"description\": \"Useful for when you want to query the weather in a specific 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.\"\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
        MultiModalMessage systemMsg = MultiModalMessage.builder()
                .role(Role.SYSTEM.getValue())
                .content(Arrays.asList(Collections.singletonMap("text", systemPrompt)))
                .build();

        MultiModalMessage userMsg = MultiModalMessage.builder()
                .role(Role.USER.getValue())
                .content(Arrays.asList(Collections.singletonMap("text", "What time is it?")))
                .build();

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

        // Build the request parameters
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                .model("qwen3.8-max")
                .enableThinking(false)
                // API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/model-studio/get-api-key
                // If you have not configured the environment variable, replace the following line with: .apiKey("sk-xxx")
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .messages(messages)
                .build();

        // Singapore region. Replace {WorkspaceId} with your actual Workspace ID. URLs vary by region.
        MultiModalConversation conv = new MultiModalConversation(Protocol.HTTP.getValue(), "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1");
        MultiModalConversationResult result = conv.call(param);

        // Print the result
        System.out.println(JsonUtils.toJson(result));
    }
}

Después de ejecutar el código anterior, usted puede usar un analizador XML para extraer la información de la llamada a la herramienta, incluido el nombre de la función y los parámetros de entrada, de entre las etiquetas <tool_call> y </tool_call>.

Códigos de error

Si una llamada al modelo falla y devuelve un mensaje de error, consulte Códigos de error para resolver el problema.