Modelos de Linguagem Grandes (LLMs) não acessam dados em tempo real nem sistemas externos. O Function Calling permite que os modelos chamem ferramentas externas, como APIs, bancos de dados e funções definidas pelo usuário. Isso possibilita ao modelo recuperar informações ou executar ações além de suas capacidades nativas.
Como funciona
O Function Calling opera por meio de uma interação em várias etapas entre sua aplicação e o LLM:
-
Faça a primeira chamada ao modelo
A aplicação envia a pergunta do usuário e uma lista de ferramentas disponíveis para o LLM.
-
Receba instruções de chamada de ferramenta do modelo
Se o modelo decidir chamar uma ferramenta externa, ele retornará uma instrução JSON especificando o nome da função e os parâmetros de entrada.
Caso o modelo decida não chamar uma ferramenta, ele retornará uma resposta em linguagem natural.
-
Execute a ferramenta na aplicação
A aplicação executa a ferramenta especificada e obtém a saída.
-
Faça a segunda chamada ao modelo
Adicione a saída da ferramenta ao array de mensagens e chame o modelo novamente.
-
Receba a resposta final do modelo
O modelo combina a saída da ferramenta com a pergunta do usuário para gerar uma resposta em linguagem natural.
A figura a seguir ilustra o fluxo de trabalho.
Modelos suportados
Qwen
-
Modelos de geração de texto
- Qwen-Max: séries Qwen3.8-Max, Qwen3.7-Max, Qwen3.6-Max, Qwen3-Max e Qwen-Max
- Qwen-Plus: séries Qwen3.7-Plus, Qwen3.6-Plus, Qwen3.5-Plus e Qwen-Plus.
- Qwen-Flash: séries Qwen3.7-Flash, Qwen3.6-Flash, Qwen3.5-Flash e Qwen-Flash
- Qwen-Coder: séries Qwen3-Coder, Qwen2.5-Coder e Qwen-Coder
- Qwen-Turbo: série Qwen-Turbo
- Série open source Qwen3.6
- Série open source Qwen3.5
- Série open source Qwen3
- Série open source Qwen2.5
- Série open source Qwen3.8
-
Modelos multimodais
- Qwen-VL: séries Qwen3-VL-Plus e Qwen3-VL-Flash
- Qwen-Omni: séries Qwen3.5-Omni-Plus, Qwen3.5-Omni-Flash e Qwen3-Omni-Flash
- Qwen-Omni-Realtime: séries Qwen3.5-Omni-Plus-Realtime e Qwen3.5-Omni-Flash-Realtime
- Série open source Qwen3-VL
-
Modelos de chat por voz
- Qwen-Audio-Realtime: séries Qwen-Audio-3.0-Realtime-Plus e Qwen-Audio-3.0-Realtime-Flash
DeepSeek
- deepseek-v4-pro
- deepseek-v4-flash
- deepseek-v3.2
- deepseek-v3.2-exp (modo sem raciocínio)
- deepseek-v3.1 (modo sem raciocínio)
- deepseek-r1
- deepseek-r1-0528
- deepseek-v3
GLM
ImportanteAo usar Function Calling com modelos da série GLM, inclua extra_body={"tool_stream": True} na sua requisição. Caso contrário, o modelo não retornará tool_calls e a chamada de ferramenta não funcionará.
- glm-5.2
- glm-5.1
- glm-5
- glm-4.7
- glm-4.6
Kimi
- kimi-k2.7-code
- kimi-k2.6
- kimi-k2.5
- kimi-k2-thinking
- Moonshot-Kimi-K2-Instruct
MiniMax
MiniMax-M2.5
Primeiros passos
Antes de começar, obtain an API key e configure it as an environment variable. Se você utilizar o OpenAI SDK ou o DashScope SDK, também será necessário install the SDK.
O exemplo a seguir demonstra o fluxo completo de Function Calling para um cenário de consulta meteorológica.
Compatível com 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/en/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/en/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 qwen3.6-max-preview or 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 qwen3.6-max-preview or 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/en/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();
}
}
}
Após executar o código, a seguinte saída será exibida:
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.
Como usar
O Function Calling oferece duas formas de transmitir informações sobre ferramentas:
-
Método 1: Transmitir informações pelo parâmetro tools (recomendado)
Para mais detalhes, consulte How to use. Siga as etapas para definir ferramentas, criar um array messages, executar um Function Calling, rodar a função da ferramenta e permitir que o LLM resuma a saída dessa função.
-
Método 2: Transmitir informações por meio de uma System Message
Transmitir informações pelo parâmetro
toolsgera os melhores resultados, pois o servidor se adapta automaticamente ao modelo de prompt ideal. Caso utilize um modelo Qwen e prefira não usar o parâmetrotools, consulte Pass tool information through a System Message.
As seções a seguir utilizam a API compatível com OpenAI como exemplo para detalhar o uso do Function Calling com o parâmetro tools.
Considere um cenário de negócios que recebe dois tipos de perguntas: consultas sobre o clima e consultas sobre a hora.
1. Definir ferramentas
As ferramentas conectam os LLMs a serviços externos. Portanto, é necessário defini-las primeiro.
1.1. Criar funções de ferramenta
Crie duas funções de ferramenta: uma para consulta de clima e outra para consulta de hora.
-
Ferramenta de consulta de clima
Esta ferramenta recebe o parâmetro
arguments. O formato deargumentsé{"location": "queried location"}. A saída da ferramenta é uma string no formato:"{location} today is {weather}".Para fins de demonstração, a ferramenta de consulta de clima definida aqui não realiza uma consulta real. Ela seleciona aleatoriamente entre ensolarado, nublado ou chuvoso. Em um cenário real de negócios, substitua este código por uma ferramenta como Amap Weather .
-
Ferramenta de consulta de hora
A ferramenta de consulta de hora não exige parâmetros de entrada. Sua saída é uma string no formato:
"Current time: {queried time}.".Se estiver usando Node.js, execute
npm install date-fnspara instalar o pacote date-fns e obter a hora atual.
## 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")
Após executar a ferramenta, a seguinte saída será exibida:
Testing tool output:
The weather in Shanghai today is Cloudy.
Current time: 2025-01-08 20:21:45.
1.2. Criar o array tools
Antes de escolher uma ferramenta, é preciso compreender sua função, cenários de uso e parâmetros de entrada. O mesmo vale para os LLMs. O modelo seleciona a ferramenta adequada com base nessas informações. Forneça os dados da ferramenta no seguinte formato JSON.
| Para a ferramenta de consulta de clima, o formato das informações de descrição é o seguinte: |
Antes de realizar um Function Calling, defina um array de informações de ferramentas (tools) no seu código. Esse array inclui o nome da função, a descrição e a definição de parâmetros para cada ferramenta. O array será transmitido como parâmetro nas requisições subsequentes.
# 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. Criar o array messages
O Function Calling transmite instruções e contexto ao LLM por meio do array messages. Antes de fazer uma chamada, o array messages deve conter uma System Message e uma User Message.
System Message
Embora a função e os cenários de uso das ferramentas já tenham sido descritos quando você created the tools array, reforçar no System Message o momento exato de chamar cada ferramenta geralmente melhora a precisão da invocação. Para o cenário atual, defina o System Prompt como:
You are a helpful assistant. If the user asks about the weather, call the 'get_current_weather' function;
if the user asks about the time, call the 'get_current_time' function.
Please answer the questions in a friendly tone.
User Message
A User Message serve para transmitir a pergunta do usuário. Supondo que o usuário pergunte "Weather in Shanghai", o array messages neste momento será:
# 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");
Como as ferramentas disponíveis incluem consultas de clima e de hora, também é possível perguntar a hora atual.
3. Fazer um Function Calling
Transmita os arrays toolsemessages criados ao LLM para realizar um Function Calling. O LLM determina se deve chamar uma ferramenta. Em caso afirmativo, ele retorna o nome da função da ferramenta e seus parâmetros.
Para verificar os modelos suportados, consulte Supported models .
# 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/en/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/en/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/en/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/en/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();
Como o usuário perguntou sobre o clima em Shanghai, o LLM especifica o nome da função da ferramenta a ser usada como "get_current_weather" e o parâmetro de entrada da função 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
}
]
}
Observe que, se o LLM determinar que nenhuma ferramenta é necessária para a pergunta, ele responderá diretamente por meio do parâmetro content. Ao enviar "Hello", o parâmetro tool_calls fica vazio e o formato do objeto retornado é:
{
"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
}
Se o parâmetro
tool_callsestiver vazio, seu programa pode retornar diretamente ocontentsem executar as etapas seguintes.
Para garantir que o LLM selecione uma ferramenta específica sempre que você fizer um Function Calling, consulte Forced tool calling .
4. Executar a função da ferramenta
Executar a função da ferramenta transforma a decisão do modelo em uma operação real.
A execução da função da ferramenta ocorre no seu ambiente de computação, e não no LLM.
O LLM apenas gera uma string. Antes de rodar a função da ferramenta, analise separadamente o nome da função e seus parâmetros de entrada.
-
Função da ferramenta
Crie um mapeamento
function_mapperdo nome da função da ferramenta para a entidade da função da ferramenta, a fim de vincular a string retornada à entidade correspondente. -
Parâmetros de entrada
Os parâmetros de entrada retornados pelo Function Calling são uma string JSON. Utilize uma ferramenta para convertê-la em um objeto JSON e extrair as informações dos parâmetros.
Após a análise, passe os parâmetros para a função da ferramenta e execute-a para obter o resultado de saída.
# 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`);
Ao executar o código, a seguinte saída será exibida:
The weather in Shanghai today is Cloudy.
ObservaçãoEm cenários reais de negócios, muitas ferramentas executam ações específicas (como enviar e-mails ou fazer upload de arquivos) em vez de consultar dados, e não geram uma string de saída. Recomendamos adicionar mensagens de status (como "E-mail enviado com sucesso" ou "Falha na operação") para essas ferramentas, ajudando o LLM a compreender o estado da execução.
5. Permitir que o LLM resuma a saída da função da ferramenta
O formato de saída da função da ferramenta tende a ser rígido. Retorná-lo diretamente ao usuário pode soar robótico. Envie a saída da ferramenta para o contexto do modelo e chame-o novamente para gerar uma resposta em linguagem natural.
-
Adicionar uma Assistant Message
Depois que você make a Function Calling, obtém uma Assistant Message através de
completion.choices[0].message. Primeiro, adicione-a ao arraymessages. -
Adicionar uma Tool Message
Inclua a saída da ferramenta no array
messagesno formato{"role": "tool", "content": "tool output", "tool_call_id": completion.choices[0].message.tool_calls[0].id}.Observação
- Certifique-se de que a saída da ferramenta esteja em formato de string.
- O
tool_call_idé um identificador único gerado pelo sistema para cada solicitação de chamada de ferramenta. O modelo pode solicitar a chamada de várias ferramentas simultaneamente. Ao devolver múltiplos resultados ao modelo, otool_call_idgarante que a saída de cada ferramenta corresponda corretamente à sua intenção de chamada.
# 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");
Neste ponto, o array messages fica assim:
[
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)
]
Após atualizar o array messages, execute o código abaixo.
# 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();
Recupere o conteúdo da resposta em content: "The weather in Shanghai today is cloudy. If you have any other questions, feel free to ask."
{
"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
}
Você concluiu um fluxo completo de Function Calling.
Uso avançado
Especificar o método de chamada de ferramenta
Chamada paralela de ferramentas
Uma consulta de clima para uma única cidade exige apenas uma chamada de ferramenta. Porém, se uma pergunta demandar múltiplas chamadas — como "Como está o tempo em Beijing e Shanghai?" ou "Qual o clima em Hangzhou e que horas são agora?" —, após você make a Function Calling, apenas uma informação de chamada de ferramenta será retornada. Por exemplo, ao perguntar "How's the weather like in Beijing and Shanghai?":
{
"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
}
]
}
O resultado retornado contém apenas os parâmetros de entrada para Beijing. Para garantir que o resultado inclua todas as funções de ferramenta e parâmetros de entrada, defina o parâmetro de requisição parallel_tool_calls como true quando você make a Function Calling.
ObservaçãoA chamada paralela de ferramentas é adequada para tarefas sem dependências entre si. Se houver dependências (por exemplo, a entrada da ferramenta A depende da saída da ferramenta B), consulte Getting started para implementar chamadas seriais de ferramentas (uma por vez) usando um loop while.
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,
# New parameter
parallel_tool_calls=True
)
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: true
});
console.log("Returned object:");
console.log(JSON.stringify(completion.choices[0].message));
console.log("\n");
return completion;
}
const completion = await functionCalling();
O array tool_calls no objeto retornado passa a conter as informações de parâmetros de entrada tanto para Beijing quanto para Shanghai:
{
"content": "",
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"Beijing\"}"
},
"index": 0,
"id": "call_c2d8a3a24c4d4929b26ae2",
"type": "function"
},
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"Shanghai\"}"
},
"index": 1,
"id": "call_dc7f2f678f1944da9194cd",
"type": "function"
}
]
}
Chamada forçada de ferramenta
Os LLMs geram conteúdo com certo grau de incerteza e podem escolher a ferramenta errada. Para forçar o uso ou a desativação de uma ferramenta específica para determinado tipo de pergunta, modifique o parâmetro tool_choice. O valor padrão de tool_choice é "auto", o que significa que o LLM decide autonomamente como realizar a chamada de ferramenta.
Quando o LLM resumir a saída da função da ferramenta, remova o parâmetro
tool_choice. Caso contrário, a API continuará retornando informações de chamada de ferramenta.
-
Forçar o uso de uma ferramenta específica
Se desejar que o Function Calling invoque obrigatoriamente uma ferramenta específica para certo tipo de pergunta, defina o parâmetro
tool_choicecomo{"type": "function", "function": {"name": "the_function_to_call"}}. Dessa forma, o LLM não participará da seleção da ferramenta e apenas emitirá as informações de parâmetros de entrada.Supondo que o cenário atual envolva apenas perguntas sobre clima, altere o código de
function_callingpara:
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();
Independentemente da pergunta enviada, a função da ferramenta no objeto retornado será sempre get_current_weather.
Antes de adotar essa estratégia, certifique-se de que a pergunta tenha relação com a ferramenta selecionada. Do contrário, resultados inesperados podem ocorrer.
Em algumas perguntas que exigem uma ferramenta, o LLM pode decidir que nenhuma chamada é necessária. Para obrigar o Function Calling a sempre realizar uma chamada de ferramenta (garantindo que o parâmetro tool_calls no objeto retornado não esteja vazio), defina o parâmetro tool_choice como "required". Assim, o Function Calling sempre retornará informações de ferramenta e parâmetros de entrada.
Considerando que todas as perguntas do cenário atual requeiram uma chamada de ferramenta, modifique o código de function_calling para:
def function_calling():
completion = client.chat.completions.create(
model="qwen3.8-max",
extra_body={"enable_thinking": False},
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: "qwen3.8-max",
enable_thinking: false,
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();
Não importa qual pergunta seja enviada, o parâmetro tool_calls no objeto retornado nunca estará vazio.
Antes de adotar essa estratégia, certifique-se de que a pergunta tenha relação com as ferramentas disponíveis. Do contrário, resultados inesperados podem ocorrer.
-
Forçar a não utilização de ferramentas
Caso precise que o Function Calling jamais realize uma chamada de ferramenta (fazendo com que o objeto retornado contenha apenas conteúdo de resposta em
contente o parâmetrotool_callsvazio), defina o parâmetrotool_choicecomo"none"ou simplesmente não envie o parâmetrotools. O parâmetrotool_callsretornado pelo Function Calling estará sempre vazio.Supondo que nenhuma pergunta no cenário atual exija uma chamada de ferramenta, altere o código de
function_callingpara:
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();
Conversa de múltiplas rodadas
Um usuário pode perguntar "Weather in Beijing" na primeira rodada e, em seguida, "What about Shanghai?" na segunda. Se o contexto do modelo não contiver as informações da primeira rodada, ele não conseguirá determinar qual ferramenta chamar. Em cenários de conversa de múltiplas rodadas, mantenha o array messages completo após cada interação. Adicione a nova User Message a esse array e siga com make a Function Calling e as etapas subsequentes. A estrutura de messages ficará assim:
[
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
]
Saída em streaming
O uso de saída em streaming permite obter o nome da função da ferramenta e as informações dos parâmetros de entrada em tempo real, melhorando a experiência do usuário. Nesse cenário:
- As informações de parâmetros da chamada de ferramenta são retornadas em fragmentos como um fluxo de dados.
- O nome da função da ferramenta é retornado no primeiro fragmento de dados da resposta do fluxo.
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/en/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);
}
Após executar o código, a seguinte saída será exibida:
[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
Execute o código abaixo para montar as informações dos parâmetros 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]));
A seguinte saída será exibida:
{"index":0,"id":"call_16c72bef988a4c6c8cc662","function":{"arguments":"{\"location\": \"Hangzhou\"}","name":"get_current_weather"},"type":"function"}
Na etapa em que o LLM resume a saída da função da ferramenta, a Mensagem do Assistente adicionada deve seguir o formato abaixo. Basta substituir os elementos em tool_calls pelo conteúdo obtido anteriormente.
{
"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,
}
],
}
Chamada de ferramentas com a Responses API
Os exemplos anteriores baseiam-se nas APIs OpenAI Chat Completions e DashScope. Caso você utilize a OpenAI Responses API, o processo geral permanece o mesmo, mas o formato da API apresenta as seguintes diferenças:
| Dimensão | Chat Completions | Responses API |
|---|---|---|
Formato de definição da ferramenta | | |
Saída da chamada de ferramenta | response.choices[0].message.tool_calls | Itens em |
Retorno do resultado da ferramenta | | |
Resposta 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/en/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/en/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);
Chamada de ferramentas para modelos omni-modal
Modelos omni-modal suportam chamada de ferramentas. Os métodos de chamada para as séries Qwen-Omni e Qwen-Omni-Realtime são diferentes.
Série Qwen-Omni
As séries Qwen3.5-Omni-Plus, Qwen3.5-Omni-Flash e Qwen3-Omni-Flash suportam chamada de ferramentas por meio da API compatível com OpenAI. A etapa de obtenção das informações da ferramenta difere de outros modelos nas seguintes formas:
- Saída em streaming é obrigatória: O Qwen-Omni suporta apenas saída em streaming. Ao obter informações da ferramenta, você também deve definir
stream=True. - Recomenda-se saída apenas em texto: O modelo precisa apenas de informações textuais ao obter dados da ferramenta (nome da função e parâmetros). Para evitar a geração de áudio desnecessário, recomendamos definir ``modalities=["text"]`. Quando a saída inclui modalidades de texto e áudio, é necessário ignorar os fragmentos de dados de áudio durante a obtenção das informações da ferramenta.
Para mais informações sobre o Qwen-Omni, consulte Non-real-time (Qwen-Omni) .
from openai import OpenAI
import os
client = OpenAI(
# API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
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"],
},
},
},
]
completion = client.chat.completions.create(
model="qwen3.5-omni-plus",
messages=[{"role": "user", "content": "Weather in Hangzhou?"}],
# Set the modality of the output data. Valid values: ["text"], ["text","audio"]. We recommend setting it to ["text"].
modalities=["text"],
# stream must be set to True, otherwise an error will occur.
stream=True,
tools=tools
)
for chunk in completion:
# If the output includes the audio modality, change the following condition to: if chunk.choices and not hasattr(chunk.choices[0].delta, "audio"):
if chunk.choices:
delta = chunk.choices[0].delta
print(delta.tool_calls)
import { OpenAI } from "openai";
const openai = new OpenAI(
{
// API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/en/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"
}
);
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-omni-flash",
messages: [
{
"role": "user",
"content": "Weather in Hangzhou"
}],
stream: true,
// Set the modality of the output data. Valid values: ["text"], ["text","audio"]. We recommend setting it to ["text"].
modalities: ["text"],
tools:tools
});
for await (const chunk of stream) {
// If the output includes audio, replace the conditional statement with: if (chunk.choices?.length && chunk.choices[0].delta && !('audio' in chunk.choices[0].delta))
if (chunk.choices?.length){
const delta = chunk.choices[0].delta;
console.log(delta.tool_calls);
}}
Após executar o código, a seguinte saída será exibida:
[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=None, name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='call_391c8e5787bc4972a388aa', function=ChoiceDeltaToolCallFunction(arguments=' {"location": "Hangzhou"}', name=None), type='function')]
None
Para obter o código que monta as informações dos parâmetros de entrada (arguments), consulte Streaming output.
Série Qwen-Omni-Realtime
As séries Qwen3.5-Omni-Plus-Realtime e Qwen3.5-Omni-Flash-Realtime oferecem suporte a chamadas de ferramentas e são ideais para cenários de conversação por voz. Você pode invocá-las por meio do DashScope SDK ou do protocolo WebSocket nativo.
Fluxo de trabalho:
Após estabelecer uma conexão WebSocket, transmita a definição da ferramenta via session.update para iniciar o seguinte fluxo de interação:
- O usuário faz uma pergunta por voz. O cliente captura o áudio e o envia ao servidor (o que corresponde ao método
append_audio()). Quando o VAD do servidor detecta o fim da fala, ele executa a inferência do modelo e determina que uma ferramenta precisa ser chamada. - O servidor retorna as informações da chamada de ferramenta ao cliente (correspondentes ao evento
response.function_call_arguments.done), incluindo o nome da função (name), os parâmetros de entrada (arguments) e o identificador da chamada (call_id). Veja um exemplo abaixo:
{
"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\"}"
}
- Com base no nome da função e nos parâmetros de entrada recebidos, execute a ferramenta correspondente localmente no cliente para obter o resultado da execução.
- Envie o resultado da execução da ferramenta de volta ao servidor (por meio do evento
conversation.item.create), informando o identificador da chamada (call_id) e o resultado obtido (output). Confira o exemplo a seguir:
{
"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."
}
}
- Em seguida, envie um evento
response.createpara que o servidor gere a resposta final em voz com base no resultado da ferramenta. - Ao receber o áudio e o texto retornados pelo servidor (eventos
response.audio.deltaeresponse.audio_transcript.delta), reproduza a resposta de voz para o usuário.
A série Qwen-Omni-Realtime não oferece suporte aos parâmetros
tool_choiceeparallel_tool_calls.
Para obter mais informações sobre o Qwen-Omni-Realtime, consulte Real-time (Qwen-Omni-Realtime) , Client events e Server-side events .
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())
Chamada de ferramentas para modelos de raciocínio profundo
Modelos de raciocínio profundo executam inferência antes de gerar informações de chamada de ferramenta, o que aumenta a interpretabilidade e a confiabilidade das decisões.
-
Processo de raciocínio
O modelo analisa a intenção do usuário, identifica as ferramentas necessárias, verifica a validade dos parâmetros e planeja a estratégia de chamada passo a passo.
-
Chamada de ferramenta
O modelo gera uma ou mais solicitações de chamada de função em formato estruturado.
Há suporte para chamadas paralelas de ferramentas.
O exemplo a seguir demonstra uma chamada de ferramenta usando um modelo de raciocínio profundo com streaming.
Para obter mais informações sobre modelos de raciocínio para geração de texto, consulte Deep thinking . Para obter mais informações sobre modelos de raciocínio multimodais, consulte Image and video understanding e Non-real-time (Qwen-Omni) .
O parâmetro
tool_choiceaceita apenas os valores"auto"(valor padrão, no qual o modelo seleciona a ferramenta autonomamente) ou"none"(força o modelo a não selecionar nenhuma ferramenta).
No modo de raciocínio (enable_thinking=True), o parâmetro tool_choice não pode ser definido como "required" nem como um objeto (por exemplo, {"type": "function", "function": {...}}). Definir tool_choice com qualquer um desses valores enquanto o modo de raciocínio está ativado faz com que a solicitação falhe e retorne o erro The tool_choice parameter does not support being set to required or object in thinking mode. Não use tool_choice="required" como forma de garantir que tool_calls seja não vazio no modo de raciocínio. Se você precisar de chamadas de ferramentas MCP confiáveis com o modo de raciocínio ativado, utilize a Responses API para se conectar ao MCP.
OpenAI compatible
Python
Código de exemplo
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/en/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. This parameter is invalid for qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, and QwQ models.
"enable_thinking": True
},
tools=tools,
parallel_tool_calls=True,
stream=True,
# Uncomment to get token consumption information
# stream_options={
# "include_usage": True
# }
)
reasoning_content = "" # 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 retornado
Insira "Weather in the four municipalities" para obter o seguinte 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 exemplo
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/en/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. This parameter is invalid for qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, and QwQ models.
enable_thinking: true,
tools,
stream: true,
parallel_tool_calls: true
});
for await (const chunk of stream) {
if (!chunk.choices?.length) {
console.log("\n" + "=".repeat(20) + "Usage" + "=".repeat(20));
console.log(chunk.usage);
continue;
}
const delta = chunk.choices[0]?.delta;
if (!delta) continue;
// Process 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 retornado
Insira "Weather in the four municipalities" para obter o seguinte 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 exemplo
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 exemplo
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 retornado
Insira "Weather in the four municipalities" para obter o seguinte 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 exemplo
// 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 retornado
{"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 exemplo
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/en/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"]
}
}
}]
}
}'
Entrada em produção
Testar a precisão da chamada de ferramentas
-
Estabelecer um sistema de avaliação:
Construa um conjunto de dados de teste que reflita cenários reais de negócios e defina métricas de avaliação claras, como precisão na seleção de ferramentas, precisão na extração de parâmetros e taxa de sucesso de ponta a ponta.
-
Otimizar prompts
Com base nos problemas identificados durante os testes, como seleções incorretas de ferramentas ou parâmetros errados, otimize os prompts do sistema, as descrições das ferramentas e as descrições dos parâmetros.
-
Atualizar o modelo
Se o ajuste de prompts não melhorar o desempenho, atualizar para uma versão mais poderosa do modelo, como
qwen3.6-plus, é o método mais direto e eficaz.
Controlar dinamicamente o número de ferramentas
Quando uma aplicação integra dezenas ou até centenas de ferramentas, fornecer todas elas ao modelo pode causar os seguintes problemas:
- Degradação de desempenho: A dificuldade do modelo em selecionar a ferramenta correta dentro de um grande conjunto aumenta drasticamente.
- Custo e latência: Muitas descrições de ferramentas consomem uma grande quantidade de tokens de entrada, o que eleva os custos e torna as respostas mais lentas.
Solução: Adicione uma camada de roteamento/recuperação de ferramentas antes de chamar o modelo. Essa camada filtra a biblioteca de ferramentas com base na consulta do usuário para fornecer um subconjunto pequeno e relevante ao modelo.
Principais métodos para implementar o roteamento de ferramentas:-
Recuperação semântica
Converta as descrições das ferramentas (
description) em vetores usando um modelo de embedding e armazene-os em um banco de dados vetorial. Quando um usuário enviar uma consulta, execute uma busca por similaridade vetorial no vetor da consulta para recuperar as K ferramentas mais relevantes. -
Recuperação híbrida
Este método combina a correspondência aproximada da recuperação semântica com a correspondência exata de palavras-chave tradicionais ou tags de metadados. Para isso, adicione campos
tagsoukeywordsàs ferramentas. Durante a recuperação, executar tanto a busca vetorial quanto a filtragem por palavras-chave melhora significativamente a precisão, especialmente em cenários específicos ou de alta frequência. -
Roteador LLM leve
Para lógicas de roteamento mais complexas, utilize um modelo menor, mais rápido e menos custoso, como o Qwen-Flash, como modelo roteador. A tarefa desse modelo é gerar uma lista de nomes de ferramentas relevantes com base na consulta do usuário.
- Mantenha o conjunto de candidatos conciso: Independentemente do método utilizado, recomendamos fornecer no máximo 20 ferramentas ao modelo principal. Isso garante um equilíbrio ideal entre carga cognitiva do modelo, custo, latência e precisão.
- Estratégia de filtragem em camadas: Construa uma estratégia de roteamento em funil. Por exemplo, use primeiro correspondências de baixo custo por palavras-chave ou regras para filtrar ferramentas claramente irrelevantes. Em seguida, aplique recuperação semântica nas ferramentas restantes para aumentar a eficiência e a qualidade.
Princípios de segurança de ferramentas
Ao conceder capacidades de execução de ferramentas a um LLM, a segurança é a prioridade máxima. Os princípios fundamentais são o privilégio mínimo e a confirmação humana.
- Princípio do privilégio mínimo: O conjunto de ferramentas fornecido ao modelo deve seguir rigorosamente o princípio do privilégio mínimo. Por padrão, as ferramentas devem ser somente leitura, como ferramentas para consultar o clima ou pesquisar documentos. Evite fornecer quaisquer permissões de "escrita" que envolvam alterações de estado ou operações em recursos.
- Isolar ferramentas perigosas: Não forneça ferramentas perigosas diretamente ao LLM, como ferramentas para executar código arbitrário (
code interpreter), operar o sistema de arquivos (fs.delete), realizar operações de exclusão ou atualização em bancos de dados (db.drop_table) ou processar transações financeiras (payment.transfer). - Envolvimento humano: Um processo de revisão e confirmação manual é obrigatório para todas as operações irreversíveis ou de alto privilégio. O modelo pode gerar uma solicitação de operação, mas o botão final de "executar" deve ser clicado por um usuário humano. Por exemplo, o modelo pode preparar um e-mail, mas o usuário precisa confirmar o envio.
Otimização da experiência do usuário
O processo de chamada de função envolve múltiplas etapas, e um problema em qualquer uma delas pode afetar negativamente a experiência do usuário.
Tratar falhas na execução de ferramentas
Falhas na execução de ferramentas são comuns. Adote as seguintes estratégias:
- Limite de tentativas: Defina um limite razoável de novas tentativas, como 3, para evitar longas esperas do usuário ou desperdício de recursos do sistema devido a falhas contínuas.
- Fornecer respostas alternativas: Se as tentativas se esgotarem ou ocorrer um erro irresolúvel, retorne um aviso claro e amigável ao usuário, como: "Desculpe, não consigo encontrar as informações relevantes no momento. O serviço pode estar ocupado. Tente novamente mais tarde."
Lidar com a latência de processamento
Alta latência reduz a satisfação do usuário. Implemente otimizações tanto no frontend quanto no backend.
- Definir timeout: Configure um timeout independente e razoável para cada etapa do processo de chamada de função. Se ocorrer um timeout, interrompa imediatamente a operação e forneça feedback ao usuário.
- Oferecer feedback instantâneo: Quando uma chamada de função iniciar, exiba um aviso na interface, como "Consultando o clima para você..." ou "Buscando informações relevantes...". Isso dá ao usuário feedback em tempo real sobre o progresso.
Faturamento
Além dos tokens no array messages, as descrições de ferramentas também são cobradas como tokens de entrada.
Passar informações de ferramentas via System Message
Recomendamos passar as informações das ferramentas para o modelo de linguagem grande (LLM) usando o parâmetro tools, conforme descrito na seção How to use. Para passar as informações das ferramentas através de uma System Message, utilize o modelo de prompt no código abaixo para obter o melhor desempenho do modelo:
OpenAI compatible
Python
Código de exemplo
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/en/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 exemplo
import OpenAI from "openai";
const client = new OpenAI({
// API keys vary by region. To get an API key, see https://www.alibabacloud.com/help/en/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 exemplo
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/en/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 exemplo
// 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/en/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));
}
}
Após executar o código anterior, use um analisador XML para extrair as informações da chamada de ferramenta, incluindo o nome da função e os parâmetros de entrada, entre as tags
<tool_call>e</tool_call>.
Códigos de erro
Se uma chamada de modelo falhar e retornar uma mensagem de erro, consulte Error codes para resolver o problema.