大模型在面對即時性問題、數學計算等問題時可能效果不佳。Function Calling 通過引入外部工具,讓大模型可以回答原本無法解決的問題。
工作原理
Function Calling 通過在應用程式和大模型之間的多步驟互動,使大模型可以參考外部工具資訊進行回答。
發起第一次模型調用
應用程式首先向大模型發起一個包含使用者問題與模型可調用工具清單的請求。
接收模型的工具調用指令(工具名稱與入參)
若模型判斷需要調用外部工具,會返回一個JSON格式的指令,用於告知應用程式需要執行的函數與入參。
若模型判斷無需調用工具,會返回自然語言格式的回複。
在應用端運行工具
應用程式接收到工具指令後,需要運行工具,獲得工具輸出結果。
發起第二次模型調用
擷取到工具輸出結果後,需添加至模型的上下文(messages),再次發起模型調用。
接收來自模型的最終響應
模型將工具輸出結果和使用者問題資訊整合,產生自然語言格式的回複。
工作流程示意圖如下所示:
支援的模型
通義千問
DeepSeek
deepseek-v3.2
deepseek-v3.2-exp(非思考模式)
deepseek-v3.1(非思考模式)
deepseek-r1
deepseek-r1-0528
deepseek-v3
Kimi
kimi-k2-thinking
Moonshot-Kimi-K2-Instruct
快速開始
您需要已擷取與配置 API Key並配置API Key到環境變數(準備下線,併入配置 API Key)。如果通過 OpenAI SDK 或 DashScope SDK 進行調用,需要安裝SDK。
以天氣查詢情境為例,介紹快速使用 Function Calling 的方法。
OpenAI 相容
from openai import OpenAI
from datetime import datetime
import json
import os
import random
client = OpenAI(
# 如果使用華北2(北京)地區的模型,需要使用華北2(北京)地區的 API Key,擷取連結:https://bailian.console.alibabacloud.com/?tab=model#/api-key
# 若沒有配置環境變數,請用百鍊API Key將下行替換為:api_key="sk-xxx",
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 如果使用華北2(北京)地區的模型,需要將base_url替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
# 類比使用者問題
USER_QUESTION = "新加坡天氣咋樣"
# 定義工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如新加坡、紐約等。",
}
},
"required": ["location"],
},
},
},
]
# 類比天氣查詢工具
def get_current_weather(arguments):
weather_conditions = ["晴天", "多雲", "雨天"]
random_weather = random.choice(weather_conditions)
location = arguments["location"]
return f"{location}今天是{random_weather}。"
# 封裝模型響應函數
def get_response(messages):
completion = client.chat.completions.create(
model="qwen-plus",
messages=messages,
tools=tools,
)
return completion
messages = [{"role": "user", "content": USER_QUESTION}]
response = get_response(messages)
assistant_output = response.choices[0].message
if assistant_output.content is None:
assistant_output.content = ""
messages.append(assistant_output)
# 如果不需要調用工具,直接輸出內容
if assistant_output.tool_calls is None:
print(f"無需調用天氣查詢工具,直接回複:{assistant_output.content}")
else:
# 進入工具調用迴圈
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"正在調用工具 [{func_name}],參數:{arguments}")
# 執行工具
tool_result = get_current_weather(arguments)
# 構造工具返回資訊
tool_message = {
"role": "tool",
"tool_call_id": tool_call_id,
"content": tool_result, # 保持原始工具輸出
}
print(f"工具返回:{tool_message['content']}")
messages.append(tool_message)
# 再次調用模型,擷取總結後的自然語言回複
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_output.content}")import OpenAI from 'openai';
// 初始化用戶端
const openai = new OpenAI({
apiKey: process.env.DASHSCOPE_API_KEY,
// 如果使用華北2(北京)地區的模型,需要將baseURL替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
});
// 定義工具列表
const tools = [
{
type: "function",
function: {
name: "get_current_weather",
description: "當你想查詢指定城市的天氣時非常有用。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "城市或縣區,比如新加坡、紐約等。",
},
},
required: ["location"],
},
},
},
];
// 類比天氣查詢工具
const getCurrentWeather = (args) => {
const weatherConditions = ["晴天", "多雲", "雨天"];
const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
const location = args.location;
return `${location}今天是${randomWeather}。`;
};
// 封裝模型響應函數
const getResponse = async (messages) => {
const response = await openai.chat.completions.create({
model: "qwen-plus",
messages: messages,
tools: tools,
});
return response;
};
const main = async () => {
const input = "新加坡天氣咋樣";
let messages = [
{
role: "user",
content: input,
}
];
let response = await getResponse(messages);
let assistantOutput = response.choices[0].message;
// 確保 content 不是 null
if (!assistantOutput.content) assistantOutput.content = "";
messages.push(assistantOutput);
// 判斷是否需要調用工具
if (!assistantOutput.tool_calls) {
console.log(`無需調用天氣查詢工具,直接回複:${assistantOutput.content}`);
} else {
// 進入工具調用迴圈
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(`正在調用工具 [${funcName}],參數:`, funcArgs);
// 執行工具
const toolResult = getCurrentWeather(funcArgs);
// 構造工具返回資訊
const toolMessage = {
role: "tool",
tool_call_id: toolCallId,
content: toolResult,
};
console.log(`工具返回:${toolMessage.content}`);
messages.push(toolMessage);
// 再次調用模型擷取自然語言總結
response = await getResponse(messages);
assistantOutput = response.choices[0].message;
if (!assistantOutput.content) assistantOutput.content = "";
messages.push(assistantOutput);
}
console.log(`助手最終回複:${assistantOutput.content}`);
}
};
// 啟動程式
main().catch(console.error);DashScope
import os
from dashscope import Generation
import dashscope
import json
import random
# 如果使用華北2(北京)地區的模型,需要將base_http_api_url替換為:https://dashscope.aliyuncs.com/api/v1
dashscope.base_http_api_url = 'https://dashscope-intl.aliyuncs.com/api/v1'
# 1. 定義工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如新加坡、紐約等。",
}
},
"required": ["location"],
},
},
}
]
# 2. 類比天氣查詢工具
def get_current_weather(arguments):
weather_conditions = ["晴天", "多雲", "雨天"]
random_weather = random.choice(weather_conditions)
location = arguments["location"]
return f"{location}今天是{random_weather}。"
# 3. 封裝模型響應函數
def get_response(messages):
response = Generation.call(
# 如果沒有配置環境變數,請將下行替換為api_key="sk-xxx"
api_key=os.getenv("DASHSCOPE_API_KEY"),
model="qwen-plus",
messages=messages,
tools=tools,
result_format="message",
)
return response
# 4. 初始化對話歷史
messages = [
{
"role": "user",
"content": "新加坡天氣咋樣"
}
]
# 5. 第一次調用模型
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)
# 6. 判斷是否需要調用工具
if "tool_calls" not in assistant_output or not assistant_output["tool_calls"]:
print(f"無需調用工具,直接回複:{assistant_output['content']}")
else:
# 7. 進入工具調用迴圈
# 迴圈條件:只要最新的模型回複中包含工具調用請求
while "tool_calls" in assistant_output and assistant_output["tool_calls"]:
tool_call = assistant_output["tool_calls"][0]
# 解析工具調用的資訊
func_name = tool_call["function"]["name"]
arguments = json.loads(tool_call["function"]["arguments"])
tool_call_id = tool_call.get("id") # 擷取 tool_call_id
print(f"正在調用工具 [{func_name}],參數:{arguments}")
# 執行對應的工具函數
tool_result = get_current_weather(arguments)
# 構造工具返回資訊
tool_message = {
"role": "tool",
"content": tool_result,
"tool_call_id": tool_call_id
}
print(f"工具返回:{tool_message['content']}")
messages.append(tool_message)
# 再次調用模型,讓模型基於工具結果進行回複
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)
# 8. 輸出最終的自然語言回複
print(f"助手最終回複:{assistant_output['content']}")import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.tools.FunctionDefinition;
import com.alibaba.dashscope.tools.ToolCallBase;
import com.alibaba.dashscope.tools.ToolCallFunction;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.utils.JsonUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Main {
/**
* 定義工具的本地實現。
* @param arguments 模型傳入的、包含工具所需參數的JSON字串。
* @return 工具執行後的結果字串。
*/
public static String getCurrentWeather(String arguments) {
try {
// 模型提供的參數是JSON格式的,需要我們手動解析。
ObjectMapper objectMapper = new ObjectMapper();
JsonNode argsNode = objectMapper.readTree(arguments);
String location = argsNode.get("location").asText();
// 用隨機結果來類比真實的API調用或商務邏輯。
List<String> weatherConditions = Arrays.asList("晴天", "多雲", "雨天");
String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size()));
return location + "今天是" + randomWeather + "。";
} catch (Exception e) {
// 異常處理,確保程式健壯性。
return "無法解析地點參數。";
}
}
public static void main(String[] args) {
try {
// 向模型描述(註冊)我們的工具。
String weatherParamsSchema =
"{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"城市或縣區,比如新加坡、紐約等。\"}},\"required\":[\"location\"]}";
FunctionDefinition weatherFunction = FunctionDefinition.builder()
.name("get_current_weather") // 工具的唯一標識名,必須與本地實現對應。
.description("當你想查詢指定城市的天氣時非常有用。") // 清晰的描述能協助模型更好地決定何時使用該工具。
.parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject())
.build();
// 如果使用華北2(北京)地區的模型,需要將url替換為:https://dashscope.aliyuncs.com/api/v1
Generation gen = new Generation(Protocol.HTTP.getValue(), "https://dashscope-intl.aliyuncs.com/api/v1");
String userInput = "新加坡天氣咋樣";
List<Message> messages = new ArrayList<>();
messages.add(Message.builder().role(Role.USER.getValue()).content(userInput).build());
// 首次調用模型。將使用者的請求和我們定義好的工具列表一同發送給模型。
GenerationParam param = GenerationParam.builder()
.model("qwen-plus") // 指定需要調用的模型。
.apiKey(System.getenv("DASHSCOPE_API_KEY")) // 從環境變數中擷取API Key。
.messages(messages) // 傳入當前的對話歷史。
.tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // 傳入可用的工具列表。
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
GenerationResult result = gen.call(param);
Message assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput); // 將模型的首次回複也加入到對話歷史中。
// 檢查模型的回複,判斷它是否請求調用工具。
if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
// 情況A:模型沒有調用工具,而是直接給出了回答。
System.out.println("無需調用天氣查詢工具,直接回複:" + assistantOutput.getContent());
} else {
// 情況B:模型決定調用工具。
// 使用 while 迴圈可以處理模型連續調用多次工具的情境。
while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) {
ToolCallBase toolCall = assistantOutput.getToolCalls().get(0);
// 從模型的回複中解析出工具調用的具體資訊(要調用的函數名、參數)。
ToolCallFunction functionCall = (ToolCallFunction) toolCall;
String funcName = functionCall.getFunction().getName();
String arguments = functionCall.getFunction().getArguments();
System.out.println("正在調用工具 [" + funcName + "],參數:" + arguments);
// 根據工具名,在本地執行對應的Java方法。
String toolResult = getCurrentWeather(arguments);
// 構造一個 role 為 "tool" 的訊息,其中包含工具的執行結果。
Message toolMessage = Message.builder()
.role("tool")
.toolCallId(toolCall.getId())
.content(toolResult)
.build();
System.out.println("工具返回:" + toolMessage.getContent());
messages.add(toolMessage); // 將工具的返回結果也加入到對話歷史中。
// 再次調用模型。
param.setMessages(messages);
result = gen.call(param);
assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput);
}
// 列印模型經過總結後,產生的最終回複。
System.out.println("助手最終回複:" + assistantOutput.getContent());
}
} catch (NoApiKeyException | InputRequiredException e) {
System.err.println("錯誤: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}運行後得到如下輸出:
正在調用工具 [get_current_weather],參數:{'location': '新加坡'}
工具返回:新加坡今天是多雲。
助手最終回複:新加坡今天是多雲的天氣。如何使用
Function Calling 支援兩種傳入工具資訊的方式:
方式一:通過 tools 參數傳入(推薦)
請參見如何使用,按照定義工具、建立messages數組、發起 Function Calling、運行工具函數、大模型總結工具函數輸出的步驟進行調用。
方式二:通過 System Message 傳入
通過 tools 參數傳入時,服務端會根據模型自動適配合適的 prompt 模板並組裝,因此推薦您優先使用 tools 參數。 如果您在使用 Qwen 模型時不期望使用 tools 參數,請參見通過 System Message 傳入工具資訊。
以下內容以 OpenAI 相容調用方式為例,通過 tools 參數傳入工具資訊,向您分步驟介紹 Function Calling 的詳細用法。
假設業務情境中會收到天氣查詢與時間查詢兩類問題。
1. 定義工具
工具是串連大模型與外部世界的橋樑,首先需要定義工具。
1.1. 建立工具函數
建立兩個工具函數:天氣查詢工具與時間查詢工具。
天氣查詢工具
接收
arguments參數,arguments格式為{"location": "查詢的地點"}。工具的輸出為字串,格式為:“{位置}今天是{天氣}”。為了便於示範,此處定義的天氣查詢工具並不真正查詢天氣,會從晴天、多雲、雨天隨機播放。在實際業務中可使用如高德天氣查詢等工具進行替換。
時間查詢工具
時間查詢工具不需要輸入參數。工具的輸出為字串,格式為:
“目前時間:{查詢到的時間}。”。如果使用 Node.js,請運行
npm install date-fns安裝擷取時間的工具包 date-fns:
## 步驟1:定義工具函數
# 添加匯入random模組
import random
from datetime import datetime
# 類比天氣查詢工具。返回結果樣本:“北京今天是雨天。”
def get_current_weather(arguments):
# 定義備選的天氣條件列表
weather_conditions = ["晴天", "多雲", "雨天"]
# 隨機播放一個天氣條件
random_weather = random.choice(weather_conditions)
# 從 JSON 中提取位置資訊
location = arguments["location"]
# 返回格式化的天氣資訊
return f"{location}今天是{random_weather}。"
# 查詢目前時間的工具。返回結果樣本:“目前時間:2024-04-15 17:15:18。“
def get_current_time():
# 擷取當前日期和時間
current_datetime = datetime.now()
# 格式化當前日期和時間
formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
# 返回格式化後的目前時間
return f"目前時間:{formatted_time}。"
# 測試載入器函數並輸出結果,運行後續步驟時可以去掉以下四句測試代碼
print("測試載入器輸出:")
print(get_current_weather({"location": "上海"}))
print(get_current_time())
print("\n")// 步驟1:定義工具函數
// 匯入時間查詢工具
import { format } from 'date-fns';
function getCurrentWeather(args) {
// 定義備選的天氣條件列表
const weatherConditions = ["晴天", "多雲", "雨天"];
// 隨機播放一個天氣條件
const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
// 從 JSON 中提取位置資訊
const location = args.location;
// 返回格式化的天氣資訊
return `${location}今天是${randomWeather}。`;
}
function getCurrentTime() {
// 擷取當前日期和時間
const currentDatetime = new Date();
// 格式化當前日期和時間
const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
// 返回格式化後的目前時間
return `目前時間:${formattedTime}。`;
}
// 測試載入器函數並輸出結果,運行後續步驟時可以去掉以下四句測試代碼
console.log("測試載入器輸出:")
console.log(getCurrentWeather({location:"上海"}));
console.log(getCurrentTime());
console.log("\n")運行工具後,得到輸出:
測試載入器輸出:
上海今天是多雲。
目前時間:2025-01-08 20:21:45。1.2 建立 tools 數組
人類在選擇工具之前,需要對工具有全面的瞭解,包括工具的功能、何時使用以及輸入參數等。大模型也需要這些資訊才能更準確地選擇工具。請根據以下 JSON 格式提供工具資訊。
| 對於天氣查詢工具來說,工具描述資訊的格式如下: |
在發起 Function Calling 前,需在代碼中定義工具資訊數組(tools),包含每個工具的函數名、描述和參數定義。這個數組在後續發起Function Calling請求時作為參數傳入。
# 請將以下代碼粘貼到步驟1代碼後
## 步驟2:建立 tools 數組
tools = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "當你想知道現在的時間時非常有用。",
"parameters": {}
}
},
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。",
}
},
"required": ["location"]
}
}
}
]
tool_name = [tool["function"]["name"] for tool in tools]
print(f"建立了{len(tools)}個工具,為:{tool_name}\n")// 請將以下代碼粘貼到步驟1代碼後
// 步驟2:建立 tools 數組
const tools = [
{
type: "function",
function: {
name: "get_current_time",
description: "當你想知道現在的時間時非常有用。",
parameters: {}
}
},
{
type: "function",
function: {
name: "get_current_weather",
description: "當你想查詢指定城市的天氣時非常有用。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "城市或縣區,比如北京市、杭州市、餘杭區等。",
}
},
required: ["location"]
}
}
}
];
const toolNames = tools.map(tool => tool.function.name);
console.log(`建立了${tools.length}個工具,為:${toolNames.join(', ')}\n`);2. 建立messages數組
Function Calling 通過 messages 數組向大模型傳入指令與上下文資訊。發起 Function Calling 前,messages 數組需要包含 System Message 與 User Message。
System Message
儘管在建立 tools 數組時已經對工具的作用與何時使用工具進行了描述,但在 System Message 中強調何時調用工具通常會提高工具調用的準確率。在當前情境下,可以將 System Prompt 設定為:
你是一個很有協助的助手。如果使用者提問關於天氣的問題,請調用 ‘get_current_weather’ 函數;
如果使用者提問關於時間的問題,請調用‘get_current_time’函數。
請以友好的語氣回答問題。User Message
User Message 用於傳入使用者提問的問題。假設使用者提問“上海天氣”,此時的 messages 數組為:
# 步驟3:建立messages數組
# 請將以下代碼粘貼到步驟2 代碼後
# 文本產生模型的 User Message樣本
messages = [
{
"role": "system",
"content": """你是一個很有協助的助手。如果使用者提問關於天氣的問題,請調用 ‘get_current_weather’ 函數;
如果使用者提問關於時間的問題,請調用‘get_current_time’函數。
請以友好的語氣回答問題。""",
},
{
"role": "user",
"content": "上海天氣"
}
]
# Qwen3-VL模型的 User Message樣本
# messages=[
# {
# "role": "system",
# "content": """你是一個很有協助的助手。如果使用者提問關於天氣的問題,請調用 ‘get_current_weather’ 函數;
# 如果使用者提問關於時間的問題,請調用‘get_current_time’函數。
# 請以友好的語氣回答問題。""",
# },
# {"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": "根據映像上的地點,查詢該地點當前天氣"}]},
# ]
print("messages 數組建立完成\n") // 步驟3:建立messages數組
// 請將以下代碼粘貼到步驟2 代碼後
const messages = [
{
role: "system",
content: "你是一個很有協助的助手。如果使用者提問關於天氣的問題,請調用 ‘get_current_weather’ 函數; 如果使用者提問關於時間的問題,請調用‘get_current_time’函數。請以友好的語氣回答問題。",
},
{
role: "user",
content: "上海天氣"
}
];
// Qwen3-VL模型的 User Message樣本,
// 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: "圖中描繪的是什麼景象?"}]
// }];
console.log("messages 數組建立完成\n");由於備選工具包含天氣查詢與時間查詢,也可提問關於目前時間的問題。
3. 發起 Function Calling
將建立好的 tools 與 messages 傳入大模型,即可發起一次 Function Calling。大模型會判斷是否調用工具。若調用,則返回該工具的函數名與參數。
支援的模型參見支援的模型。
# 步驟4:發起 function calling
# 請將以下代碼粘貼到步驟3 代碼後
from openai import OpenAI
import os
client = OpenAI(
# 如果使用華北2(北京)地區的模型,需要使用華北2(北京)地區的 API Key,擷取連結:https://bailian.console.alibabacloud.com/?tab=model#/api-key
# 若沒有配置環境變數,請用百鍊API Key將下行替換為:api_key="sk-xxx",
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 如果使用華北2(北京)地區的模型,需要將base_url替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
def function_calling():
completion = client.chat.completions.create(
# 此處以qwen-plus為例,可按需更換模型名稱。模型列表:https://www.alibabacloud.com/help/zh/model-studio/getting-started/models
model="qwen-plus",
messages=messages,
tools=tools
)
print("返回對象:")
print(completion.choices[0].message.model_dump_json())
print("\n")
return completion
print("正在發起function calling...")
completion = function_calling()// 步驟4:發起 function calling
// 請將以下代碼粘貼到步驟3 代碼後
import OpenAI from "openai";
const openai = new OpenAI(
{
// 如果使用華北2(北京)地區的模型,需要使用華北2(北京)地區的 API Key,擷取連結:https://bailian.console.alibabacloud.com/?tab=model#/api-key
// 若沒有配置環境變數,請用百鍊API Key將下行替換為:apiKey: "sk-xxx",
apiKey: process.env.DASHSCOPE_API_KEY,
// 如果使用華北2(北京)地區的模型,需要將baseURL替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
}
);
async function functionCalling() {
const completion = await openai.chat.completions.create({
model: "qwen-plus", // 此處以qwen-plus為例,可按需更換模型名稱。模型列表:https://www.alibabacloud.com/help/zh/model-studio/getting-started/models
messages: messages,
tools: tools
});
console.log("返回對象:");
console.log(JSON.stringify(completion.choices[0].message));
console.log("\n");
return completion;
}
const completion = await functionCalling();由於使用者提問為上海天氣,大模型指定需要使用的工具函數名稱為:"get_current_weather",函數的入參為:"{\"location\": \"上海\"}"。
{
"content": "",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": [
{
"id": "call_6596dafa2a6a46f7a217da",
"function": {
"arguments": "{\"location\": \"上海\"}",
"name": "get_current_weather"
},
"type": "function",
"index": 0
}
]
}需要注意,如果問題被大模型判斷為無需使用工具,會通過content參數直接回複。在輸入“你好”時,tool_calls參數為空白,返回對象格式為:
{
"content": "你好!有什麼可以協助你的嗎?如果你有關於天氣或者時間的問題,我特別擅長回答。",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": null
}如果tool_calls參數為空白,可使程式直接返回content,無需運行以下步驟。
若希望每次發起 Function Calling 後大模型都可以選擇指定工具,請參見強制工具調用。
4. 運行工具函數
運行工具函數是將大模型的決策轉化為實際操作的關鍵步驟。
運行工具函數的過程由您的計算環境而非大模型來完成。
由於大模型只可以輸出字串格式的內容,因此在運行工具函數前,需要對字串格式的工具函數與入參分別解析。
工具函數
建立一個工具函數名稱到工具函數實體的映射
function_mapper,將返回的工具函數字串映射到工具函數實體;入參
Function Calling 返回的入參為 JSON 字串,使用工具將其解析為 JSON 對象,提取入參資訊。
完成解析後,將參數傳入工具函數並執行,擷取輸出結果。
# 步驟5:運行工具函數
# 請將以下代碼粘貼到步驟4 代碼後
import json
print("正在執行工具函數...")
# 從返回的結果中擷取函數名稱和入參
function_name = completion.choices[0].message.tool_calls[0].function.name
arguments_string = completion.choices[0].message.tool_calls[0].function.arguments
# 使用json模組解析參數字串
arguments = json.loads(arguments_string)
# 建立一個函數映射表
function_mapper = {
"get_current_weather": get_current_weather,
"get_current_time": get_current_time
}
# 擷取函數實體
function = function_mapper[function_name]
# 如果入參為空白,則直接調用函數
if arguments == {}:
function_output = function()
# 否則,傳入參數後調用函數
else:
function_output = function(arguments)
# 列印工具的輸出
print(f"工具函數輸出:{function_output}\n")// 步驟5:運行工具函數
// 請將以下代碼粘貼到步驟4 代碼後
console.log("正在執行工具函數...");
const function_name = completion.choices[0].message.tool_calls[0].function.name;
const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments;
// 使用JSON模組解析參數字串
const args = JSON.parse(arguments_string);
// 建立一個函數映射表
const functionMapper = {
"get_current_weather": getCurrentWeather,
"get_current_time": getCurrentTime
};
// 擷取函數實體
const func = functionMapper[function_name];
// 如果入參為空白,則直接調用函數
let functionOutput;
if (Object.keys(args).length === 0) {
functionOutput = func();
} else {
// 否則,傳入參數後調用函數
functionOutput = func(args);
}
// 列印工具的輸出
console.log(`工具函數輸出:${functionOutput}\n`);運行後得到如下輸出:
上海今天是多雲。在實際業務情境中,許多工具的核心功能是執行具體操作(如郵件發送、檔案上傳等),而非資料查詢,這類工具執行後不會輸出字串。為協助大模型瞭解工具運行狀態,建議在設計此類工具時添加如“郵件發送完成”、"操作執行失敗"等狀態原因資訊。
5. 大模型總結工具函數輸出
工具函數的輸出格式較為固定,如果直接返回給使用者,可能會有語氣生硬、不夠靈活等問題。如果您希望大模型能夠綜合使用者輸入以及工具輸出結果,產生自然語言風格的回複,可以將工具輸出提交到模型上下文並再次向模型發出請求。
添加 Assistant Message
發起 Function Calling後,通過
completion.choices[0].message得到 Assistant Message,首先將它添加到 messages 數組中;添加 Tool Message
將工具的輸出通過
{"role": "tool", "content": "工具的輸出","tool_call_id": completion.choices[0].message.tool_calls[0].id}形式添加到 messages 數組。說明請確保工具的輸出為字串格式。
tool_call_id是系統為每一次的工具調用請求產生的唯一識別碼。模型可能一次性要求調用多個工具,將多個工具結果返回給模型時,tool_call_id可確保工具的輸出結果能夠與它的調用意圖對應。
# 步驟6:向大模型提交工具輸出
# 請將以下代碼粘貼到步驟5 代碼後
messages.append(completion.choices[0].message)
print("已添加assistant message")
messages.append({"role": "tool", "content": function_output, "tool_call_id": completion.choices[0].message.tool_calls[0].id})
print("已添加tool message\n")// 步驟6:向大模型提交工具輸出
// 請將以下代碼粘貼到步驟5 代碼後
messages.push(completion.choices[0].message);
console.log("已添加 assistant message")
messages.push({
"role": "tool",
"content": functionOutput,
"tool_call_id": completion.choices[0].message.tool_calls[0].id
});
console.log("已添加 tool message\n");此時的 messages 數組為:
[
System Message -- 指引模型調用工具的策略
User Message -- 使用者的問題
Assistant Message -- 模型返回的工具調用資訊
Tool Message -- 工具的輸出資訊(如果採用下文介紹的並行工具調用,可能有多個 Tool Message)
]更新 messages 數組後,運行以下代碼。
# 步驟7:大模型總結工具輸出
# 請將以下代碼粘貼到步驟6 代碼後
print("正在總結工具輸出...")
completion = function_calling()// 步驟7:大模型總結工具輸出
// 請將以下代碼粘貼到步驟6 代碼後
console.log("正在總結工具輸出...");
const completion_1 = await functionCalling();可從content得到回複內容:“上海今天的天氣是多雲。如果您有其他問題,歡迎繼續提問。”
{
"content": "上海今天的天氣是多雲。如果您有其他問題,歡迎繼續提問。",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": null
}至此,您已完成了一次完整的 Function Calling 流程。
進階用法
指定工具調用方式
並行工具調用
單一城市的天氣查詢經過一次工具調用即可。如果輸入問題需要調用多次工具,如“北京上海的天氣如何”或“杭州天氣,以及現在幾點了”,發起 Function Calling 後只會返回一個工具調用資訊,以提問“北京上海的天氣如何”為例:
{
"content": "",
"refusal": null,
"role": "assistant",
"audio": null,
"function_call": null,
"tool_calls": [
{
"id": "call_61a2bbd82a8042289f1ff2",
"function": {
"arguments": "{\"location\": \"北京市\"}",
"name": "get_current_weather"
},
"type": "function",
"index": 0
}
]
}返回結果中只有北京市的入參資訊。為瞭解決這一問題,在發起 Function Calling時,可佈建要求參數parallel_tool_calls為true,這樣返回對象中將包含所有需要調用的工具函數與入參資訊。
並行工具調用適合任務之間無依賴的情況。若任務之間有依賴關係(工具A的輸入與工具B的輸出結果有關),請參見快速開始,通過while迴圈實現串列工具調用(一次調用一個工具)。
def function_calling():
completion = client.chat.completions.create(
model="qwen-plus", # 此處以 qwen-plus 為例,可按需更換模型名稱
messages=messages,
tools=tools,
# 新增參數
parallel_tool_calls=True
)
print("返回對象:")
print(completion.choices[0].message.model_dump_json())
print("\n")
return completion
print("正在發起function calling...")
completion = function_calling()async function functionCalling() {
const completion = await openai.chat.completions.create({
model: "qwen-plus", // 此處以qwen-plus為例,可按需更換模型名稱
messages: messages,
tools: tools,
parallel_tool_calls: true
});
console.log("返回對象:");
console.log(JSON.stringify(completion.choices[0].message));
console.log("\n");
return completion;
}
const completion = await functionCalling();在返回對象的tool_calls數組中包含了北京上海的入參資訊:
{
"content": "",
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"北京市\"}"
},
"index": 0,
"id": "call_c2d8a3a24c4d4929b26ae2",
"type": "function"
},
{
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"上海市\"}"
},
"index": 1,
"id": "call_dc7f2f678f1944da9194cd",
"type": "function"
}
]
}強制工具調用
大模型產生內容具有不確定性,有時會選擇錯誤的工具進行調用。如果您希望對於某一類問題,大模型能夠採取人為設定的策略(如強制使用某個工具、強制不使用工具),可修改tool_choice參數。tool_choice參數的預設值為"auto",表示由大模型自主判斷如何進行工具調用。
大模型總結工具函數輸出時,請將tool_choice參數去除,否則 API 仍會返回工具調用資訊。強制使用某個工具
如果您希望對於某一類問題,Function Calling 能強制調用某個工具,可設定
tool_choice參數為{"type": "function", "function": {"name": "the_function_to_call"}},大模型將不參與工具的選擇,只輸出入參資訊。假設當前情境中只包含天氣查詢的問題,可修改 function_calling 代碼為:
def function_calling(): completion = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, tool_choice={"type": "function", "function": {"name": "get_current_weather"}} ) print(completion.model_dump_json()) function_calling()async function functionCalling() { const response = await openai.chat.completions.create({ model: "qwen-plus", messages: messages, tools: tools, tool_choice: {"type": "function", "function": {"name": "get_current_weather"}} }); console.log("返回對象:"); console.log(JSON.stringify(response.choices[0].message)); console.log("\n"); return response; } const response = await functionCalling();無論輸入什麼問題,返回對象的工具函數都會是
get_current_weather。使用該策略前請確保問題與選擇的工具相關,否則可能返回不符合預期的結果。
強制不使用工具
如果您希望無論輸入什麼問題,Function Calling 都不會進行工具調用(返回對象中包含回複內容
content而tool_calls參數為空白),可設定tool_choice參數為"none",或不傳入tools參數,Function Calling 返回的tool_calls參數將始終為空白。假設當前情境中的問題均無需調用工具,可修改 function_calling 代碼為:
def function_calling(): completion = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, tool_choice="none" ) print(completion.model_dump_json()) function_calling()async function functionCalling() { const completion = await openai.chat.completions.create({ model: "qwen-plus", messages: messages, tools: tools, tool_choice: "none" }); console.log("返回對象:"); console.log(JSON.stringify(completion.choices[0].message)); console.log("\n"); return completion; } const completion = await functionCalling();
多輪對話
使用者可能在第一輪提問“北京天氣”,第二輪提問“上海的呢?”若模型上下文中沒有第一輪的資訊,模型無法判斷需要調用哪個工具。建議在多輪對話情境中,每輪結束後維持 messages 數組,在此基礎上添加 User Message並發起 Function Calling以及後續步驟。messages 結構如下所示:
[
System Message -- 指引模型調用工具的策略
User Message -- 使用者的問題
Assistant Message -- 模型返回的工具調用資訊
Tool Message -- 工具的輸出資訊
Assistant Message -- 模型總結的工具調用資訊
User Message -- 使用者第二輪的問題
]流式輸出
為了提升使用者體驗和減少等待時間,可使用流式輸出即時擷取工具函數名稱與入參資訊。其中:
工具調用的參數資訊:以資料流的形式分塊返回。
工具函數名稱:在流式響應的第一個資料區塊中返回。
from openai import OpenAI
import os
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 如果使用華北2(北京)地區的模型,需要將替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。",
}
},
"required": ["location"],
},
},
},
]
stream = client.chat.completions.create(
model="qwen-plus",
messages=[{"role": "user", "content": "杭州天氣?"}],
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(
{
// 如果使用華北2(北京)地區的模型,需要使用華北2(北京)地區的 API Key,擷取連結:https://bailian.console.alibabacloud.com/?tab=model#/api-key
// 若沒有配置環境變數,請用百鍊API Key將下行替換為:apiKey: "sk-xxx",
apiKey: process.env.DASHSCOPE_API_KEY,
// 如果使用華北2(北京)地區的模型,需要將baseURL替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"
}
);
const tools = [
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"]
}
}
}
];
const stream = await openai.chat.completions.create({
model: "qwen-plus",
messages: [{ role: "user", content: "北京天氣" }],
tools: tools,
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0].delta;
console.log(delta.tool_calls);
}運行後得到如下輸出:
[ChoiceDeltaToolCall(index=0, id='call_8f08d2b0fc0c4d8fab7123', function=ChoiceDeltaToolCallFunction(arguments='{"location":', name='get_current_weather'), type='function')]
[ChoiceDeltaToolCall(index=0, id='', function=ChoiceDeltaToolCallFunction(arguments=' "杭州"}', name=None), type='function')]
None運行以下代碼拼接入參資訊(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]));獲得如下輸出:
{"index":0,"id":"call_16c72bef988a4c6c8cc662","function":{"arguments":"{\"location\": \"杭州\"}","name":"get_current_weather"},"type":"function"}在使用大模型總結工具函數輸出步驟,添加的 Assistant Message 需要符合下方格式。僅需將下方的tool_calls中的元素替換為以上內容即可。
{
"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,
}
],
}Qwen3-Omni-Flash模型的工具調用
在擷取工具資訊階段,Qwen3-Omni-Flash模型的使用方式與其他模型有以下不同:
必須使用流式輸出:
Qwen3-Omni-Flash僅支援流式輸出,在擷取工具資訊時也必須設定stream=True。建議僅輸出文本:模型在擷取工具資訊(函數的名稱和參數)時僅需文本資訊,為避免產生不必要的音頻,建議設定
modalities=["text"]。當輸出包含文本和音頻兩種模態時,擷取工具資訊時需要跳過音頻資料區塊。
Qwen3-Omni-Flash詳情參見:全模態。
from openai import OpenAI
import os
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
# 如果使用華北2(北京)地區的模型,需要將替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。",
}
},
"required": ["location"],
},
},
},
]
completion = client.chat.completions.create(
model="qwen3-omni-flash",
messages=[{"role": "user", "content": "杭州天氣?"}],
# 設定輸出資料的模態,可取值:["text"]、["text","audio"],建議設定為["text"]
modalities=["text"],
# stream 必須設定為 True,否則會報錯
stream=True,
tools=tools
)
for chunk in completion:
# 如果輸出包含音頻模態,請將下列條件改為: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(
{
// 如果使用華北2(北京)地區的模型,需要使用華北2(北京)地區的 API Key,擷取連結:https://bailian.console.alibabacloud.com/?tab=model#/api-key
// 若沒有配置環境變數,請用百鍊API Key將下行替換為:apiKey: "sk-xxx",
apiKey: process.env.DASHSCOPE_API_KEY,
// 如果使用華北2(北京)地區的模型,需要將baseURL替換為:https://dashscope.aliyuncs.com/compatible-mode/v1
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
}
);
const tools = [
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"]
}
}
}
];
const stream = await openai.chat.completions.create({
model: "qwen3-omni-flash",
messages: [
{
"role": "user",
"content": "杭州天氣"
}],
stream: true,
// 設定輸出資料的模態,可取值:["text"]、["text","audio"],建議設定為["text"]
modalities: ["text"],
tools:tools
});
for await (const chunk of stream) {
// 如果輸出包含音頻,請將條件陳述式替換為: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);
}}運行後得到如下輸出:
[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": "杭州市"}', name=None), type='function')]
None拼接入參資訊(arguments)的代碼請參見流式輸出。
深度思考模型的工具調用
深度思考模型在輸出工具調用資訊前會進行思考,可以提升決策的可解釋性與可靠性。
思考過程
模型逐步分析使用者意圖、識別所需工具、驗證參數合法性,並規劃調用策略;
工具調用
模型以結構化格式輸出一個或多個函數調用請求。
支援並行工具調用。
以下展示流式調用深度思考模型的工具調用樣本。
文本產生思考模型請參見:深度思考;多模態思考模型請參見:視覺理解、全模態。
tool_choice參數只支援設定為"auto"(預設值,表示由模型自主選擇工具)或"none"(強制模型不選擇工具)。
OpenAI相容
Python
範例程式碼
import os
from openai import OpenAI
# 初始化OpenAI用戶端,配置阿里雲DashScope服務
client = OpenAI(
# 若沒有配置環境變數,請用阿里雲百鍊API Key將下行替換為:api_key="sk-xxx",
api_key=os.getenv("DASHSCOPE_API_KEY"), # 從環境變數讀取API密鑰
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)
# 定義可用工具列表
tools = [
# 工具1 擷取當前時刻的時間
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "當你想知道現在的時間時非常有用。",
"parameters": {} # 無需參數
}
},
# 工具2 擷取指定城市的天氣
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"] # 必填參數
}
}
}
]
messages = [{"role": "user", "content": input("請輸入問題:")}]
# Qwen3-VL模型的 message樣本
# 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": "根據映像上的地點,請問該地點當前的天氣"}]
# }]
completion = client.chat.completions.create(
# 此處以qwen-plus為例,可更換為其它深度思考模型
model="qwen-plus",
messages=messages,
extra_body={
# 開啟深度思考,該參數對qwen3-30b-a3b-thinking-2507、qwen3-235b-a22b-thinking-2507、QwQ 模型無效
"enable_thinking": True
},
tools=tools,
parallel_tool_calls=True,
stream=True,
# 解除注釋後,可以擷取到token消耗資訊
# stream_options={
# "include_usage": True
# }
)
reasoning_content = "" # 定義完整思考過程
answer_content = "" # 定義完整回複
tool_info = [] # 儲存工具調用資訊
is_answering = False # 判斷是否結束思考過程並開始回複
print("="*20+"思考過程"+"="*20)
for chunk in completion:
if not chunk.choices:
# 處理用量統計資訊
print("\n"+"="*20+"Usage"+"="*20)
print(chunk.usage)
else:
delta = chunk.choices[0].delta
# 處理AI的思考過程(鏈式推理)
if hasattr(delta, 'reasoning_content') and delta.reasoning_content is not None:
reasoning_content += delta.reasoning_content
print(delta.reasoning_content,end="",flush=True) # 即時輸出思考過程
# 處理最終回複內容
else:
if not is_answering: # 首次進入回複階段時列印標題
is_answering = True
print("\n"+"="*20+"回複內容"+"="*20)
if delta.content is not None:
answer_content += delta.content
print(delta.content,end="",flush=True) # 流式輸出回複內容
# 處理工具調用資訊(支援並行工具調用)
if delta.tool_calls is not None:
for tool_call in delta.tool_calls:
index = tool_call.index # 工具調用索引,用於並行調用
# 動態擴充工具資訊儲存列表
while len(tool_info) <= index:
tool_info.append({})
# 收集工具調用ID(用於後續函數調用)
if tool_call.id:
tool_info[index]['id'] = tool_info[index].get('id', '') + tool_call.id
# 收集合函式名稱(用於後續路由到具體函數)
if tool_call.function and tool_call.function.name:
tool_info[index]['name'] = tool_info[index].get('name', '') + tool_call.function.name
# 收集合函式參數(JSON字串格式,需要後續解析)
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+"工具調用資訊"+"="*19)
if not tool_info:
print("沒有工具調用")
else:
print(tool_info)返回結果
輸入“四個直轄市的天氣”,得到以下返回結果:
====================思考過程====================
好的,使用者問的是“四個直轄市的天氣”。首先,我需要明確四個直轄市是哪幾個。根據中國的行政區劃,直轄市包括北京、上海、天津和重慶。所以使用者想知道這四個城市的天氣情況。
接下來,我需要檢查可用的工具。提供的工具中有get_current_weather函數,參數是location,類型字串。每個城市需要單獨查詢,因為函數一次只能查一個地點。因此,我需要為每個直轄市調用一次這個函數。
然後,我需要考慮如何產生正確的工具調用。每個調用應該包含城市名稱作為參數。比如,第一個調用是北京,第二個是上海,依此類推。確保參數名稱是location,值是正確的城市名。
另外,使用者可能希望得到每個城市的天氣資訊,所以需要確保每個函數調用都正確無誤。可能需要連續調用四次,每次對應一個城市。不過,根據工具的使用規則,可能需要分多次處理,或者一次產生多個調用。但根據樣本,可能每次只調用一個函數,所以可能需要逐步進行。
最後,確認是否有其他需要考慮的因素,比如參數是否正確,城市名稱是否準確,以及是否需要處理可能的錯誤情況,比如城市不存在或API不可用。但目前看來,四個直轄市都是明確的,應該沒問題。
====================回複內容====================
===================工具調用資訊===================
[{'id': 'call_767af2834c12488a8fe6e3', 'name': 'get_current_weather', 'arguments': '{"location": "北京市"}'}, {'id': 'call_2cb05a349c89437a947ada', 'name': 'get_current_weather', 'arguments': '{"location": "上海市"}'}, {'id': 'call_988dd180b2ca4b0a864ea7', 'name': 'get_current_weather', 'arguments': '{"location": "天津市"}'}, {'id': 'call_4e98c57ea96a40dba26d12', 'name': 'get_current_weather', 'arguments': '{"location": "重慶市"}'}]Node.js
範例程式碼
import OpenAI from "openai";
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const openai = new OpenAI({
apiKey: process.env.DASHSCOPE_API_KEY,
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
});
const tools = [
{
type: "function",
function: {
name: "get_current_time",
description: "當你想知道現在的時間時非常有用。",
parameters: {}
}
},
{
type: "function",
function: {
name: "get_current_weather",
description: "當你想查詢指定城市的天氣時非常有用。",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
required: ["location"]
}
}
}
];
async function main() {
const rl = readline.createInterface({ input, output });
const question = await rl.question("請輸入您的問題:");
rl.close();
const messages = [{ role: "user", content: question }];
// Qwen3-VL模型的 message樣本
// 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: "圖中地點的天氣?"}]
// }];
let reasoningContent = "";
let answerContent = "";
const toolInfo = [];
let isAnswering = false;
console.log("=".repeat(20) + "思考過程" + "=".repeat(20));
try {
const stream = await openai.chat.completions.create({
// 此處以qwen-plus為例,可更換為其它深度思考模型
model: "qwen-plus",
messages,
// 開啟深度思考,該參數對qwen3-30b-a3b-thinking-2507、qwen3-235b-a22b-thinking-2507、QwQ 模型無效
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;
// 處理思考過程
if (delta.reasoning_content) {
reasoningContent += delta.reasoning_content;
process.stdout.write(delta.reasoning_content);
}
// 處理回複內容
else {
if (!isAnswering) {
isAnswering = true;
console.log("\n" + "=".repeat(20) + "回複內容" + "=".repeat(20));
}
if (delta.content) {
answerContent += delta.content;
process.stdout.write(delta.content);
}
// 處理工具調用
if (delta.tool_calls) {
for (const toolCall of delta.tool_calls) {
const index = toolCall.index;
// 確保數組長度足夠
while (toolInfo.length <= index) {
toolInfo.push({});
}
// 更新工具ID
if (toolCall.id) {
toolInfo[index].id = (toolInfo[index].id || "") + toolCall.id;
}
// 更新函數名稱
if (toolCall.function?.name) {
toolInfo[index].name = (toolInfo[index].name || "") + toolCall.function.name;
}
// 更新參數
if (toolCall.function?.arguments) {
toolInfo[index].arguments = (toolInfo[index].arguments || "") + toolCall.function.arguments;
}
}
}
}
}
console.log("\n" + "=".repeat(19) + "工具調用資訊" + "=".repeat(19));
console.log(toolInfo.length ? toolInfo : "沒有工具調用");
} catch (error) {
console.error("發生錯誤:", error);
}
}
main(); 返回結果
輸入“四個直轄市的天氣”,得到以下返回結果:
請輸入您的問題:四個直轄市的天氣
====================思考過程====================
好的,使用者問的是四個直轄市的天氣。首先,我需要明確中國的四個直轄市分別是哪幾個。北京、上海、天津和重慶,對吧?接下來,我需要為每個城市調用天氣查詢功能。
但是使用者的問題可能需要我分別擷取這四個城市的天氣情況。每個城市都需要調用一次get_current_weather函數,參數是各自的城市名稱。我需要確保參數正確,比如直轄市的全名,比如“北京市”、“上海市”、“天津市”和“重慶市”。
然後,我需要按照順序依次調用這四個城市的天氣介面。每個調用都需要單獨的tool_call。使用者可能希望得到每個城市的當前天氣資訊,所以需要確保每個調用都正確無誤。可能需要注意每個城市的正確拼字和名稱,避免出現錯誤。例如,重慶有時可能被簡稱為“重慶市”,所以參數裡應該用全稱。
現在,我需要產生四個tool_call,每個對應一個直轄市。檢查每個參數是否正確,然後按順序排列。這樣使用者就能得到四個直轄市的天氣資料了。
====================回複內容====================
===================工具調用資訊===================
[
{
id: 'call_21dc802e717f491298d1b2',
name: 'get_current_weather',
arguments: '{"location": "北京市"}'
},
{
id: 'call_2cd3be1d2f694c4eafd4e5',
name: 'get_current_weather',
arguments: '{"location": "上海市"}'
},
{
id: 'call_48cf3f78e02940bd9085e4',
name: 'get_current_weather',
arguments: '{"location": "天津市"}'
},
{
id: 'call_e230a2b4c64f4e658d223e',
name: 'get_current_weather',
arguments: '{"location": "重慶市"}'
}
]HTTP
範例程式碼
curl
curl -X POST https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-plus",
"messages": [
{
"role": "user",
"content": "杭州天氣怎麼樣"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "當你想知道現在的時間時非常有用。",
"parameters": {}
}
},
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location":{
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"]
}
}
}
],
"enable_thinking": true,
"stream": true
}'DashScope
Python
範例程式碼
import dashscope
dashscope.base_http_api_url = "https://dashscope-intl.aliyuncs.com/api/v1/"
tools = [
# 工具1 擷取當前時刻的時間
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "當你想知道現在的時間時非常有用。",
"parameters": {} # 因為擷取目前時間無需輸入參數,因此parameters為空白字典
}
},
# 工具2 擷取指定城市的天氣
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
# 查詢天氣時需要提供位置,因此參數設定為location
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"]
}
}
}
]
# 定義問題
messages = [{"role": "user", "content": input("請輸入問題:")}]
# Qwen3-VL的 message樣本
# messages = [
# {
# "role": "user",
# "content": [
# {"image": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"},
# {"text": "映像地點天氣?"}]
# }]
# 如果使用Qwen3-VL模型,請Generation替換為MultiModalConversation介面
completion = dashscope.Generation.call(
# 此處以qwen-plus為例,可更換為其它深度思考模型
model="qwen-plus",
messages=messages,
enable_thinking=True,
tools=tools,
parallel_tool_calls=True,
stream=True,
incremental_output=True,
result_format="message"
)
reasoning_content = ""
answer_content = ""
tool_info = []
is_answering = False
print("="*20+"思考過程"+"="*20)
for chunk in completion:
if chunk.status_code == 200:
msg = chunk.output.choices[0].message
# 處理思考過程
if 'reasoning_content' in msg and msg.reasoning_content:
reasoning_content += msg.reasoning_content
print(msg.reasoning_content, end="", flush=True)
# 處理回複內容
if 'content' in msg and msg.content:
if not is_answering:
is_answering = True
print("\n"+"="*20+"回複內容"+"="*20)
answer_content += msg.content
print(msg.content, end="", flush=True)
# 處理工具調用
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': ''}) # 初始化所有欄位
# 累加式更新工具ID
if 'id' in tool_call:
tool_info[index]['id'] += tool_call.get('id', '')
# 累加式更新函數資訊
if 'function' in tool_call:
func = tool_call['function']
# 累加式更新函數名稱
if 'name' in func:
tool_info[index]['name'] += func.get('name', '')
# 累加式更新參數
if 'arguments' in func:
tool_info[index]['arguments'] += func.get('arguments', '')
print(f"\n"+"="*19+"工具調用資訊"+"="*19)
if not tool_info:
print("沒有工具調用")
else:
print(tool_info)返回結果
輸入“四個直轄市的天氣”,得到以下返回結果:
請輸入問題:四個直轄市的天氣
====================思考過程====================
好的,使用者問的是四個直轄市的天氣。首先,我需要確認中國的四個直轄市分別是哪些。北京、上海、天津和重慶,對吧?接下來,使用者需要每個城市的天氣情況,所以我需要調用天氣查詢功能。
不過,問題來了,使用者沒有指定具體的城市名稱,只是說四個直轄市。可能需要我明確每個直轄市的名稱,然後分別查詢。比如,北京、上海、天津、重慶這四個直轄市。我需要確保每個城市都正確無誤。
然後,我要檢查可用的工具,使用者提供的函數是get_current_weather,參數是location。因此,我需要為每個直轄市調用這個函數,傳入對應的城市名稱作為參數。比如,第一次調用location是北京市,第二次是上海市,第三次是天津市,第四次是重慶市。
不過,可能需要注意,像重慶這樣的直轄市,有時候可能需要更具體的區,但使用者可能只需要市級的天氣。所以直接使用直轄市名稱應該沒問題。接下來,我需要產生四個獨立的函數調用,每個對應一個直轄市。這樣使用者就能得到四個城市的天氣情況了。
最後,確保每個調用的參數正確,並且沒有遺漏。這樣使用者的問題就能得到完整的回答了。
===================工具調用資訊===================
[{'id': 'call_2f774ed97b0e4b24ab10ec', 'name': 'get_current_weather', 'arguments': '{"location": "北京市"}'}, {'id': 'call_dc3b05b88baa48c58bc33a', 'name': 'get_current_weather', 'arguments': '{"location": "上海市"}}'}, {'id': 'call_249b2de2f73340cdb46cbc', 'name': 'get_current_weather', 'arguments': '{"location": "天津市"}'}, {'id': 'call_833333634fda49d1b39e87', 'name': 'get_current_weather', 'arguments': '{"location": "重慶市"}}'}]Java
範例程式碼
// dashscope SDK的版本 >= 2.19.4
import java.util.Arrays;
import com.alibaba.dashscope.exception.UploadFileException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.tools.FunctionDefinition;
import io.reactivex.Flowable;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.lang.System;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaVersion;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
private static ObjectNode jsonSchemaWeather;
private static ObjectNode jsonSchemaTime;
static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";}
static class TimeTool {
public String call() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return "目前時間:" + now.format(formatter) + "。";
}
}
static class WeatherTool {
private String location;
public WeatherTool(String location) {
this.location = location;
}
public String call() {
return location + "今天是晴天";
}
}
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));
}
// 為文本產生模型建立工具調用方法
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));
}
// 構建文本產生模型參數,支援工具調用
private static GenerationParam buildGenerationParam(Message userMsg) {
FunctionDefinition fdWeather = buildFunctionDefinition(
"get_current_weather", "擷取指定地區的天氣", jsonSchemaWeather);
FunctionDefinition fdTime = buildFunctionDefinition(
"get_current_time", "擷取當前時刻的時間", jsonSchemaTime);
return GenerationParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-plus")
.enableThinking(true)
.messages(Arrays.asList(userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.incrementalOutput(true)
.tools(Arrays.asList(
ToolFunction.builder().function(fdWeather).build(),
ToolFunction.builder().function(fdTime).build()))
.build();
}
// 為 qwen3-vl-plus 模型建立工具調用方法
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)));
}
// 構建 Qwen3-vl 模型參數,支援工具調用
private static MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage userMsg) {
FunctionDefinition fdWeather = buildFunctionDefinition(
"get_current_weather", "擷取指定地區的天氣", jsonSchemaWeather);
FunctionDefinition fdTime = buildFunctionDefinition(
"get_current_time", "擷取當前時刻的時間", jsonSchemaTime);
return MultiModalConversationParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen3-vl-plus") // 使用多模態模型 Qwen3-vl
.enableThinking(true)
.messages(Arrays.asList(userMsg))
.tools(Arrays.asList( // 組態工具列表
ToolFunction.builder().function(fdWeather).build(),
ToolFunction.builder().function(fdTime).build()))
.build();
}
private static FunctionDefinition buildFunctionDefinition(
String name, String description, ObjectNode schema) {
return FunctionDefinition.builder()
.name(name)
.description(description)
.parameters(JsonUtils.parseString(schema.toString()).getAsJsonObject())
.build();
}
public static void main(String[] args) {
try {
Generation gen = new Generation();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("請告訴我杭州的天氣")
.build();
try {
streamCallWithMessage(gen, userMsg);
} catch (InputRequiredException e) {
throw new RuntimeException(e);
}
// 使用 Qwen3-VL 模型進行工具調用時,請解除下列注釋
// MultiModalConversation conv = new MultiModalConversation();
// MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
// .content(Arrays.asList(Collections.singletonMap("image", "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"),
// Collections.singletonMap("text", "圖中地點的天氣"))).build();
// try {
// streamCallWithMultiModalMessage(conv,userMessage);
// } catch (UploadFileException e) {
// throw new RuntimeException(e);
// }
} catch (ApiException | NoApiKeyException e) {
logger.error("An exception occurred: {}", e.getMessage());
}
System.exit(0);
}
}
返回結果
{"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":"好的,使用者讓我"}}]}}
{"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":"告訴杭州的天氣。我"}}]}}
{"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":"需要先確定是否有"}}]}}
{"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":"相關的工具可用。查看提供的"}}]}}
{"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":"工具,發現有一個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函數,參數是location"}}]}}
{"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":"。所以應該調"}}]}}
{"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":"用這個函數,參數"}}]}}
{"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":"設為杭州。不需要"}}]}}
{"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":"其他工具,因為"}}]}}
{"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":"使用者只問了"}}]}}
{"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":"天氣。接下來構造"}}]}}
{"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":"tool_call,把"}}]}}
{"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":"名稱和參數填"}}]}}
{"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":"進去。確保參數是"}}]}}
{"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對象,location是"}}]}}
{"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":"字串。檢查無"}}]}}
{"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":"誤後返回。"}}]}}
{"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\": \"杭州"}}]}}]}}
{"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
範例程式碼
curl
# ======= 重要提示 =======
# 如果使用 Qwen3-VL 模型,請修改message參數,並將url 替換為 https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
# 新加坡和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# 以下為新加坡地區url,若使用北京地區的模型,需將url替換為:https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation
# === 執行時請刪除該注釋 ===
curl -X POST "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/text-generation/generation" \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-DashScope-SSE: enable" \
-d '{
"model": "qwen-plus",
"input":{
"messages":[
{
"role": "user",
"content": "杭州天氣"
}
]
},
"parameters": {
"enable_thinking": true,
"incremental_output": true,
"result_format": "message",
"tools": [{
"type": "function",
"function": {
"name": "get_current_time",
"description": "當你想知道現在的時間時非常有用。",
"parameters": {}
}
},{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "當你想查詢指定城市的天氣時非常有用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
}
},
"required": ["location"]
}
}
}]
}
}'應用於生產環境
測試載入器調用準確率
建立評估體系:
構建貼近真實業務的測試資料集,並定義清晰的評估指標,如:工具選擇準確率、參數提取準確率、端到端成功率等。
最佳化提示詞
根據測試暴露出的具體問題(選錯工具、參數錯誤),針對性地最佳化系統提示詞、工具描述和參數描述,是核心的調優手段。
升級模型
當提示詞工程調優無法提升效能時,升級到能力更強的模型版本(如
qwen3-max-preview)是提升指標最直接有效方法。
動態控制工具數量
當應用整合的工具數量超過幾十甚至上百個時,將整個工具庫全部提供給模型會帶來以下問題:
效能下降:模型在龐大的工具集中選擇正確工具的難度劇增;
成本與延遲:大量的工具描述會消耗巨量的輸入 Token,導致費用上升和響應變慢;
解決方案是在調用模型前,增加一個工具路由/檢索層,根據使用者當前查詢,從完整的工具庫中快速、精準地篩選出一個小而相關的工具子集,再將其提供給模型。
實現工具路由的幾種主流方法:
語義檢索
預先將所有工具的描述資訊(
description)通過 Embedding 模型轉化為向量,並存入向量資料庫。當使用者查詢時,將查詢向量通過向量相似性搜尋,召回最相關的 Top-K 個工具。混合檢索
將語義檢索的“模糊比對”能力與傳統關鍵詞或中繼資料標籤的“精確匹配”能力相結合。為工具添加
tags或keywords欄位,檢索時同時進行向量搜尋和關鍵詞過濾,可以大幅提升高頻或特定情境下的召回精準度。輕量級 LLM 路由器
對於更複雜的路由邏輯,可以使用一個更小、更快、更便宜的模型(如 Qwen-Flash)作為前置“路由模型”。它的任務是根據使用者問題輸出相關的工具名稱列表。
實踐建議
保持候選集精簡:無論使用何種方法,最終提供給主模型的工具數量建議不超過 20 個。這是在模型認知負荷、成本、延遲和準確率之間的最佳平衡點。
分層過濾策略:可以構建一個漏鬥式的路由策略。例如,先用成本極低的關鍵詞/規則匹配進行第一輪篩選,過濾掉明顯不相關的工具,再對剩餘的工具進行語義檢索,從而提高效率和品質。
工具安全性原則
將工具執行能力開放給大模型時,必須將安全置於首位。核心原則是“最小許可權”和“人類確認”。
最小許可權原則:為模型提供的工具集應嚴格遵守最小許可權原則。預設情況下,工具應是唯讀(如查詢天氣、搜尋文檔),避免直接提供任何涉及狀態變更或資源操作的“寫”許可權。
危險工具隔離:請勿向大模型直接提供危險工具,例如執行任意代碼(
code interpreter)、操作檔案系統(fs.delete)、執行資料庫刪除或更新操作(db.drop_table)或涉及資金流轉的工具(payment.transfer)。人類參與:對於所有高許可權或無法復原的操作,必須引入人工審核和確認環節。模型可以產生操作請求,但最終的執行“按鈕”必須由人類使用者點擊。例如,模型可以準備好一封郵件,但發送操作需要使用者確認。
使用者體驗最佳化
Function Calling 鏈路較長,任何一個環節出問題都可能導致使用者體驗下降。
處理工具運行失敗
工具運行失敗是常見情況。可採取以下策略:
最大重試次數:設定合理的重試上限(例如 3 次),避免因連續失敗導致使用者長時間等待或系統資源浪費。
提供兜底話術:當重試耗盡或遇到無法解決的錯誤時,應向使用者返回清晰、友好的提示資訊,例如:“抱歉,我暫時無法查詢到相關資訊,可能是服務有些繁忙,請您稍後再試。”
應對處理延遲
較高的延遲會降低使用者滿意度,需要通過前端互動和後端最佳化來改善。
設定逾時時間:為 Function Calling 的每一步設定獨立且合理的逾時時間。一旦逾時,應立即中斷操作並給出反饋。
提供即時反饋:開始執行 Function Calling 時,建議在介面上給出提示,如“正在為您查詢天氣...”、“正在搜尋相關資訊...”,向使用者即時反饋處理進度。
計費說明
除了 messages 數組中的 Token 外,工具描述資訊也會作為輸入 Token 拼接到提示詞中進行計費。
通過 System Message 傳入工具資訊
錯誤碼
如果模型調用失敗並返回報錯資訊,請參見錯誤資訊進行解決。