All Products
Search
Document Center

Alibaba Cloud Model Studio:Pemanggilan Fungsi

Last Updated:Jul 01, 2026

Model Bahasa Besar (LLM) tidak dapat mengakses data real-time atau sistem eksternal. Pemanggilan Fungsi memungkinkan model memanggil alat eksternal, seperti API, database, dan user-defined function, sehingga mampu mengambil informasi atau melakukan tindakan di luar kemampuan bawaannya.

Cara kerja

Pemanggilan Fungsi bekerja melalui interaksi multi-langkah antara aplikasi Anda dan LLM:

  1. Lakukan panggilan model pertama

    Aplikasi mengirim pertanyaan pengguna dan daftar alat yang tersedia ke LLM.

  2. Terima instruksi pemanggilan alat dari model

    Jika model memutuskan untuk memanggil alat eksternal, model tersebut mengembalikan instruksi JSON yang menentukan nama fungsi dan parameter input.

    Jika model memutuskan untuk tidak memanggil alat, model tersebut mengembalikan respons dalam bahasa alami.
  3. Jalankan alat di aplikasi

    Aplikasi menjalankan alat yang ditentukan dan mendapatkan output-nya.

  4. Lakukan panggilan model kedua

    Tambahkan output alat ke array messages dan panggil kembali model tersebut.

  5. Terima respons akhir dari model

    Model menggabungkan output alat dengan pertanyaan pengguna untuk menghasilkan respons dalam bahasa alami.

Gambar berikut menunjukkan alur kerja tersebut.

Model yang didukung

Qwen

  • Model generasi teks

    • Qwen-Max: seri Qwen3.7-Max, seri Qwen3.6-Max, seri Qwen3-Max, dan seri Qwen-Max

    • Qwen-Plus: seri Qwen3.7-Plus, Qwen3.6-Plus, Qwen3.5-Plus, dan Qwen-Plus

    • Qwen-Flash: seri Qwen3.6-Flash, seri Qwen3.5-Flash, dan seri Qwen-Flash

    • Qwen-Coder: seri Qwen3-Coder, seri Qwen2.5-Coder, dan seri Qwen-Coder

    • Qwen-Turbo: seri Qwen-Turbo

    • seri open source Qwen3.6

    • seri open source Qwen3.5

    • seri open source Qwen3

    • seri open source Qwen2.5

  • Model multimodal

    • Qwen-VL: seri Qwen3-VL-Plus dan seri Qwen3-VL-Flash

    • Qwen-Omni: seri Qwen3.5-Omni-Plus, seri Qwen3.5-Omni-Flash, dan seri Qwen3-Omni-Flash

    • Qwen-Omni-Realtime: seri Qwen3.5-Omni-Plus-Realtime dan seri Qwen3.5-Omni-Flash-Realtime

    • seri open source Qwen3-VL

DeepSeek

  • deepseek-v4-pro

  • deepseek-v4-flash

  • deepseek-v3.2

  • deepseek-v3.2-exp (mode tidak berpikir)

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

  • deepseek-r1

  • deepseek-r1-0528

  • deepseek-v3

GLM

Penting

Saat menggunakan Pemanggilan Fungsi dengan model seri GLM, Anda harus menyertakan extra_body={"tool_stream": True} dalam permintaan. Jika tidak, model tidak akan mengembalikan tool_calls, dan pemanggilan alat tidak akan berfungsi.

  • glm-5.2

  • glm-5.1

  • glm-5

  • glm-4.7

  • glm-4.6

Kimi

  • kimi-k2.6

  • kimi-k2.5

  • kimi-k2-thinking

  • Moonshot-Kimi-K2-Instruct

MiniMax

MiniMax-M2.5

Mulai

Sebelum memulai, dapatkan Kunci API dan konfigurasikan sebagai Variabel lingkungan. Jika Anda menggunakan SDK OpenAI atau SDK DashScope, Anda juga harus menginstal SDK.

Contoh berikut menunjukkan alur lengkap Pemanggilan Fungsi untuk skenario kueri cuaca.

Kompatibel dengan OpenAI

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

client = OpenAI(
    # Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Jika Anda menggunakan model di wilayah China (Beijing), ganti base_url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",  
)
# Simulasikan pertanyaan pengguna
USER_QUESTION = "Bagaimana cuaca di Singapura?"
# Definisikan daftar alat
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Singapura atau New York.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

# Simulasikan alat kueri cuaca
def get_current_weather(arguments):
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"Cuaca di {location} hari ini {random_weather}."

# Enkapsulasi fungsi respons model
def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen3.6-plus",
        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)
# Jika tidak diperlukan pemanggilan alat, cetak konten secara langsung
if assistant_output.tool_calls is None:
    print(f"Tidak perlu pemanggilan alat. Respons langsung: {assistant_output.content}")
else:
    # Masuk ke loop pemanggilan alat
    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"Memanggil alat [{func_name}], argumen: {arguments}")
        # Jalankan alat
        tool_result = get_current_weather(arguments)
        # Buat pesan balasan alat
        tool_message = {
            "role": "tool",
            "tool_call_id": tool_call_id,
            "content": tool_result,  # Pertahankan output alat asli
        }
        print(f"Alat mengembalikan: {tool_message['content']}")
        messages.append(tool_message)
        # Panggil kembali model untuk mendapatkan respons ringkasan dalam bahasa alami
        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"Respons akhir Asisten: {assistant_output.content}")
import OpenAI from 'openai';  
  
// Inisialisasi klien  
const openai = new OpenAI({  
  apiKey: process.env.DASHSCOPE_API_KEY,  
  // Jika Anda menggunakan model di wilayah China (Beijing), ganti baseURL dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
  baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",  
});  
  
// Definisikan daftar alat  
const tools = [  
  {  
    type: "function",  
    function: {  
      name: "get_current_weather",  
      description: "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",  
      parameters: {  
        type: "object",  
        properties: {  
          location: {  
            type: "string",  
            description: "Kota atau distrik, seperti Singapura atau New York.",  
          },  
        },  
        required: ["location"],  
      },  
    },  
  },  
];  
  
// Simulasikan alat kueri cuaca  
const getCurrentWeather = (args) => {  
  const weatherConditions = ["Sunny", "Cloudy", "Rainy"];  
  const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];  
  const location = args.location;  
  return `Cuaca di ${location} hari ini ${randomWeather}.`;  
};  
  
// Enkapsulasi fungsi respons model  
const getResponse = async (messages) => {  
  const response = await openai.chat.completions.create({  
    model: "qwen3.6-plus",  
    enable_thinking: false,
    messages: messages,  
    tools: tools,  
  });  
  return response;  
};  

const main = async () => {  
  const input = "Bagaimana cuaca di Singapura?";

  let messages = [  
    {  
      role: "user",  
      content: input,  
    }  
  ];  
  let response = await getResponse(messages);  
  let assistantOutput = response.choices[0].message;  
  // Pastikan konten tidak null  
  if (!assistantOutput.content) assistantOutput.content = "";  
  messages.push(assistantOutput);  
  // Tentukan apakah diperlukan pemanggilan alat  
  if (!assistantOutput.tool_calls) {  
    console.log(`Tidak perlu pemanggilan alat. Respons langsung: ${assistantOutput.content}`);  
  } else {  
    // Masuk ke loop pemanggilan alat  
    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(`Memanggil alat [${funcName}], argumen:`, funcArgs);  
      // Jalankan alat  
      const toolResult = getCurrentWeather(funcArgs);  
      // Buat pesan balasan alat  
      const toolMessage = {  
        role: "tool",  
        tool_call_id: toolCallId,  
        content: toolResult,  
      };  
      console.log(`Alat mengembalikan: ${toolMessage.content}`);  
      messages.push(toolMessage);  
      // Panggil kembali model untuk mendapatkan ringkasan dalam bahasa alami  
      response = await getResponse(messages);  
      assistantOutput = response.choices[0].message;  
      if (!assistantOutput.content) assistantOutput.content = "";  
      messages.push(assistantOutput);  
    }  
    console.log(`Respons akhir Asisten: ${assistantOutput.content}`);  
  }  
};  
  
// Mulai program  
main().catch(console.error);

DashScope

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

# URL berikut untuk wilayah China (Beijing). Ganti {WorkspaceId} dengan ID ruang kerja aktual Anda. URL bervariasi berdasarkan wilayah.
dashscope.base_http_api_url = 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1'

# 1. Definisikan daftar alat
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Singapura atau New York.",
                    }
                },
                "required": ["location"],
            },
        },
    }
]

# 2. Simulasikan alat kueri cuaca
def get_current_weather(arguments):
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"Cuaca di {location} hari ini {random_weather}."

# 3. Enkapsulasi fungsi respons model
def get_response(messages):
    response = MultiModalConversation.call(
        # Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
        # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan api_key="sk-xxx"
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        # Contoh ini menggunakan model multimodal qwen3.6-plus. Untuk memanggil model teks saja seperti qwen3.6-max-preview atau qwen-plus, gunakan API model teks saja. Untuk informasi lebih lanjut, lihat https://www.alibabacloud.com/help/model-studio/qwen-api-via-dashscope
        model="qwen3.6-plus",
        enable_thinking=False,
        messages=messages,
        tools=tools,
        result_format="message",
    )
    return response

# 4. Inisialisasi riwayat percakapan
messages = [
    {
        "role": "user",
        "content": [{"text": "Bagaimana cuaca di Singapura?"}]
    }
]

# 5. Panggil model untuk pertama kalinya
response = get_response(messages)
assistant_output = response.output.choices[0].message
messages.append(assistant_output)

# 6. Tentukan apakah diperlukan pemanggilan alat
if "tool_calls" not in assistant_output or not assistant_output["tool_calls"]:
    print(f"Tidak perlu pemanggilan alat. Respons langsung: {assistant_output['content']}")
else:
    # 7. Masuk ke loop pemanggilan alat
    # Kondisi loop: selama respons model terbaru berisi permintaan pemanggilan alat
    while "tool_calls" in assistant_output and assistant_output["tool_calls"]:
        tool_call = assistant_output["tool_calls"][0]
        # Uraikan informasi pemanggilan alat
        func_name = tool_call["function"]["name"]
        arguments = json.loads(tool_call["function"]["arguments"])
        tool_call_id = tool_call.get("id")  # Dapatkan tool_call_id
        print(f"Memanggil alat [{func_name}], argumen: {arguments}")
        # Jalankan fungsi alat yang sesuai
        tool_result = get_current_weather(arguments)
        # Buat pesan balasan alat
        tool_message = {
            "role": "tool",
            "content": tool_result,
            "tool_call_id": tool_call_id
        }
        print(f"Alat mengembalikan: {tool_message['content']}")
        messages.append(tool_message)
        # Panggil kembali model untuk mendapatkan respons berdasarkan hasil alat
        response = get_response(messages)
        assistant_output = response.output.choices[0].message
        messages.append(assistant_output)
    # 8. Cetak respons bahasa alami akhir
    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"Respons akhir Asisten: {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 {

    /**
     * Mengekstrak teks biasa dari konten MultiModalMessage.
     * Format konten adalah List<Map<String, String>>, contoh: [{text=Cuacanya cerah}]
     */
    @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() : "";
    }

    /**
     * Mendefinisikan implementasi lokal alat.
     * @param arguments String JSON dari model yang berisi parameter yang diperlukan untuk alat.
     * @return String dengan hasil eksekusi alat.
     */
    public static String getCurrentWeather(String arguments) {
        try {
            // Parameter yang diberikan oleh model dalam format JSON dan perlu diurai secara manual.
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode argsNode = objectMapper.readTree(arguments);
            String location = argsNode.get("location").asText();

            // Mensimulasikan panggilan API nyata atau logika bisnis dengan hasil acak.
            List<String> weatherConditions = Arrays.asList("Sunny", "Cloudy", "Rainy");
            String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size()));

            return "Cuaca di " + location + " hari ini " + randomWeather + ".";
        } catch (Exception e) {
            // Penanganan pengecualian untuk memastikan ketahanan program.
            return "Gagal mengurai parameter lokasi.";
        }
    }

    public static void main(String[] args) {
        try {
            // Jelaskan (daftarkan) alat kami ke model.
            String weatherParamsSchema =
                    "{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"Kota atau distrik, seperti Singapura atau New York.\"}},\"required\":[\"location\"]}";

            FunctionDefinition weatherFunction = FunctionDefinition.builder()
                    .name("get_current_weather") // Nama unik alat, yang harus sesuai dengan implementasi lokal.
                    .description("Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.") // Deskripsi yang jelas membantu model memutuskan kapan menggunakan alat.
                    .parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject())
                    .build();
            // URL berikut untuk wilayah China (Beijing). Ganti {WorkspaceId} dengan ID ruang kerja aktual Anda. URL bervariasi berdasarkan wilayah.
            MultiModalConversation conv = new MultiModalConversation(Protocol.HTTP.getValue(), "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1");
            String userInput = "Bagaimana cuaca di Singapura?";

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

            // Panggilan pertama ke model. Kirim permintaan pengguna dan daftar alat yang ditentukan ke model.
            MultiModalConversationParam param = MultiModalConversationParam.builder()
                    .model("qwen3.6-plus") //Contoh ini menggunakan model multimodal qwen3.6-plus. Untuk memanggil model teks saja seperti qwen3.6-max-preview atau qwen-plus, gunakan API model teks saja. Untuk informasi lebih lanjut, lihat https://www.alibabacloud.com/help/model-studio/qwen-api-via-dashscope
                    .enableThinking(false)
                    .apiKey(System.getenv("DASHSCOPE_API_KEY")) // Dapatkan Kunci API dari variabel lingkungan. Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
                    .messages(messages) // Lewatkan riwayat percakapan saat ini.
                    .tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // Lewatkan daftar alat yang tersedia.
                    .build();

            MultiModalConversationResult result = conv.call(param);
            MultiModalMessage assistantOutput = result.getOutput().getChoices().get(0).getMessage();
            messages.add(assistantOutput); // Tambahkan respons pertama model ke riwayat percakapan.

            // Periksa respons model untuk menentukan apakah meminta pemanggilan alat.
            if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
                // Kasus A: Model tidak memanggil alat dan memberikan jawaban langsung.
                System.out.println("Tidak perlu pemanggilan alat. Respons langsung: " + getTextContent(assistantOutput.getContent()));
            } else {
                // Kasus B: Model memutuskan untuk memanggil alat.
                // Gunakan loop while untuk menangani skenario di mana model memanggil alat beberapa kali berturut-turut.
                while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) {
                    ToolCallBase toolCall = assistantOutput.getToolCalls().get(0);

                    // Uraikan informasi spesifik pemanggilan alat (nama fungsi, parameter) dari respons model.
                    ToolCallFunction functionCall = (ToolCallFunction) toolCall;
                    String funcName = functionCall.getFunction().getName();
                    String arguments = functionCall.getFunction().getArguments();
                    System.out.println("Memanggil alat [" + funcName + "], argumen: " + arguments);

                    // Jalankan metode Java yang sesuai secara lokal berdasarkan nama alat.
                    String toolResult = getCurrentWeather(arguments);

                    // Buat pesan dengan peran "tool" yang berisi hasil eksekusi alat.
                    MultiModalMessage toolMessage = MultiModalMessage.builder()
                            .role("tool")
                            .toolCallId(toolCall.getId())
                            .content(Arrays.asList(Collections.singletonMap("text", toolResult)))
                            .build();
                    System.out.println("Alat mengembalikan: " + toolResult);
                    messages.add(toolMessage); // Tambahkan hasil balik alat ke riwayat percakapan.

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

                // Cetak respons akhir yang dihasilkan model setelah dirangkum.
                System.out.println("Respons akhir Asisten: " + getTextContent(assistantOutput.getContent()));
            }

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

Setelah menjalankan kode, output berikut ditampilkan:

Memanggil alat [get_current_weather], argumen: {'location': 'Singapura'}
Alat mengembalikan: Cuaca di Singapura hari ini Cloudy.
Respons akhir Asisten: Cuaca di Singapura hari ini cloudy.

Cara menggunakan

Pemanggilan Fungsi mendukung dua metode untuk meneruskan informasi alat:

  • Metode 1: Teruskan informasi melalui parameter tools (disarankan)

    Untuk informasi lebih lanjut, lihat Cara menggunakan. Ikuti langkah-langkah untuk mendefinisikan alat, membuat array messages, melakukan Pemanggilan Fungsi, menjalankan fungsi alat, dan meminta LLM merangkum output fungsi alat.

  • Metode 2: Teruskan informasi melalui Pesan Sistem

    Meneruskan informasi melalui parameter tools memberikan hasil terbaik karena server secara otomatis menyesuaikan template prompt optimal. Jika Anda menggunakan model Qwen dan tidak ingin menggunakan parameter tools, lihat Teruskan informasi alat melalui Pesan Sistem.

Bagian berikut menggunakan API kompatibel OpenAI sebagai contoh untuk menjelaskan penggunaan detail Pemanggilan Fungsi dengan parameter tools.

Asumsikan skenario bisnis yang menerima dua jenis pertanyaan: kueri cuaca dan kueri waktu.

1. Definisikan alat

Alat menghubungkan LLM ke layanan eksternal. Anda harus terlebih dahulu mendefinisikan alat tersebut.

1.1. Buat fungsi alat

Buat dua fungsi alat: alat kueri cuaca dan alat kueri waktu.

  • Alat kueri cuaca

    Alat ini menerima parameter arguments. Format arguments adalah {"location": "lokasi yang ditanyakan"}. Output alat adalah string dalam format: "{lokasi} hari ini {cuaca}".

    Untuk tujuan demonstrasi, alat kueri cuaca yang didefinisikan di sini tidak benar-benar menanyakan cuaca. Alat ini memilih secara acak dari cerah, berawan, atau hujan. Dalam skenario bisnis nyata, Anda dapat menggantinya dengan alat seperti Amap Weather.
  • Alat kueri waktu

    Alat kueri waktu tidak memerlukan parameter input apa pun. Output alat adalah string dalam format: "Waktu saat ini: {waktu yang ditanyakan}.".

    Jika Anda menggunakan Node.js, jalankan npm install date-fns untuk menginstal paket date-fns guna mendapatkan waktu.
## Langkah 1: Definisikan fungsi alat

# Tambahkan impor untuk modul random
import random
from datetime import datetime

# Simulasikan alat kueri cuaca. Contoh output: "Cuaca di Beijing hari ini hujan."
def get_current_weather(arguments):
    # Definisikan daftar kondisi cuaca alternatif
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    # Pilih kondisi cuaca secara acak
    random_weather = random.choice(weather_conditions)
    # Ekstrak informasi lokasi dari JSON
    location = arguments["location"]
    # Kembalikan informasi cuaca yang diformat
    return f"Cuaca di {location} hari ini {random_weather}."

# Alat untuk menanyakan waktu saat ini. Contoh output: "Waktu saat ini: 2024-04-15 17:15:18."
def get_current_time():
    # Dapatkan tanggal dan waktu saat ini
    current_datetime = datetime.now()
    # Format tanggal dan waktu saat ini
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    # Kembalikan waktu saat ini yang diformat
    return f"Waktu saat ini: {formatted_time}."

# Uji fungsi alat dan cetak hasilnya. Anda dapat menghapus empat baris kode uji berikut saat menjalankan langkah-langkah selanjutnya.
print("Menguji output alat:")
print(get_current_weather({"location": "Shanghai"}))
print(get_current_time())
print("\n")
// Langkah 1: Definisikan fungsi alat

// Impor alat kueri waktu
import { format } from 'date-fns';

function getCurrentWeather(args) {
    // Definisikan daftar kondisi cuaca alternatif
    const weatherConditions = ["Sunny", "Cloudy", "Rainy"];
    // Pilih kondisi cuaca secara acak
    const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
    // Ekstrak informasi lokasi dari JSON
    const location = args.location;
    // Kembalikan informasi cuaca yang diformat
    return `Cuaca di ${location} hari ini ${randomWeather}.`;
}

function getCurrentTime() {
    // Dapatkan tanggal dan waktu saat ini
    const currentDatetime = new Date();
    // Format tanggal dan waktu saat ini
    const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss');
    // Kembalikan waktu saat ini yang diformat
    return `Waktu saat ini: ${formattedTime}.`;
}

// Uji fungsi alat dan cetak hasilnya. Anda dapat menghapus empat baris kode uji berikut saat menjalankan langkah-langkah selanjutnya.
console.log("Menguji output alat:")
console.log(getCurrentWeather({location:"Shanghai"}));
console.log(getCurrentTime());
console.log("\n")

Setelah menjalankan alat, output berikut ditampilkan:

Menguji output alat:
Cuaca di Shanghai hari ini Cloudy.
Waktu saat ini: 2025-01-08 20:21:45.

1.2. Buat array alat

Sebelum manusia dapat memilih alat, mereka perlu memahami fungsinya, skenario penggunaannya, dan parameter inputnya. Hal yang sama berlaku untuk LLM. Model memilih alat yang sesuai berdasarkan informasi ini. Berikan informasi alat dalam format JSON berikut.

  • Field type diatur ke "function".

  • Field function adalah objek.

    • Field name adalah nama fungsi alat kustom. Kami merekomendasikan menggunakan nama yang sama dengan fungsi tersebut, seperti get_current_weather atau get_current_time.

    • Field description menjelaskan kemampuan fungsi alat. LLM merujuk ke field ini untuk memutuskan apakah akan menggunakan fungsi alat tersebut.

    • Field parameters menjelaskan parameter input fungsi alat. Ini adalah objek. LLM merujuk ke field ini untuk mengekstrak parameter input. Jika fungsi alat tidak memerlukan parameter input, Anda tidak perlu menentukan field parameters.

      • Field type diatur ke "object".

      • Field properties menjelaskan nama, tipe data, dan deskripsi parameter input. Ini adalah objek. Kunci adalah nama parameter input, dan nilai adalah tipe data dan deskripsi parameter input.

      • Field required menentukan parameter mana yang wajib. Ini adalah array.

Untuk alat kueri cuaca, format informasi deskripsi alat adalah sebagai berikut:

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                }
            },
            "required": ["location"]
        }
    }
}

Sebelum melakukan Pemanggilan Fungsi, definisikan array informasi alat (tools) dalam kode Anda. Array ini mencakup nama fungsi, deskripsi, dan definisi parameter untuk setiap alat. Array ini diteruskan sebagai parameter dalam permintaan selanjutnya.

# Tempelkan kode berikut setelah kode Langkah 1

## Langkah 2: Buat array alat

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
                    }
                },
                "required": ["location"]
            }
        }
    }
]
tool_name = [tool["function"]["name"] for tool in tools]
print(f"Membuat {len(tools)} alat: {tool_name}\n")
// Tempelkan kode berikut setelah kode Langkah 1

// Langkah 2: Buat array alat

const tools = [
    {
      type: "function",
      function: {
        name: "get_current_time",
        description: "Berguna ketika Anda ingin mengetahui waktu saat ini.",
        parameters: {}
      }
    },
    {
      type: "function",
      function: {
        name: "get_current_weather",
        description: "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
            }
          },
          required: ["location"]
        }
      }
    }
  ];
  
const toolNames = tools.map(tool => tool.function.name);
console.log(`Membuat ${tools.length} alat: ${toolNames.join(', ')}\n`);

2. Buat array messages

Pemanggilan Fungsi meneruskan instruksi dan konteks ke LLM melalui array messages. Sebelum melakukan panggilan, array messages harus berisi Pesan Sistem dan Pesan Pengguna.

Pesan Sistem

Meskipun fungsi dan skenario penggunaan alat telah dijelaskan saat Anda membuat array alat, menekankan kapan harus memanggil alat dalam Pesan Sistem biasanya meningkatkan akurasi pemanggilan alat. Untuk skenario saat ini, Anda dapat mengatur Prompt Sistem menjadi:

Anda adalah asisten yang membantu. Jika pengguna bertanya tentang cuaca, panggil fungsi 'get_current_weather';
jika pengguna bertanya tentang waktu, panggil fungsi 'get_current_time'.
Harap jawab pertanyaan dengan nada ramah.

Pesan Pengguna

Pesan Pengguna digunakan untuk meneruskan pertanyaan pengguna. Dengan asumsi pengguna bertanya "Cuaca di Shanghai", array messages pada titik ini adalah:

# Langkah 3: Buat array messages
# Tempelkan kode berikut setelah kode Langkah 2
# Contoh Pesan Pengguna untuk model generasi teks
messages = [
    {
        "role": "system",
        "content": """Anda adalah asisten yang membantu. Jika pengguna bertanya tentang cuaca, panggil fungsi 'get_current_weather';
     jika pengguna bertanya tentang waktu, panggil fungsi 'get_current_time'.
     Harap jawab pertanyaan dengan nada ramah.""",
    },
    {
        "role": "user",
        "content": "Cuaca di Shanghai"
    }
]

# Contoh Pesan Pengguna untuk model multimodal
# messages=[
#  {
#         "role": "system",
#         "content": """Anda adalah asisten yang membantu. Jika pengguna bertanya tentang cuaca, panggil fungsi 'get_current_weather';
#      jika pengguna bertanya tentang waktu, panggil fungsi 'get_current_time'.
#      Harap jawab pertanyaan dengan nada ramah.""",
#     },
#     {"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": "Tanyakan cuaca saat ini untuk lokasi dalam gambar"}]},
# ]

print("array messages dibuat\n") 
// Langkah 3: Buat array messages
// Tempelkan kode berikut setelah kode Langkah 2
const messages = [
    {
        role: "system",
        content: "Anda adalah asisten yang membantu. Jika pengguna bertanya tentang cuaca, panggil fungsi 'get_current_weather'; jika pengguna bertanya tentang waktu, panggil fungsi 'get_current_time'. Harap jawab pertanyaan dengan nada ramah.",
    },
    {
        role: "user",
        content: "Cuaca di Shanghai"
    }
];
// Contoh Pesan Pengguna untuk model multimodal,
// 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: "Apa yang digambarkan dalam gambar?"}]
//   }];

console.log("array messages dibuat\n");
Karena alat yang tersedia mencakup kueri cuaca dan waktu, Anda juga dapat menanyakan waktu saat ini.

3. Lakukan Pemanggilan Fungsi

Teruskan tools dan messages yang telah dibuat ke LLM untuk melakukan Pemanggilan Fungsi. LLM menentukan apakah akan memanggil alat. Jika iya, LLM mengembalikan nama fungsi alat dan parameternya.

Untuk model yang didukung, lihat Model yang didukung.
# Langkah 4: Lakukan pemanggilan fungsi
# Tempelkan kode berikut setelah kode Langkah 3
from openai import OpenAI
import os

client = OpenAI(
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    // Jika Anda menggunakan model di wilayah China (Beijing), ganti base_url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)

def function_calling():
    completion = client.chat.completions.create(
        // Contoh ini menggunakan qwen3.6-plus. Anda dapat mengubah nama model sesuai kebutuhan. Untuk daftar model, lihat https://www.alibabacloud.com/help/en/model-studio/getting-started/models
        model="qwen3.6-plus",
        extra_body={"enable_thinking": False},
        messages=messages,
        tools=tools
    )
    print("Objek yang dikembalikan:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("Melakukan pemanggilan fungsi...")
completion = function_calling()
// Langkah 4: Lakukan pemanggilan fungsi
// Tempelkan kode berikut setelah kode Langkah 3
import OpenAI from "openai";
const openai = new OpenAI(
    {
        // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // Jika Anda menggunakan model di wilayah China (Beijing), ganti baseURL dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
        baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
    }
);

async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen3.6-plus",  // Contoh ini menggunakan qwen3.6-plus. Anda dapat mengubah nama model sesuai kebutuhan. Untuk daftar model, lihat https://www.alibabacloud.com/help/en/model-studio/getting-started/models
        enable_thinking: false,
        messages: messages,
        tools: tools
    });
    console.log("Objek yang dikembalikan:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

Karena pengguna bertanya tentang cuaca di Shanghai, LLM menentukan nama fungsi alat yang akan digunakan sebagai "get_current_weather" dan parameter input fungsi sebagai "{\"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
        }
    ]
}

Perhatikan bahwa jika LLM menentukan bahwa tidak diperlukan alat untuk pertanyaan tersebut, LLM akan merespons langsung melalui parameter content. Saat Anda memasukkan "Hello", parameter tool_calls kosong, dan format objek yang dikembalikan adalah:

{
    "content": "Halo! Ada yang bisa saya bantu? Saya sangat pandai menjawab pertanyaan tentang cuaca atau waktu.",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}
Jika parameter tool_calls kosong, program Anda dapat langsung mengembalikan content tanpa menjalankan langkah-langkah berikutnya.
Jika Anda ingin LLM memilih alat tertentu setiap kali melakukan Pemanggilan Fungsi, lihat Pemanggilan alat paksa.

4. Jalankan fungsi alat

Menjalankan fungsi alat menerjemahkan keputusan model menjadi operasi nyata.

Proses menjalankan fungsi alat diselesaikan oleh lingkungan komputasi Anda, bukan oleh LLM.

LLM hanya mengeluarkan string. Sebelum menjalankan fungsi alat, Anda perlu mengurai nama fungsi alat dan parameter inputnya secara terpisah.

  • Fungsi alat

    Buat pemetaan function_mapper dari nama fungsi alat ke entitas fungsi alat untuk memetakan string fungsi alat yang dikembalikan ke entitas fungsi alat.

  • Parameter input

    Parameter input yang dikembalikan oleh Pemanggilan Fungsi adalah string JSON. Gunakan alat untuk menguraikannya menjadi objek JSON untuk mengekstrak informasi parameter input.

Setelah diurai, teruskan parameter ke fungsi alat dan jalankannya untuk mendapatkan hasil output.

# Langkah 5: Jalankan fungsi alat
# Tempel kode berikut setelah kode Langkah 4
import json

print("Running the tool function...")
# Dapatkan nama fungsi dan parameter input dari hasil yang dikembalikan
function_name = completion.choices[0].message.tool_calls[0].function.name
arguments_string = completion.choices[0].message.tool_calls[0].function.arguments

# Gunakan modul json untuk mengurai string parameter
arguments = json.loads(arguments_string)
# Buat tabel pemetaan fungsi
function_mapper = {
    "get_current_weather": get_current_weather,
    "get_current_time": get_current_time
}
# Dapatkan entitas fungsi
function = function_mapper[function_name]
# Jika parameter input kosong, panggil fungsi secara langsung
if arguments == {}:
    function_output = function()
# Jika tidak, teruskan parameter lalu panggil fungsi
else:
    function_output = function(arguments)
# Cetak output alat
print(f"Tool function output: {function_output}\n")
// Langkah 5: Jalankan fungsi alat
// Tempelkan kode berikut setelah kode Langkah 4

console.log("Menjalankan fungsi alat...");
const function_name = completion.choices[0].message.tool_calls[0].function.name;
const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments;

// Gunakan modul JSON untuk mengurai string parameter
const args = JSON.parse(arguments_string);

// Buat tabel pemetaan fungsi
const functionMapper = {
    "get_current_weather": getCurrentWeather,
    "get_current_time": getCurrentTime
};

// Dapatkan entitas fungsi
const func = functionMapper[function_name];

// Jika parameter input kosong, panggil fungsi langsung
let functionOutput;
if (Object.keys(args).length === 0) {
    functionOutput = func();
} else {
    // Jika tidak, teruskan parameter lalu panggil fungsi
    functionOutput = func(args);
}

// Cetak output alat
console.log(`Output fungsi alat: ${functionOutput}\n`);

Setelah menjalankan kode, output berikut ditampilkan:

Cuaca di Shanghai hari ini Cloudy.
Catatan

Dalam skenario bisnis nyata, banyak alat yang melakukan tindakan spesifik (seperti mengirim email atau mengunggah file) daripada menanyakan data, dan tidak mengeluarkan string. Kami merekomendasikan menambahkan informasi deskripsi status (seperti "Email berhasil dikirim" atau "Operasi gagal") untuk alat semacam itu agar membantu LLM memahami status eksekusi.

5. Biarkan LLM merangkum output fungsi alat

Format output fungsi alat relatif tetap. Mengembalikannya langsung kepada pengguna mungkin terdengar kaku. Kirimkan output alat ke konteks model dan panggil kembali model untuk menghasilkan respons bergaya bahasa alami.

  1. Tambahkan Pesan Asisten

    Setelah Anda melakukan Function Calling, Anda akan menerima Pesan Asisten melalui completion.choices[0].message. Tambahkan pesan tersebut ke array `messages`.

  2. Tambahkan Pesan Alat

    Tambahkan output alat ke array messages dalam format {"role": "tool", "content": "output alat", "tool_call_id": completion.choices[0].message.tool_calls[0].id}.

    Catatan
    • Pastikan output alat dalam format string.

    • tool_call_id adalah pengidentifikasi unik yang dihasilkan sistem untuk setiap permintaan pemanggilan alat. Model mungkin meminta untuk memanggil beberapa alat sekaligus. Saat mengembalikan beberapa hasil alat ke model, tool_call_id memastikan bahwa hasil output alat dapat dicocokkan dengan maksud pemanggilannya.

# Langkah 6: Kirimkan output alat ke LLM
# Tempelkan kode berikut setelah kode Langkah 5

messages.append(completion.choices[0].message)
print("Pesan asisten ditambahkan")
messages.append({"role": "tool", "content": function_output, "tool_call_id": completion.choices[0].message.tool_calls[0].id})
print("Pesan alat ditambahkan\n")
// Langkah 6: Kirimkan output alat ke LLM
// Tempelkan kode berikut setelah kode Langkah 5

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

Pada titik ini, array messages adalah:

[
  Pesan Sistem -- Memandu strategi pemanggilan alat model
  Pesan Pengguna -- Pertanyaan pengguna
  Pesan Asisten -- Informasi pemanggilan alat yang dikembalikan model
  Pesan Alat -- Informasi output alat (mungkin ada beberapa Pesan Alat jika digunakan pemanggilan alat paralel, seperti yang dijelaskan di bawah)
]

Setelah memperbarui array messages, jalankan kode berikut.

# Langkah 7: Biarkan LLM merangkum output alat
# Tempelkan kode berikut setelah kode Langkah 6
print("Merangkum output alat...")
completion = function_calling()
// Langkah 7: Biarkan LLM merangkum output alat
// Tempelkan kode berikut setelah kode Langkah 6

console.log("Merangkum output alat...");
const completion_1 = await functionCalling();

Anda dapat mengambil konten respons dari content: "Cuaca di Shanghai hari ini berawan. Jika Anda memiliki pertanyaan lain, jangan ragu untuk bertanya."

{
    "content": "Cuaca di Shanghai hari ini berawan. Jika Anda memiliki pertanyaan lain, jangan ragu untuk bertanya.",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": null
}

Anda telah menyelesaikan alur Pemanggilan Fungsi secara lengkap.

Penggunaan lanjutan

Tentukan metode pemanggilan alat

Pemanggilan alat paralel

Kueri cuaca untuk satu kota hanya memerlukan satu pemanggilan alat. Namun, jika pertanyaan memerlukan beberapa pemanggilan alat—misalnya, "Bagaimana cuaca di Beijing dan Shanghai?" atau "Bagaimana cuaca di Hangzhou dan jam berapa sekarang?"—maka setelah Anda melakukan Pemanggilan Fungsi, hanya satu informasi pemanggilan alat yang akan dikembalikan. Sebagai contoh, jika Anda menanyakan "Bagaimana cuaca di Beijing dan 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
        }
    ]
}

Hasil yang dikembalikan hanya berisi parameter input untuk Beijing. Untuk memastikan hasil mencakup semua fungsi alat beserta parameter input-nya, Anda dapat mengatur parameter permintaan parallel_tool_calls ke true saat melakukan Pemanggilan Fungsi.

Catatan

Pemanggilan alat paralel cocok untuk tugas-tugas yang tidak memiliki dependensi. Jika terdapat dependensi antar tugas (misalnya, input alat A bergantung pada output alat B), lihat Mulai untuk mengimplementasikan pemanggilan alat serial (memanggil satu alat dalam satu waktu) melalui loop while.

def function_calling():
    completion = client.chat.completions.create(
        model="qwen3.6-plus",  # Contoh ini menggunakan qwen3.6-plus. Anda dapat mengubah nama model sesuai kebutuhan.
        extra_body={"enable_thinking": False},
        messages=messages,
        tools=tools,
        # Parameter baru
        parallel_tool_calls=True
    )
    print("Objek yang dikembalikan:")
    print(completion.choices[0].message.model_dump_json())
    print("\n")
    return completion

print("Melakukan pemanggilan fungsi...")
completion = function_calling()
async function functionCalling() {
    const completion = await openai.chat.completions.create({
        model: "qwen3.6-plus",  // Contoh ini menggunakan qwen3.6-plus. Anda dapat mengubah nama model sesuai kebutuhan.
        enable_thinking: false,
        messages: messages,
        tools: tools,
        parallel_tool_calls: true
    });
    console.log("Objek yang dikembalikan:");
    console.log(JSON.stringify(completion.choices[0].message));
    console.log("\n");
    return completion;
}

const completion = await functionCalling();

Array tool_calls dalam objek yang dikembalikan berisi informasi parameter input untuk Beijing dan 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"
        }
    ]
}

Pemanggilan alat paksa

LLM menghasilkan konten dengan tingkat ketidakpastian tertentu dan mungkin memilih alat yang salah. Untuk memaksa penggunaan atau menonaktifkan alat tertentu pada jenis pertanyaan tertentu, Anda dapat memodifikasi parameter tool_choice. Nilai default parameter tool_choice adalah "auto", yang berarti LLM secara otonom memutuskan cara melakukan pemanggilan alat.

Saat LLM merangkum output fungsi alat, hapus parameter tool_choice. Jika tidak, API akan tetap mengembalikan informasi pemanggilan alat.
  • Paksa penggunaan alat tertentu

    Jika Anda ingin Pemanggilan Fungsi secara paksa memanggil alat tertentu untuk jenis pertanyaan tertentu, atur parameter tool_choice ke {"type": "function", "function": {"name": "the_function_to_call"}}. Dalam hal ini, LLM tidak akan berpartisipasi dalam pemilihan alat dan hanya akan menghasilkan informasi parameter input.

    Dengan asumsi skenario saat ini hanya melibatkan pertanyaan kueri cuaca, Anda dapat memodifikasi kode function_calling menjadi:

    def function_calling():
        completion = client.chat.completions.create(
            model="qwen3.6-plus",
            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.6-plus",
            enable_thinking: false,
            messages: messages,
            tools: tools,
            tool_choice: {"type": "function", "function": {"name": "get_current_weather"}}
        });
        console.log("Objek yang dikembalikan:");
        console.log(JSON.stringify(response.choices[0].message));
        console.log("\n");
        return response;
    }
    
    const response = await functionCalling();

    Tidak peduli pertanyaan apa yang dimasukkan, fungsi alat dalam objek yang dikembalikan akan selalu menjadi get_current_weather.

    Sebelum menggunakan strategi ini, pastikan pertanyaan relevan dengan alat yang dipilih. Jika tidak, hasil yang tidak terduga mungkin terjadi.
  • Paksa penggunaan minimal satu alat

    Untuk beberapa pertanyaan yang memerlukan alat, LLM mungkin memutuskan bahwa pemanggilan tidak diperlukan. Untuk memastikan Pemanggilan Fungsi selalu melakukan pemanggilan alat (sehingga parameter tool_calls dalam objek yang dikembalikan tidak kosong), atur parameter tool_choice ke "required". Dengan demikian, Pemanggilan Fungsi akan selalu mengembalikan informasi alat dan parameter input.

    Dengan asumsi semua pertanyaan dalam skenario saat ini memerlukan pemanggilan alat, Anda dapat memodifikasi kode function_calling menjadi:

    def function_calling():
        completion = client.chat.completions.create(
            model="qwen3.6-plus",
            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.6-plus",
            enable_thinking: false,
            messages: messages,
            tools: tools,
            tool_choice: "required"
        });
        console.log("Objek yang dikembalikan:");
        console.log(JSON.stringify(completion.choices[0].message));
        console.log("\n");
        return completion;
    }
    
    const completion = await functionCalling();

    Tidak peduli pertanyaan apa yang dimasukkan, parameter tool_calls dalam objek yang dikembalikan tidak akan pernah kosong.

    Sebelum menggunakan strategi ini, pastikan pertanyaan relevan dengan alat. Jika tidak, hasil yang tidak terduga mungkin terjadi.
  • Force No Tool Usage

    Jika Anda ingin Pemanggilan Fungsi tidak pernah melakukan pemanggilan alat (objek yang dikembalikan berisi konten respons dalam content dan parameter tool_calls kosong), atur parameter tool_choice ke "none", atau jangan sertakan parameter tools. Parameter tool_calls yang dikembalikan oleh Pemanggilan Fungsi akan selalu kosong.

    Dengan asumsi tidak ada pertanyaan dalam skenario saat ini yang memerlukan pemanggilan alat, Anda dapat memodifikasi kode function_calling menjadi:

    def function_calling():
        completion = client.chat.completions.create(
            model="qwen3.6-plus",
            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.6-plus",
            enable_thinking: false,
            messages: messages,
            tools: tools,
            tool_choice: "none"
        });
        console.log("Objek yang dikembalikan:");
        console.log(JSON.stringify(completion.choices[0].message));
        console.log("\n");
        return completion;
    }
    
    const completion = await functionCalling();

Percakapan multi-putaran

Pengguna mungkin bertanya "Cuaca di Beijing" pada putaran pertama, lalu "Bagaimana dengan Shanghai?" pada putaran kedua. Jika konteks model tidak menyertakan informasi dari putaran pertama, model tidak dapat menentukan alat mana yang harus dipanggil. Dalam skenario percakapan multi-putaran, pertahankan array messages lengkap setelah setiap putaran. Tambahkan Pesan Pengguna baru ke array tersebut, lalu lakukan Pemanggilan Fungsi dan langkah-langkah selanjutnya. Struktur messages adalah sebagai berikut:

[
  Pesan Sistem -- Memandu strategi pemanggilan alat model
  Pesan Pengguna -- Pertanyaan pengguna
  Pesan Asisten -- Informasi pemanggilan alat yang dikembalikan model
  Pesan Alat -- Informasi output alat
  Pesan Asisten -- Ringkasan model atas informasi pemanggilan alat
  Pesan Pengguna -- Pertanyaan pengguna putaran kedua
]

Output streaming

Menggunakan output streaming memungkinkan Anda memperoleh informasi nama fungsi alat dan parameter input secara real-time, sehingga meningkatkan pengalaman pengguna. Dalam hal ini:

  • Informasi parameter untuk pemanggilan alat dikembalikan dalam potongan sebagai aliran data.

  • Nama fungsi alat dikembalikan dalam potongan data pertama dari respons streaming.

from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Jika Anda menggunakan model di wilayah China (Beijing), ganti dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna saat Anda ingin membuat kueri cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Sebuah kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

stream = client.chat.completions.create(
    model="qwen3.6-plus",
    extra_body={"enable_thinking": False},
    messages=[{"role": "user", "content": "Cuaca di 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(
    {
        // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // Jika Anda menggunakan model di wilayah China (Beijing), ganti baseURL dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
        baseURL: "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen3.6-plus",
    enable_thinking: false,
    messages: [{ role: "user", content: "Cuaca di Beijing" }],
    tools: tools,
    stream: true,
});

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

Setelah menjalankan kode, output berikut ditampilkan:

[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

Jalankan kode berikut untuk merakit informasi parameter input (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]));

Output berikut ditampilkan:

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

Pada langkah di mana LLM merangkum output fungsi alat, Pesan Asisten yang ditambahkan harus memenuhi format berikut. Cukup ganti elemen dalam tool_calls di bawah dengan konten di atas.

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

Pemanggilan alat dengan API Responses

Contoh sebelumnya didasarkan pada API Chat Completions OpenAI dan API DashScope. Jika Anda menggunakan API Responses OpenAI, proses keseluruhan tetap sama, tetapi format API memiliki perbedaan berikut:

Dimensi

Chat Completions

API Responses

Format definisi alat

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

Output pemanggilan alat

response.choices[0].message.tool_calls

Item dalam response.output di mana type adalah function_call

Pengembalian hasil alat

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

Respons akhir

response.choices[0].message.content

response.output_text

from openai import OpenAI
import json
import os
import random

// Inisialisasi klien
client = OpenAI(
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key="sk-xxx",
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat 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",
)
// Simulasikan pertanyaan pengguna
USER_QUESTION = "Bagaimana cuaca di Singapura?"
// Definisikan daftar alat
tools = [
    {
        "type": "function",
        "name": "get_current_weather",
        "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "Kota atau distrik, seperti Singapura atau London.",
                }
            },
            "required": ["location"],
        },
    }
]

// Simulasikan alat kueri cuaca
def get_current_weather(arguments):
    weather_conditions = ["Sunny", "Cloudy", "Rainy"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"Cuaca di {location} hari ini {random_weather}."

// Enkapsulasi fungsi respons model
def get_response(input_data):
    response = client.responses.create(
        model="qwen3.6-plus",
        extra_body={"enable_thinking": False},
        input=input_data,
        tools=tools,
    )
    return response

// Pertahankan konteks percakapan
conversation = [{"role": "user", "content": USER_QUESTION}]

response = get_response(conversation)
function_calls = [item for item in response.output if item.type == "function_call"]
// Jika tidak diperlukan pemanggilan alat, cetak konten secara langsung
if not function_calls:
    print(f"Respons akhir Asisten: {response.output_text}")
else:
    // Masuk ke loop pemanggilan alat
    while function_calls:
        for fc in function_calls:
            func_name = fc.name
            arguments = json.loads(fc.arguments)
            print(f"Memanggil alat [{func_name}], argumen: {arguments}")
            // Jalankan alat
            tool_result = get_current_weather(arguments)
            print(f"Alat mengembalikan: {tool_result}")
            // Tambahkan pemanggilan alat dan hasilnya sebagai pasangan ke konteks
            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,
                }
            )
        // Panggil kembali model dengan konteks lengkap
        response = get_response(conversation)
        function_calls = [
            item for item in response.output if item.type == "function_call"
        ]
    print(f"Respons akhir Asisten: {response.output_text}")
import OpenAI from "openai";

// Inisialisasi klien
const openai = new OpenAI({
  // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
  // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey: "sk-xxx",
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL:
    "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
});

// Definisikan daftar alat
const tools = [
  {
    type: "function",
    name: "get_current_weather",
    description: "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
    parameters: {
      type: "object",
      properties: {
        location: {
          type: "string",
          description: "Kota atau distrik, seperti Singapura atau London.",
        },
      },
      required: ["location"],
    },
  },
];

// Simulasikan alat kueri cuaca
const getCurrentWeather = (args) => {
  const weatherConditions = ["Sunny", "Cloudy", "Rainy"];
  const randomWeather =
    weatherConditions[Math.floor(Math.random() * weatherConditions.length)];
  const location = args.location;
  return `Cuaca di ${location} hari ini ${randomWeather}.`;
};

// Enkapsulasi fungsi respons model
const getResponse = async (inputData) => {
  const response = await openai.responses.create({
    model: "qwen3.6-plus",
    enable_thinking: false,
    input: inputData,
    tools: tools,
  });
  return response;
};

const main = async () => {
  const userQuestion = "Cuaca di Singapura";

  // Pertahankan konteks percakapan
  const conversation = [{ role: "user", content: userQuestion }];

  let response = await getResponse(conversation);
  let functionCalls = response.output.filter(
    (item) => item.type === "function_call"
  );
  // Jika tidak diperlukan pemanggilan alat, cetak konten secara langsung
  if (functionCalls.length === 0) {
    console.log(`Respons akhir Asisten: ${response.output_text}`);
  } else {
    // Masuk ke loop pemanggilan alat
    while (functionCalls.length > 0) {
      for (const fc of functionCalls) {
        const funcName = fc.name;
        const args = JSON.parse(fc.arguments);
        console.log(`Memanggil alat [${funcName}], argumen:`, args);
        // Jalankan alat
        const toolResult = getCurrentWeather(args);
        console.log(`Alat mengembalikan: ${toolResult}`);
        // Tambahkan pemanggilan alat dan hasilnya sebagai pasangan ke konteks
        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,
        });
      }
      // Panggil kembali model dengan konteks lengkap
      response = await getResponse(conversation);
      functionCalls = response.output.filter(
        (item) => item.type === "function_call"
      );
    }
    console.log(`Respons akhir Asisten: ${response.output_text}`);
  }
};

// Mulai program
main().catch(console.error);

Pemanggilan alat untuk model omni-modal

Model omni-modal mendukung pemanggilan alat. Metode pemanggilan untuk seri Qwen-Omni dan seri Qwen-Omni-Realtime berbeda.

Seri Qwen-Omni

Seri Qwen3.5-Omni-Plus, Qwen3.5-Omni-Flash, dan Qwen3-Omni-Flash mendukung pemanggilan alat melalui API kompatibel OpenAI. Tahap mendapatkan informasi alat berbeda dari model lain dalam hal-hal berikut:

  • Output streaming wajib: Qwen-Omni hanya mendukung output streaming. Saat mendapatkan informasi alat, Anda juga harus mengatur stream=True.

  • Output teks saja direkomendasikan: Model hanya memerlukan informasi teks saat mendapatkan informasi alat (nama dan parameter fungsi). Untuk menghindari pembuatan audio yang tidak perlu, kami merekomendasikan mengatur modalities=["text"]. Jika output mencakup modalitas teks dan audio, Anda perlu melewati potongan data audio saat mendapatkan informasi alat.

Untuk informasi lebih lanjut tentang Qwen-Omni, lihat Non-real-time (Qwen-Omni).
from openai import OpenAI
import os

client = OpenAI(
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    // Jika Anda menggunakan model di wilayah China (Beijing), gantilah dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

completion = client.chat.completions.create(
    model="qwen3.5-omni-plus",
    messages=[{"role": "user", "content": "Cuaca di Hangzhou?"}],

    // Atur modalitas data output. Nilai valid: ["text"], ["text","audio"]. Kami merekomendasikan mengaturnya ke ["text"].
    modalities=["text"],

    // stream harus diatur ke True, jika tidak terjadi error.
    stream=True,
    tools=tools
)

for chunk in completion:
    // Jika output mencakup modalitas audio, ubah kondisi berikut menjadi: 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(
    {
        // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey: "sk-xxx",
        apiKey: process.env.DASHSCOPE_API_KEY,
        // Jika Anda menggunakan model di wilayah China (Beijing), ganti baseURL dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
        baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
    }
);
const tools = [
    {
        "type": "function",
        "function": {
            "name": "getCurrentWeather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

const stream = await openai.chat.completions.create({
    model: "qwen3-omni-flash",
    messages: [
        {
            "role": "user",
            "content": "Cuaca di Hangzhou"
        }],
    stream: true,
    // Atur modalitas data output. Nilai valid: ["text"], ["text","audio"]. Kami merekomendasikan mengaturnya ke ["text"].
    modalities: ["text"],
    tools:tools
});

for await (const chunk of stream) {
    // Jika output mencakup audio, ganti pernyataan kondisional dengan: 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);
}}

Setelah menjalankan kode, output berikut ditampilkan:

[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

Untuk kode merakit informasi parameter input (arguments), lihat Output streaming.

Seri Qwen-Omni-Realtime

Seri Qwen3.5-Omni-Plus-Realtime dan Qwen3.5-Omni-Flash-Realtime mendukung pemanggilan alat dan cocok untuk skenario percakapan suara. Anda dapat memanggilnya melalui SDK DashScope atau protokol WebSocket asli.

Alur kerja:

Setelah membuat koneksi WebSocket, teruskan definisi alat melalui session.update untuk memasuki alur interaksi berikut:

Fase 1: Input suara dan pemanggilan alat

  1. Pengguna mengajukan pertanyaan dengan suara. Klien mengumpulkan audio dan mengirimkannya ke server (sesuai dengan metode append_audio()). Setelah VAD server mendeteksi akhir ucapan, server melakukan inferensi model dan menentukan bahwa alat perlu dipanggil.

  2. Server mengembalikan informasi pemanggilan alat ke klien (sesuai dengan event response.function_call_arguments.done), termasuk nama fungsi (name), parameter input fungsi (arguments), dan pengidentifikasi panggilan (call_id). Contohnya sebagai berikut:

    {
        "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\"}"
    }
  3. Klien menjalankan fungsi alat yang sesuai secara lokal berdasarkan nama fungsi dan parameter input untuk mendapatkan hasil eksekusi.

Fase 2: Klien mengirim kembali hasil alat dan memicu respons akhir

  1. Klien mengirim kembali hasil eksekusi alat ke server (sesuai dengan event conversation.item.create), termasuk pengidentifikasi panggilan (call_id) dan hasil eksekusi (output). Contohnya sebagai berikut:

    {
        "type": "conversation.item.create",
        "item": {
            "type": "function_call_output",
            "call_id": "call_2be200f4cafe419b9530dd",
            "output": "Cuaca di Hangzhou hari ini cerah, dengan suhu 25°C dan angin sepoi-sepoi."
        }
    }
  2. Klien kemudian mengirim event response.create untuk memicu server menghasilkan jawaban suara akhir berdasarkan hasil eksekusi alat.

  3. Klien menerima suara dan teks yang dikembalikan server (sesuai dengan event response.audio.delta dan response.audio_transcript.delta) dan memutar respons suara ke pengguna.

Seri Qwen-Omni-Realtime tidak mendukung parameter tool_choice dan parallel_tool_calls.
Untuk informasi lebih lanjut tentang Qwen-Omni-Realtime, lihat Real-time (Qwen-Omni-Realtime), Event klien, dan Event sisi server.

SDK Python DashScope

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 *

# ==================== Definisi Konstanta ====================
VOICE = 'Tina'
MODEL = "qwen3.5-omni-plus-realtime"
// Untuk mengakses wilayah Beijing, ganti WS_URL dengan: 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"
// Konfigurasikan Kunci API. Jika Anda belum mengatur variabel lingkungan, ganti baris berikut dengan Kunci API Anda: 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

# ==================== Definisi Alat ====================
def get_train_price(src: str, dst: str) -> str:
    """Tanyakan harga tiket kereta"""
    return f"Harga tiket kereta dari {src} ke {dst} adalah 100-200 CNY."

def get_flight_price(src: str, dst: str) -> str:
    """Tanyakan harga tiket penerbangan"""
    return f"Harga tiket penerbangan dari {src} ke {dst} adalah 200-300 USD."

def get_current_weather(location: str) -> str:
    """Tanyakan cuaca di kota tertentu"""
    return f"Cuaca di {location} hari ini berubah dari berkabut menjadi cerah, dengan suhu 4/-4°C dan angin sepoi-sepoi."

// Definisi alat format OpenAI terpadu
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
                    }
                },
                "required": ["location"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_flight_price",
            "description": "Berguna ketika Anda ingin menanyakan harga tiket penerbangan.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "Kota keberangkatan penerbangan, seperti Beijing atau Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "Kota kedatangan penerbangan, seperti Beijing atau Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_train_price",
            "description": "Berguna ketika Anda ingin menanyakan harga tiket kereta.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "Kota keberangkatan kereta, seperti Beijing atau Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "Kota kedatangan kereta, seperti Beijing atau Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
]

// Pemetaan nama alat ke fungsi
TOOL_FUNCTIONS = {
    "get_current_weather": get_current_weather,
    "get_flight_price": get_flight_price,
    "get_train_price": get_train_price,
}

# ==================== Penanganan Pemanggilan Alat ====================
def handle_tool_call(tool_call_response: Dict[str, Any]) -> Dict[str, Any]:
    """
    Menangani permintaan pemanggilan alat

    Args:
        tool_call_response: Informasi pemanggilan alat yang berisi nama, argumen, dan call_id

    Returns:
        Respons pemanggilan alat yang diperbarui yang berisi field output
    """
    try:
        function_name = tool_call_response['name']
        tool_call_arguments = json.loads(tool_call_response['arguments'])

        print(f'[Pemanggilan Alat] Mulai memproses: name={function_name}, args={tool_call_arguments}')

        // Temukan fungsi yang sesuai
        if function_name not in TOOL_FUNCTIONS:
            tool_call_response['output'] = f"Klien tidak menemukan alat: {function_name}"
            print(f'[Pemanggilan Alat] Error: Alat tidak ditemukan {function_name}')
            return tool_call_response

        // Panggil fungsi
        func = TOOL_FUNCTIONS[function_name]
        result = func(**tool_call_arguments)
        tool_call_response['output'] = result

        print(f'[Pemanggilan Alat] Selesai: {result}')
        return tool_call_response

    except Exception as e:
        error_msg = f"Pemanggilan alat gagal: {str(e)}"
        tool_call_response['output'] = error_msg
        print(f'[Pemanggilan Alat] Pengecualian: {error_msg}')
        traceback.print_exc()
        return tool_call_response

def send_tool_call_response(conversation: OmniRealtimeConversation, response: Dict[str, Any]) -> None:
    """Mengirim hasil pemanggilan alat ke server"""
    conversation.create_item({
        "id": 'item_' + uuid.uuid4().hex,
        "type": "function_call_output",
        "call_id": response['call_id'],
        "output": response["output"],
    })

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

    Menggunakan arsitektur dual-thread untuk pemutaran audio real-time:
    - Thread decoding: Mendekode data audio yang dikodekan base64 menjadi data PCM mentah
    - Thread pemutaran: Menulis data PCM ke perangkat output audio

    Mendukung penambahan data audio secara dinamis, pembatalan pemutaran, penyimpanan file audio, dll.
    """

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

        Args:
            pya: Instance pyaudio.PyAudio
            sample_rate: Laju sampling audio (Hz), default 24000
            chunk_size_ms: Ukuran potongan audio (milidetik), memengaruhi latensi pembatalan pemutaran, default 100ms
            save_file: Apakah akan menyimpan audio yang diputar ke 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):
        """Thread decoding: Mendekode data audio base64 menjadi data PCM mentah"""
        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):
        """Thread pemutaran: Menulis data PCM ke perangkat output audio"""
        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):
        """Batalkan pemutaran: Kosongkan semua antrian buffer"""
        self.b64_audio_buffer.queue.clear()
        self.raw_audio_buffer.queue.clear()

    def add_data(self, data):
        """Tambahkan data audio yang dikodekan base64 ke antrian pemutaran"""
        self.b64_audio_buffer.put(data)

    def wait_for_complete(self):
        """Tunggu hingga pemutaran selesai"""
        self.complete_event = threading.Event()
        self.complete_event.wait()
        self.complete_event = None

    def shutdown(self):
        """Matikan pemutar dan lepaskan sumber daya"""
        self.status = 'stop'
        self.decoder_thread.join()
        self.player_thread.join()
        self.player_stream.close()
        if self.save_file:
            self.out_file.close()

# ==================== Manajer Audio ====================
class AudioManager:
    """Mengelola sumber daya input dan output audio"""

    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:
        """Inisialisasi perangkat audio"""
        print('Menginisialisasi perangkat audio...')
        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('Perangkat audio diinisialisasi')

    def read_audio_chunk(self) -> Optional[bytes]:
        """Baca potongan data audio"""
        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] Gagal membaca data audio: {e}')
            return None

    def cleanup(self) -> None:
        """Bersihkan sumber daya audio"""
        print('Membersihkan sumber daya audio...')
        if self.player:
            self.player.shutdown()
        if self.mic_stream:
            self.mic_stream.close()
        if self.pya:
            self.pya.terminate()
        print('Sumber daya audio dibersihkan')

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

    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:
        """Atur referensi instance percakapan"""
        self.conversation = conversation

    def on_open(self) -> None:
        """Callback saat koneksi dibuat"""
        print('Koneksi dibuat')
        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 saat koneksi ditutup"""
        print(f'Koneksi ditutup: code={close_status_code}, msg={close_msg}')
        self.audio_manager.cleanup()
        sys.exit(0)

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

            // Sesi dibuat
            if event_type == 'session.created':
                print(f'Sesi dimulai: {response["session"]["id"]}')

            // Transkripsi ucapan ke teks selesai
            elif event_type == 'conversation.item.input_audio_transcription.completed':
                print(f'Pertanyaan pengguna: {response.get("transcript", "")}')

            // Respons teks inkremental
            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'Waktu ke token pertama (akhir VAD): {latency:.0f} ms')

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

            // Respons audio inkremental
            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'Waktu ke audio pertama (akhir VAD): {latency:.0f} ms')

                audio_interval = time.time() * 1000 - self.last_package_time
                print(f'Interval audio: {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 mendeteksi awal ucapan
            elif event_type == 'input_audio_buffer.speech_started':
                print('====== VAD mendeteksi awal ucapan ======')
                if self.audio_manager.player:
                    self.audio_manager.player.cancel_playing()

            // VAD mendeteksi akhir ucapan
            elif event_type == 'input_audio_buffer.speech_stopped':
                print('====== VAD mendeteksi akhir ucapan ======')
                self.last_package_time = time.time() * 1000
                self.is_first_text = True
                self.is_first_audio = True
                self.tool_calls = {}

            // Argumen pemanggilan fungsi selesai
            elif event_type == 'response.function_call_arguments.done':
                print('====== Menerima permintaan pemanggilan alat ======')
                call_id = response.get('call_id', '')
                self.tool_calls[call_id] = response.copy()
                self.tool_calls[call_id]['processed'] = False

            // Respons selesai
            elif event_type == 'response.done':
                print('====== Respons selesai ======')
                print(f'Respons lengkap: {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()

                    // Cetak metrik detail hanya saat semuanya tersedia
                    if response_id is not None and text_delay is not None and audio_delay is not None:
                        print(f'[Metric] ID Respons: {response_id}, '
                              f'Waktu ke token pertama: {text_delay:.0f}ms, '
                              f'Waktu ke audio pertama: {audio_delay:.0f}ms')
                    else:
                        print('[Metric] Informasi metrik sementara tidak tersedia (kemungkinan respons setelah pemanggilan alat)')

                self.all_response_text = ''

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

    def process_pending_tool_calls(self) -> bool:
        """
        Memproses pemanggilan alat yang tertunda

        Returns:
            Apakah ada pemanggilan alat baru yang memerlukan respons
        """
        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

                // Tangani pemanggilan alat
                result = handle_tool_call(tool_call)

                // Kirim hasil ke server
                if self.conversation:
                    send_tool_call_response(self.conversation, result)

        return has_pending

# ==================== Program Utama ====================
def main():
    """Fungsi utama"""
    print('Menginisialisasi percakapan real-time Omni...')

    // Buat manajer audio
    audio_manager = AudioManager()

    // Buat handler callback
    callback = OmniCallback(audio_manager)

    // Buat instance percakapan
    conversation = OmniRealtimeConversation(
        api_key=dashscope.api_key,
        url=WS_URL,
        model=MODEL,
        callback=callback,
    )

    // Atur referensi percakapan di callback
    callback.set_conversation(conversation)

    // Buat koneksi
    conversation.connect()

    // Konfigurasikan parameter sesi
    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,
    )

    // Atur penanganan sinyal
    def signal_handler(sig, frame):
        print('\nMenerima Ctrl+C, menghentikan...')
        conversation.close()
        audio_manager.cleanup()
        print('Percakapan real-time Omni dihentikan')
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    print("Tekan Ctrl+C untuk menghentikan percakapan...\n")

    // Loop utama: terus-menerus mengirim audio dan memeriksa pemanggilan alat
    try:
        while True:
            // Proses pemanggilan alat yang tertunda
            has_tool_calls = callback.process_pending_tool_calls()

            if has_tool_calls:
                print("*** Pemanggilan alat selesai, membuat respons baru ***")
                conversation.create_response(
                    instructions=None,
                    output_modalities=omni_output_modalities
                )
                print('====== Pemrosesan pemanggilan alat selesai ======\n')

            // Baca dan kirim data audio
            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] Pengecualian loop utama: {e}')
        traceback.print_exc()
    finally:
        conversation.close()
        audio_manager.cleanup()

if __name__ == '__main__':
    main()

SDK Java DashScope

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 {
            // Inisialisasi komponen
            AudioPlayer audioPlayer = new AudioPlayer();
            ToolRegistry toolRegistry = new ToolRegistry();
            ConversationHandler handler = new ConversationHandler(audioPlayer, toolRegistry);

            // Buat dan konfigurasikan sesi
            OmniRealtimeParam param = OmniRealtimeParam.builder()
                    .model("qwen3.5-omni-plus-realtime")
                    .apikey(System.getenv("DASHSCOPE_API_KEY"))
                    // Untuk mengakses wilayah Beijing, ganti url dengan: 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();

            // Konfigurasikan parameter sesi
            configureSession(conversation, toolRegistry);

            // Mulai pengambilan audio
            startAudioCapture(conversation, handler);

            // Bersihkan sumber daya
            cleanup(conversation, audioPlayer);

        } catch (NoApiKeyException e) {
            System.err.println("KUNCI API tidak ditemukan: Harap atur variabel lingkungan DASHSCOPE_API_KEY");
        } 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("Pemanggilan alat diaktifkan. Silakan mulai berbicara (Tekan Ctrl+C untuk keluar)...");
    }

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

                // Periksa dan proses pemanggilan alat yang tertunda
                if (handler.hasPendingToolCalls()) {
                    System.out.println("*** buat respons setelah memanggil alat");
                    handler.processPendingToolCalls(conversation);
                    conversation.createResponse(null, Arrays.asList(OmniRealtimeModality.AUDIO, OmniRealtimeModality.TEXT));
                    System.out.println("======PEMANGGILAN ALAT SELESAI======");
                }
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException ignored) {}
        }

        mic.close();
    }

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

    /**
     * Pemutar Audio - Bertanggung jawab atas pemutaran berurutan data audio
     */
    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();
        }
    }

    /**
     * Registry Alat - Mengelola alat yang tersedia dan implementasinya
     */
    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);
        }

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

            definitions.add(createFunctionDefinition(
                    "get_current_weather",
                    "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
                    createParamsSchema(
                            Collections.singletonMap("location",
                                    createProperty("string", "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.")),
                            Collections.singletonList("location")
                    )
            ));

            Map<String, Object> flightProps = new HashMap<>();
            flightProps.put("src", createProperty("string", "Kota keberangkatan penerbangan, seperti Beijing atau Hangzhou."));
            flightProps.put("dst", createProperty("string", "Kota kedatangan penerbangan, seperti Beijing atau Hangzhou."));
            definitions.add(createFunctionDefinition(
                    "get_flight_price",
                    "Berguna ketika Anda ingin menanyakan harga tiket penerbangan.",
                    createParamsSchema(flightProps, Arrays.asList("src", "dst"))
            ));

            Map<String, Object> trainProps = new HashMap<>();
            trainProps.put("src", createProperty("string", "Kota keberangkatan kereta, seperti Beijing atau Hangzhou."));
            trainProps.put("dst", createProperty("string", "Kota kedatangan kereta, seperti Beijing atau Hangzhou."));
            definitions.add(createFunctionDefinition(
                    "get_train_price",
                    "Berguna ketika Anda ingin menanyakan harga tiket kereta.",
                    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;
        }

        /**
         * Tambahkan pemanggilan alat ke antrian tertunda
         */
        public void addPendingToolCall(String callId, JsonObject toolCall) {
            pendingToolCalls.put(callId, toolCall);
        }

        /**
         * Periksa apakah ada pemanggilan alat yang tertunda
         */
        public boolean hasPendingToolCalls() {
            return !pendingToolCalls.isEmpty();
        }

        /**
         * Proses semua pemanggilan alat yang tertunda
         */
        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("[Pemanggilan Alat] mulai menangani: " + functionName + ", args: " + arguments);

            Function<JsonObject, String> handler = tools.get(functionName);
            if (handler == null) {
                return "Klien tidak menemukan alat ini. Pemanggilan gagal.";
            }

            String result = handler.apply(arguments);
            System.out.println("[Pemanggilan Alat] respons: " + 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);
        }

        // ===== Implementasi Alat =====

        private String getCurrentWeather(JsonObject args) {
            String location = args.get("location").getAsString();
            return "Cuaca di " + location + " hari ini berubah dari berkabut menjadi cerah, dengan suhu 4/-4°C dan angin sepoi-sepoi.";
        }

        private String getFlightPrice(JsonObject args) {
            String src = args.get("src").getAsString();
            String dst = args.get("dst").getAsString();
            return "Harga tiket penerbangan dari " + src + " ke " + dst + " adalah 200-300 USD.";
        }

        private String getTrainPrice(JsonObject args) {
            String src = args.get("src").getAsString();
            String dst = args.get("dst").getAsString();
            return "kesalahan kunci api tidak valid";
        }
    }

    /**
     * Handler Percakapan - Menangani event WebSocket
     */
    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("Koneksi dibuat");
        }

        @Override
        public void onClose(int code, String reason) {
            System.out.println("Koneksi ditutup");
            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("mulai sesi: " + sessionId);
        }

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

        private void handleTextDelta(JsonObject message) {
            if (isFirstText) {
                isFirstText = false;
                System.out.println("latensi teks pertama dari akhir vad: " +
                        (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("latensi audio pertama dari akhir vad: " +
                        (System.currentTimeMillis() - lastPackageTime) + " ms");
            }
            System.out.println("interval audio: " + (System.currentTimeMillis() - lastPackageTime) + " ms");
            lastPackageTime = System.currentTimeMillis();
            audioPlayer.play(message.get("delta").getAsString());
        }

        private void handleSpeechStarted() {
            System.out.println("======VAD Awal Ucapan======");
        }

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

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

        private void handleResponseDone() {
            System.out.println("======RESPONS SELESAI======");
            System.out.println("semua teks respons: " + responseTextRef.get());
            responseTextRef.set(new StringBuilder());
        }

        /**
         * Periksa apakah ada pemanggilan alat yang tertunda
         */
        public boolean hasPendingToolCalls() {
            return toolRegistry.hasPendingToolCalls();
        }

        /**
         * Proses semua pemanggilan alat yang tertunda
         */
        public void processPendingToolCalls(OmniRealtimeConversation conversation) {
            toolRegistry.processPendingToolCalls(conversation);
        }
    }
}

WebSocket(Python)

import asyncio
import json
import base64
import os
import pyaudio
import websockets

# ==================== Definisi Konstanta ====================
API_KEY = os.getenv("DASHSCOPE_API_KEY")
// Untuk mengakses wilayah Beijing, ganti dengan:
// 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"

# ==================== Definisi Alat ====================
def get_current_weather(location):
    """Tanyakan cuaca di kota tertentu"""
    return f"Cuaca di {location} hari ini berubah dari berkabut menjadi cerah, dengan suhu 4/-4°C dan angin sepoi-sepoi."

def get_flight_price(src, dst):
    """Tanyakan harga tiket penerbangan"""
    return f"Harga tiket penerbangan dari {src} ke {dst} adalah 200-300 USD."

def get_train_price(src, dst):
    """Tanyakan harga tiket kereta"""
    return f"Harga tiket kereta dari {src} ke {dst} adalah 100-200 CNY."

// Pemetaan nama alat ke fungsi
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": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.",
                    }
                },
                "required": ["location"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_flight_price",
            "description": "Berguna ketika Anda ingin menanyakan harga tiket penerbangan.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "Kota keberangkatan penerbangan, seperti Beijing atau Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "Kota kedatangan penerbangan, seperti Beijing atau Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_train_price",
            "description": "Berguna ketika Anda ingin menanyakan harga tiket kereta.",
            "parameters": {
                "type": "object",
                "properties": {
                    "src": {
                        "type": "string",
                        "description": "Kota keberangkatan kereta, seperti Beijing atau Hangzhou.",
                    },
                    "dst": {
                        "type": "string",
                        "description": "Kota kedatangan kereta, seperti Beijing atau Hangzhou.",
                    },
                },
                "required": ["src", "dst"],
            },
        },
    },
]

# ==================== Penanganan Pemanggilan Alat ====================
def handle_tool_call(name, arguments_str):
    """
    Menangani permintaan pemanggilan alat

    Args:
        name: Nama fungsi alat
        arguments_str: String parameter input dalam format JSON

    Returns:
        String hasil eksekusi alat
    """
    try:
        arguments = json.loads(arguments_str)
        print(f'[Pemanggilan Alat] Mulai memproses: name={name}, args={arguments}')

        func = TOOL_FUNCTIONS.get(name)
        if func is None:
            result = f"Klien tidak menemukan alat: {name}"
            print(f'[Pemanggilan Alat] Error: {result}')
            return result

        result = func(**arguments)
        print(f'[Pemanggilan Alat] Selesai: {result}')
        return result

    except Exception as e:
        error_msg = f"Pemanggilan alat gagal: {str(e)}"
        print(f'[Pemanggilan Alat] Pengecualian: {error_msg}')
        return error_msg

# ==================== Program Utama ====================
async def main():
    """Fungsi utama: Membuat koneksi WebSocket dan melakukan percakapan suara"""
    pya = pyaudio.PyAudio()
    speaker = pya.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)

    // Buat koneksi WebSocket
    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()

        // Konfigurasikan parameter sesi
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "modalities": ["text", "audio"],
                "voice": VOICE,
                "input_audio_format": "pcm16",
                "output_audio_format": "pcm16",
                "instructions": "Anda adalah asisten pribadi bernama Xiaoyun",
                "turn_detection": {"type": "server_vad"},
                "input_audio_transcription": {"model": "qwen3-asr-flash-realtime"},
                "tools": TOOLS,
            },
        }))
        await ws.recv()

        // Korutin pengambilan audio
        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("Pemanggilan alat diaktifkan. Berbicaralah ke mikrofon (Ctrl+C untuk keluar)...")

        // Loop penanganan event
        async for raw in ws:
            msg = json.loads(raw)
            t = msg["type"]

            // Sesi dibuat
            if t == "session.created":
                print(f"Sesi dimulai: {msg['session']['id']}")

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

            // Respons teks inkremental
            elif t in ("response.audio_transcript.delta", "response.text.delta"):
                all_response_text += msg.get("delta", "")

            // Transkripsi ucapan pengguna
            elif t == "conversation.item.input_audio_transcription.completed":
                print(f"[Pengguna] {msg['transcript']}")

            // VAD mendeteksi awal ucapan
            elif t == "input_audio_buffer.speech_started":
                print("====== VAD mendeteksi awal ucapan ======")

            // VAD mendeteksi akhir ucapan
            elif t == "input_audio_buffer.speech_stopped":
                print("====== VAD mendeteksi akhir ucapan ======")

            // Menerima permintaan pemanggilan alat
            elif t == "response.function_call_arguments.done":
                print("====== Menerima permintaan pemanggilan alat ======")
                pending[msg["call_id"]] = {
                    "name": msg["name"],
                    "arguments": msg["arguments"],
                }

            // Respons selesai
            elif t == "response.done":
                if pending:
                    // Jalankan pemanggilan alat yang tertunda
                    for cid, info in pending.items():
                        result = handle_tool_call(info["name"], info["arguments"])
                        // Kirim hasil eksekusi alat
                        await ws.send(json.dumps({
                            "type": "conversation.item.create",
                            "item": {
                                "type": "function_call_output",
                                "call_id": cid,
                                "output": result,
                            },
                        }))
                    pending.clear()
                    // Picu server untuk terus menghasilkan respons
                    await ws.send(json.dumps({
                        "type": "response.create",
                        "response": {"modalities": ["text", "audio"]},
                    }))
                    print("====== Pemrosesan pemanggilan alat selesai ======")
                else:
                    // Respons normal selesai, cetak respons lengkap
                    if all_response_text:
                        print(f"[Model] {all_response_text}")
                    all_response_text = ""

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

asyncio.run(main())

Pemanggilan alat untuk model pemikiran mendalam

Model pemikiran mendalam melakukan inferensi sebelum mengeluarkan informasi pemanggilan alat, sehingga meningkatkan interpretabilitas dan keandalan keputusan.

  1. Proses berpikir

    Model menganalisis maksud pengguna, mengidentifikasi alat yang diperlukan, memverifikasi legalitas parameter, dan merencanakan strategi pemanggilan langkah demi langkah.

  2. Pemanggilan alat

    Model mengeluarkan satu atau beberapa permintaan pemanggilan fungsi dalam format terstruktur.

    Pemanggilan alat paralel didukung.

Berikut ini menunjukkan contoh pemanggilan alat menggunakan model pemikiran mendalam streaming.

Untuk informasi lebih lanjut tentang model pemikiran mendalam generasi teks, lihat Pemikiran mendalam. Untuk informasi lebih lanjut tentang model pemikiran mendalam multimodal, lihat Pemahaman gambar dan video dan Non-real-time (Qwen-Omni).
tool_choice hanya mendukung nilai "auto" (nilai default, yang berarti model memilih alat secara otonom) atau "none" (memaksa model tidak memilih alat).

Kompatibel dengan OpenAI

Python

Kode contoh

import os
from openai import OpenAI

// Inisialisasi klien OpenAI dan konfigurasikan layanan DashScope Alibaba Cloud
client = OpenAI(
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),  // Baca Kunci API dari variabel lingkungan
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)

// Definisikan daftar alat yang tersedia
tools = [
    // Alat 1: Dapatkan waktu saat ini
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}  // Tidak memerlukan parameter
        }
    },  
    // Alat 2: Dapatkan cuaca di kota tertentu
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]  // Parameter wajib
            }
        }
    }
]

messages = [{"role": "user", "content": input("Silakan masukkan pertanyaan Anda:")}]

// Contoh pesan untuk model multimodal
// 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": "Berdasarkan lokasi dalam gambar, bagaimana cuaca saat ini di sana?"}]
//     }]

completion = client.chat.completions.create(
    // Contoh ini menggunakan qwen3.6-plus. Anda dapat menggantinya dengan model pemikiran mendalam lainnya.
    model="qwen3.6-plus",
    messages=messages,
    extra_body={
        // Aktifkan pemikiran mendalam. Parameter ini tidak valid untuk model qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, dan QwQ.
        "enable_thinking": True
    },
    tools=tools,
    parallel_tool_calls=True,
    stream=True,
    // Hapus komentar untuk mendapatkan informasi konsumsi token
    // stream_options={
    //     "include_usage": True
    // }
)

reasoning_content = ""  // Definisikan proses berpikir lengkap
answer_content = ""     // Definisikan respons lengkap
tool_info = []          // Simpan informasi pemanggilan alat
is_answering = False   // Tentukan apakah proses berpikir telah berakhir dan respons telah dimulai
print("="*20+"Proses Berpikir"+"="*20)
for chunk in completion:
    if not chunk.choices:
        // Proses informasi statistik penggunaan
        print("\n"+"="*20+"Penggunaan"+"="*20)
        print(chunk.usage)
    else:
        delta = chunk.choices[0].delta
        // Proses rantai pikiran AI (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)  // Keluarkan proses berpikir secara real-time
            
        // Proses konten respons akhir
        else:
            if not is_answering:  // Cetak judul saat memasuki fase respons untuk pertama kalinya
                is_answering = True
                print("\n"+"="*20+"Konten Respons"+"="*20)
            if delta.content is not None:
                answer_content += delta.content
                print(delta.content,end="",flush=True)  // Streaming konten respons
            
            // Proses informasi pemanggilan alat (mendukung pemanggilan alat paralel)
            if delta.tool_calls is not None:
                for tool_call in delta.tool_calls:
                    index = tool_call.index  // Indeks pemanggilan alat, untuk pemanggilan paralel
                    
                    // Perluas dinamis daftar penyimpanan informasi alat
                    while len(tool_info) <= index:
                        tool_info.append({})
                    
                    // Kumpulkan ID pemanggilan alat (untuk pemanggilan fungsi selanjutnya)
                    if tool_call.id:
                        tool_info[index]['id'] = tool_info[index].get('id', '') + tool_call.id
                    
                    // Kumpulkan nama fungsi (untuk routing ke fungsi spesifik selanjutnya)
                    if tool_call.function and tool_call.function.name:
                        tool_info[index]['name'] = tool_info[index].get('name', '') + tool_call.function.name
                    
                    // Kumpulkan parameter fungsi (format string JSON, memerlukan penguraian selanjutnya)
                    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+"Informasi Pemanggilan Alat"+"="*19)
if not tool_info:
    print("Tidak ada pemanggilan alat")
else:
    print(tool_info)

Hasil pengembalian

Masukkan "Cuaca di empat kota tingkat provinsi" untuk mendapatkan hasil berikut:

====================Proses Berpikir====================
Baiklah, pengguna menanyakan tentang cuaca di empat kota tingkat provinsi. Pertama, saya perlu mengklarifikasi kota tingkat provinsi mana yang dimaksud. Menurut pembagian administratif Tiongkok, kota tingkat provinsi mencakup Beijing, Shanghai, Tianjin, dan Chongqing. Jadi pengguna ingin mengetahui kondisi cuaca di keempat kota ini.

Selanjutnya, saya perlu memeriksa alat yang tersedia. Alat yang disediakan mencakup fungsi get_current_weather, yang menerima parameter lokasi bertipe string. Setiap kota perlu ditanyakan secara terpisah karena fungsi ini hanya dapat menanyakan satu lokasi dalam satu waktu. Oleh karena itu, saya perlu memanggil fungsi ini sekali untuk setiap kota tingkat provinsi.

Kemudian, saya perlu mempertimbangkan cara menghasilkan pemanggilan alat yang benar. Setiap pemanggilan harus mencakup nama kota sebagai parameter. Misalnya, pemanggilan pertama untuk Beijing, kedua untuk Shanghai, dan seterusnya. Saya perlu memastikan nama parameter adalah `location` dan nilainya adalah nama kota yang benar.

Juga, pengguna mungkin menginginkan informasi cuaca untuk setiap kota, jadi saya perlu memastikan setiap pemanggilan fungsi benar. Ini mungkin memerlukan empat pemanggilan berturut-turut, satu untuk setiap kota. Namun, berdasarkan aturan penggunaan alat, mungkin perlu ditangani dalam beberapa langkah, atau beberapa pemanggilan dapat dihasilkan sekaligus. Tetapi menurut contoh, tampaknya hanya satu fungsi yang dipanggil dalam satu waktu, jadi mungkin perlu dilakukan langkah demi langkah.

Akhirnya, saya perlu mengonfirmasi apakah ada faktor lain yang perlu dipertimbangkan, seperti apakah parameter sudah benar, nama kota akurat, dan apakah saya perlu menangani situasi kesalahan yang mungkin terjadi, seperti kota tidak ada atau API tidak tersedia. Tetapi untuk saat ini, empat kota tingkat provinsi sudah jelas, jadi seharusnya tidak masalah.
====================Konten Respons====================

===================Informasi Pemanggilan Alat===================
[{'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

Kode contoh

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

const openai = new OpenAI({
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    apiKey: process.env.DASHSCOPE_API_KEY,
    baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
});

const tools = [
    {
        type: "function",
        function: {
            name: "get_current_time",
            description: "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            parameters: {}
        }
    },
    {
        type: "function",
        function: {
            name: "get_current_weather",
            description: "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            parameters: {
                type: "object",
                properties: {
                    location: {
                        type: "string",
                        description: "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                required: ["location"]
            }
        }
    }
];

async function main() {
    const rl = readline.createInterface({ input, output });
    const question = await rl.question("Silakan masukkan pertanyaan Anda:"); 
    rl.close();
    
    const messages = [{ role: "user", content: question }];
    // Contoh pesan untuk model multimodal
    // 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: "Bagaimana cuaca di lokasi yang ditunjukkan dalam gambar?"}]
    //   }];
    let reasoningContent = "";
    let answerContent = "";
    const toolInfo = [];
    let isAnswering = false;

    console.log("=".repeat(20) + "Proses Berpikir" + "=".repeat(20));
    
    try {
        const stream = await openai.chat.completions.create({
            // Contoh ini menggunakan qwen3.6-plus. Anda dapat menggantinya dengan model pemikiran mendalam lainnya.
            model: "qwen3.6-plus",
            messages,
            // Aktifkan pemikiran mendalam. Parameter ini tidak valid untuk model qwen3-30b-a3b-thinking-2507, qwen3-235b-a22b-thinking-2507, dan 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) + "Penggunaan" + "=".repeat(20));
                console.log(chunk.usage);
                continue;
            }

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

            // Proses proses berpikir
            if (delta.reasoning_content) {
                reasoningContent += delta.reasoning_content;
                process.stdout.write(delta.reasoning_content);
            }
            // Proses konten respons
            else {
                if (!isAnswering) {
                    isAnswering = true;
                    console.log("\n" + "=".repeat(20) + "Konten Respons" + "=".repeat(20));
                }
                if (delta.content) {
                    answerContent += delta.content;
                    process.stdout.write(delta.content);
                }
                // Proses pemanggilan alat
                if (delta.tool_calls) {
                    for (const toolCall of delta.tool_calls) {
                        const index = toolCall.index;
                        
                        // Pastikan array cukup panjang
                        while (toolInfo.length <= index) {
                            toolInfo.push({});
                        }
                        
                        // Perbarui ID alat
                        if (toolCall.id) {
                            toolInfo[index].id = (toolInfo[index].id || "") + toolCall.id;
                        }
                        
                        // Perbarui nama fungsi
                        if (toolCall.function?.name) {
                            toolInfo[index].name = (toolInfo[index].name || "") + toolCall.function.name;
                        }
                        
                        // Perbarui parameter
                        if (toolCall.function?.arguments) {
                            toolInfo[index].arguments = (toolInfo[index].arguments || "") + toolCall.function.arguments;
                        }
                    }
                }
            }
        }

        console.log("\n" + "=".repeat(19) + "Informasi Pemanggilan Alat" + "=".repeat(19));
        console.log(toolInfo.length ? toolInfo : "Tidak ada pemanggilan alat");

    } catch (error) {
        console.error("Terjadi kesalahan:", error);
    }
}

main(); 

Hasil pengembalian

Masukkan "Cuaca di empat kota tingkat provinsi" untuk mendapatkan hasil berikut:

Silakan masukkan pertanyaan Anda:Cuaca di empat kota tingkat provinsi
====================Proses Berpikir====================
Baiklah, pengguna menanyakan tentang cuaca di empat kota tingkat provinsi. Pertama, saya perlu mengklarifikasi kota tingkat provinsi mana yang dimaksud di Tiongkok. Beijing, Shanghai, Tianjin, dan Chongqing, benar? Selanjutnya, saya perlu memanggil fungsi kueri cuaca untuk setiap kota.

Namun pertanyaan pengguna mungkin mengharuskan saya mendapatkan kondisi cuaca untuk keempat kota ini secara terpisah. Saya perlu memanggil fungsi get_current_weather sekali untuk setiap kota, dengan nama kota masing-masing sebagai parameter. Saya perlu memastikan parameternya benar, seperti nama lengkap kota tingkat provinsi, seperti "Beijing", "Shanghai", "Tianjin", dan "Chongqing".

Kemudian, saya perlu memanggil API cuaca untuk keempat kota ini secara berurutan. Setiap pemanggilan memerlukan tool_call terpisah. Pengguna mungkin menginginkan informasi cuaca saat ini untuk setiap kota, jadi saya perlu memastikan setiap pemanggilan benar. Saya mungkin perlu memperhatikan ejaan dan nama yang benar untuk setiap kota agar menghindari kesalahan. Misalnya, Chongqing kadang disingkat, jadi nama lengkap harus digunakan dalam parameter.

Sekarang, saya perlu menghasilkan empat tool_calls, satu untuk setiap kota tingkat provinsi. Saya akan memeriksa apakah setiap parameter sudah benar lalu mengaturnya secara berurutan. Dengan cara ini, pengguna akan mendapatkan data cuaca untuk semua empat kota tingkat provinsi.
====================Konten Respons====================

===================Informasi Pemanggilan Alat===================
[
  {
    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

Kode contoh

curl

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.6-plus",
    "messages": [
        {
            "role": "user", 
            "content": "Bagaimana cuaca di Hangzhou?"
        }
    ],
    "tools": [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location":{
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
  ],
  "enable_thinking": true,
  "stream": true
}'

DashScope

Python

Kode contoh

import dashscope
from dashscope import MultiModalConversation

// Jika Anda menggunakan model di wilayah China (Beijing), ganti base_http_api_url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1
dashscope.base_http_api_url = "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/"

tools = [
    // Alat 1: Dapatkan waktu saat ini
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}  // Karena mendapatkan waktu saat ini tidak memerlukan parameter input, parameters adalah dictionary kosong
        }
    },
    // Alat 2: Dapatkan cuaca di kota tertentu
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    // Lokasi diperlukan untuk menanyakan cuaca, jadi parameter diatur ke location
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

// Definisikan pertanyaan
messages = [{"role": "user", "content": [{"text": input("Silakan masukkan pertanyaan Anda:")}]}]

// Contoh pesan untuk model multimodal
// messages = [
// {
//     "role": "user",
//     "content": [
//     {"image": "https://img.alicdn.com/imgextra/i2/O1CN01FbTJon1ErXVGMRdsN_!!6000000000405-0-tps-1024-683.jpg"},
//     {"text": "Bagaimana cuaca di lokasi yang ditunjukkan dalam gambar?"}]
// }]

completion = MultiModalConversation.call(
    // Contoh ini menggunakan qwen3.6-plus. Anda dapat menggantinya dengan model pemikiran mendalam lainnya.
    model="qwen3.6-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+"Proses Berpikir"+"="*20)

for chunk in completion:
    if chunk.status_code == 200:
        msg = chunk.output.choices[0].message
        
        // Proses proses berpikir
        if 'reasoning_content' in msg and msg.reasoning_content:
            reasoning_content += msg.reasoning_content
            print(msg.reasoning_content, end="", flush=True)
        
        // Proses konten respons
        if 'content' in msg and msg.content:
            if not is_answering:
                is_answering = True
                print("\n"+"="*20+"Konten Respons"+"="*20)
            answer_content += msg.content
            print(msg.content, end="", flush=True)
        
        // Proses pemanggilan alat
        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': ''})  // Inisialisasi semua field
                
                // Perbarui ID alat secara inkremental
                if 'id' in tool_call:
                    tool_info[index]['id'] += tool_call.get('id', '')
                
                // Perbarui informasi fungsi secara inkremental
                if 'function' in tool_call:
                    func = tool_call['function']
                    // Perbarui nama fungsi secara inkremental
                    if 'name' in func:
                        tool_info[index]['name'] += func.get('name', '')
                    // Perbarui parameter secara inkremental
                    if 'arguments' in func:
                        tool_info[index]['arguments'] += func.get('arguments', '')

print(f"\n"+"="*19+"Informasi Pemanggilan Alat"+"="*19)
if not tool_info:
    print("Tidak ada pemanggilan alat")
else:
    print(tool_info)

Hasil pengembalian

Masukkan "Cuaca di empat kota tingkat provinsi" untuk mendapatkan hasil berikut:

Silakan masukkan pertanyaan Anda:Cuaca di empat kota tingkat provinsi
====================Proses Berpikir====================
Baiklah, pengguna menanyakan tentang cuaca di empat kota tingkat provinsi. Pertama, saya perlu mengonfirmasi kota tingkat provinsi mana yang dimaksud di Tiongkok. Beijing, Shanghai, Tianjin, dan Chongqing, benar? Selanjutnya, pengguna membutuhkan kondisi cuaca untuk setiap kota, jadi saya perlu memanggil fungsi kueri cuaca.

Namun, masalahnya adalah pengguna tidak menentukan nama kota, hanya "empat kota tingkat provinsi". Saya mungkin perlu menyatakan secara eksplisit nama setiap kota tingkat provinsi lalu menanyakannya secara terpisah. Misalnya, Beijing, Shanghai, Tianjin, dan Chongqing. Saya perlu memastikan setiap kota sudah benar.

Kemudian, saya perlu memeriksa alat yang tersedia. Pengguna telah menyediakan fungsi get_current_weather, yang menerima parameter lokasi. Oleh karena itu, saya perlu memanggil fungsi ini untuk setiap kota tingkat provinsi, meneruskan nama kota yang sesuai sebagai parameter. Misalnya, pemanggilan pertama lokasinya Beijing, kedua Shanghai, ketiga Tianjin, dan keempat Chongqing.

Namun, saya mungkin perlu berhati-hati. Untuk kota tingkat provinsi seperti Chongqing, kadang-kadang diperlukan distrik yang lebih spesifik, tetapi pengguna mungkin hanya menginginkan cuaca tingkat kota. Jadi menggunakan nama kota tingkat provinsi secara langsung seharusnya tidak masalah. Selanjutnya, saya perlu menghasilkan empat pemanggilan fungsi terpisah, satu untuk setiap kota tingkat provinsi. Dengan cara ini, pengguna akan mendapatkan kondisi cuaca untuk keempat kota tersebut.

Akhirnya, saya perlu memastikan parameter untuk setiap pemanggilan sudah benar dan tidak ada yang terlewat. Ini akan memastikan pertanyaan pengguna dijawab sepenuhnya.
===================Informasi Pemanggilan Alat===================
[{'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

Kode contoh

// versi SDK dashscope >= 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 "Waktu saat ini: " + now.format(formatter) + ".";
        }
    }

    static class WeatherTool {
        private String location;

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

        public String call() {
            return location + " hari ini cerah";
        }
    }

    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));
    }
    
    // Buat metode pemanggilan alat untuk model generasi teks
    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));
    }
    // Bangun parameter model generasi teks yang mendukung pemanggilan alat
    private static GenerationParam buildGenerationParam(Message userMsg) {
        FunctionDefinition fdWeather = buildFunctionDefinition(
                "get_current_weather", "Dapatkan cuaca untuk area tertentu", jsonSchemaWeather);
        FunctionDefinition fdTime = buildFunctionDefinition(
                "get_current_time", "Dapatkan waktu saat ini", jsonSchemaTime);

        return GenerationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("qwen3.6-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();
    }

    // Buat metode pemanggilan alat untuk model multimodal
    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)));
    }

    // Bangun parameter model multimodal yang mendukung pemanggilan alat
    private static MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage userMsg) {
        FunctionDefinition fdWeather = buildFunctionDefinition(
                "get_current_weather", "Dapatkan cuaca untuk area tertentu", jsonSchemaWeather);
        FunctionDefinition fdTime = buildFunctionDefinition(
                "get_current_time", "Dapatkan waktu saat ini", jsonSchemaTime);

        return MultiModalConversationParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("qwen3-vl-plus")  // Gunakan model multimodal Qwen3-VL
                .enableThinking(true)
                .messages(Arrays.asList(userMsg))
                .tools(Arrays.asList(  // Konfigurasikan daftar alat
                        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", "Tolong beri t info cuaca di Hangzhou"))).build();
            try {
                streamCallWithMultiModalMessage(conv, userMsg);
            } catch (UploadFileException e) {
                throw new RuntimeException(e);
            }
//             Hapus komentar baris berikut saat menggunakan model generasi teks untuk pemanggilan alat
//            Generation gen = new Generation();
//            Message userMessage = Message.builder()
//                    .role(Role.USER.getValue())
//                    .content("Tolong beri tahu saya cuaca di Hangzhou")
//                    .build();
//            try {
//                streamCallWithMessage(gen, userMessage);
//            } catch (InputRequiredException e) {
//                throw new RuntimeException(e);
//            }
        } catch (ApiException | NoApiKeyException e) {
            logger.error("Terjadi pengecualian: {}", e.getMessage());
        }
        System.exit(0);
    }
}

Hasil pengembalian

{"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":"Baiklah, pengguna meminta saya"}}]}}
{"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":"untuk memberi tahu mereka cuaca di Hangzhou. Saya"}}]}}
{"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":"perlu terlebih dahulu menentukan apakah ada"}}]}}
{"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":"alat yang relevan tersedia. Melihat pada yang disediakan"}}]}}
{"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":"alat, saya melihat ada fungsi 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 dengan parameter lokasi"}}]}}
{"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":". Jadi saya harus memanggil"}}]}}
{"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":"fungsi ini dengan 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":"diatur ke Hangzhou. Tidak ada alat lain"}}]}}
{"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":"yang dibutuhkan karena"}}]}}
{"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":"pengguna hanya"}}]}}
{"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":"bertanya tentang cuaca. Selanjutnya, saya akan membuat"}}]}}
{"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, mengisi"}}]}}
{"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":"nama dan parameter"}}]}}
{"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":". Saya perlu memastikan parameternya adalah"}}]}}
{"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":"objek JSON dan lokasi adalah"}}]}}
{"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. Setelah memeriksa"}}]}}
{"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":"kesalahan, saya akan mengembalikannya."}}]}}
{"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

Kode contoh

curl

# ======= Penting =======
# Jika Anda menggunakan model generasi teks saja, ganti url dengan https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation
# Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
# URL berikut untuk wilayah Singapura. Ganti {WorkspaceId} dengan ID ruang kerja aktual Anda. URL bervariasi berdasarkan wilayah.
# === Hapus komentar ini sebelum menjalankan ===

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.6-plus",
    "input":{
        "messages":[
            {
                "role": "user",
                "content": [{"text": "Cuaca di Hangzhou"}]
            }
        ]
    },
    "parameters": {
        "enable_thinking": true,
        "incremental_output": true,
        "result_format": "message",
        "tools": [{
            "type": "function",
            "function": {
                "name": "get_current_time",
                "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
                "parameters": {}
            }
        },{
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                        }
                    },
                    "required": ["location"]
                }
            }
        }]
    }
}'

Peluncuran

Uji akurasi pemanggilan alat

  • Buat sistem evaluasi:

    Bangun set data uji yang mencerminkan skenario bisnis dunia nyata dan tetapkan metrik evaluasi yang jelas, seperti akurasi pemilihan alat, akurasi ekstraksi parameter, dan tingkat keberhasilan end-to-end.

  • Optimalkan prompt

    Berdasarkan masalah yang teridentifikasi selama pengujian—misalnya, pemilihan alat atau parameter yang salah—Anda dapat mengoptimalkan prompt sistem, deskripsi alat, dan deskripsi parameter.

  • Tingkatkan model

    Jika penyetelan prompt tidak berhasil meningkatkan kinerja, beralih ke versi model yang lebih kuat, seperti qwen3.6-plus, merupakan pendekatan yang paling langsung dan efektif.

Kontrol dinamis jumlah alat

Saat aplikasi mengintegrasikan puluhan hingga ratusan alat, menyediakan semuanya kepada model dapat menimbulkan masalah berikut:

  • Penurunan kinerja: Model kesulitan memilih alat yang tepat dari kumpulan alat yang besar, sehingga kompleksitas pemilihan meningkat secara dramatis.

  • Biaya dan latensi: Banyaknya deskripsi alat akan mengonsumsi banyak token input, yang berakibat pada peningkatan biaya dan respons yang lebih lambat.

Solusi: Tambahkan lapisan routing/pengambilan alat sebelum memanggil model. Lapisan ini menyaring pustaka alat berdasarkan kueri pengguna untuk menyediakan subset kecil alat yang relevan kepada model.

Metode utama untuk mengimplementasikan routing alat:

  • Pengambilan semantik

    Konversi deskripsi alat (description) menjadi vektor menggunakan model penyematan dan simpan di database vektor. Saat pengguna mengirimkan kueri, lakukan pencarian kemiripan vektor terhadap vektor kueri untuk mengambil K alat teratas yang paling relevan.

  • Pengambilan hibrid

    Metode ini menggabungkan pencocokan kabur dari pengambilan semantik dengan pencocokan eksak kata kunci tradisional atau tag metadata. Untuk menerapkannya, tambahkan field tags atau keywords ke alat. Selama pengambilan, lakukan pencarian vektor dan penyaringan kata kunci secara bersamaan untuk secara signifikan meningkatkan akurasi recall, terutama dalam skenario frekuensi tinggi atau spesifik.

  • Router LLM ringan

    Untuk logika routing yang lebih kompleks, gunakan model yang lebih kecil, lebih cepat, dan lebih murah—seperti Qwen-Flash—sebagai model router. Tugas model ini adalah menghasilkan daftar nama alat yang relevan berdasarkan kueri pengguna.

Saran praktis

  • Jaga set kandidat tetap ringkas: Terlepas dari metode yang digunakan, kami merekomendasikan menyediakan tidak lebih dari 20 alat kepada model utama. Pendekatan ini memberikan keseimbangan optimal antara beban kognitif model, biaya, latensi, dan akurasi.

  • Strategi penyaringan berlapis: Bangun strategi routing berbentuk corong. Misalnya, awali dengan pencocokan aturan atau kata kunci berbiaya rendah untuk menyaring alat yang jelas tidak relevan, lalu lakukan pengambilan semantik pada alat yang tersisa guna meningkatkan efisiensi dan kualitas.

Prinsip keamanan alat

Saat memberikan kemampuan eksekusi alat kepada LLM, keamanan menjadi pertimbangan utama. Prinsip intinya adalah hak istimewa minimal dan konfirmasi manusia.

  • Prinsip hak istimewa minimal: Set alat yang disediakan kepada model harus secara ketat mematuhi prinsip hak istimewa minimal. Secara default, alat harus read-only, seperti alat untuk menanyakan cuaca atau mencari dokumen. Hindari memberikan izin "write" apa pun yang melibatkan perubahan status atau operasi sumber daya.

  • Isolasi alat berbahaya: Jangan berikan alat berbahaya secara langsung kepada LLM, seperti alat untuk mengeksekusi kode arbitrer (code interpreter), mengoperasikan sistem file (fs.delete), melakukan operasi hapus atau perbarui database (db.drop_table), atau menangani transaksi keuangan (payment.transfer).

  • Keterlibatan manusia: Tinjauan manual dan proses konfirmasi diperlukan untuk semua operasi berhak tinggi atau tidak dapat diubah. Model dapat menghasilkan permintaan operasi, tetapi tombol "execute" akhir harus diklik oleh pengguna manusia. Misalnya, model dapat menyiapkan email, tetapi pengguna harus mengonfirmasi operasi pengiriman.

Optimasi pengalaman pengguna

Proses pemanggilan fungsi melibatkan beberapa langkah, dan masalah pada langkah apa pun dapat berdampak negatif pada pengalaman pengguna.

Menangani kegagalan eksekusi alat

Kegagalan eksekusi alat umum terjadi. Anda dapat menerapkan strategi berikut:

  • Upaya maksimum: Tetapkan batas upaya ulang yang wajar, misalnya 3 kali, untuk menghindari penantian pengguna yang lama atau pemborosan sumber daya sistem akibat kegagalan berkelanjutan.

  • Berikan respons cadangan: Jika upaya ulang habis atau terjadi kesalahan yang tidak dapat diselesaikan, kembalikan prompt yang jelas dan ramah kepada pengguna, seperti: "Maaf, saya tidak dapat menemukan informasi yang relevan saat ini. Layanan mungkin sedang sibuk. Silakan coba lagi nanti."

Mengatasi latensi pemrosesan

Latensi tinggi dapat mengurangi kepuasan pengguna. Anda dapat menerapkan optimasi di frontend dan backend.

  • Tetapkan timeout: Tetapkan timeout independen dan wajar untuk setiap langkah proses pemanggilan fungsi. Jika terjadi timeout, hentikan operasi segera dan berikan umpan balik kepada pengguna.

  • Berikan umpan balik instan: Saat pemanggilan fungsi dimulai, tampilkan prompt di antarmuka, seperti "Menanyakan cuaca untuk Anda..." atau "Mencari informasi yang relevan...". Hal ini memberikan umpan balik real-time kepada pengguna mengenai kemajuan proses.

Penagihan

Selain token dalam array messages, deskripsi alat juga dikenai biaya sebagai token input.

Teruskan informasi alat melalui Pesan Sistem

Kami merekomendasikan meneruskan informasi alat ke model bahasa besar (LLM) menggunakan parameter tools, sebagaimana dijelaskan dalam bagian Cara menggunakan. Untuk meneruskan informasi alat melalui Pesan Sistem, gunakan templat prompt dalam kode berikut guna mencapai kinerja model yang optimal:

Kompatibel dengan OpenAI

Python

Kode contoh

import os
from openai import OpenAI
import json

client = OpenAI(
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    // Jika Anda menggunakan model di wilayah China (Beijing), ganti base_url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
)

// Prompt Sistem kustom, yang dapat dimodifikasi sesuai kebutuhan Anda
custom_prompt = "Anda adalah asisten cerdas yang bertanggung jawab untuk memanggil berbagai alat guna membantu pengguna menyelesaikan masalah. Anda dapat memilih alat yang tepat dan memanggilnya dengan benar berdasarkan kebutuhan pengguna."

tools = [
    // Alat 1: Dapatkan waktu saat ini
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}
        }
    },  
    // Alat 2: Dapatkan cuaca di kota tertentu
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

// Iterasi melalui daftar alat untuk membangun deskripsi untuk setiap alat
tools_descriptions = []
for tool in tools:
    tool_json = json.dumps(tool, ensure_ascii=False)
    tools_descriptions.append(tool_json)

// Gabungkan semua deskripsi alat menjadi satu string
tools_content = "\n".join(tools_descriptions)

system_prompt = f"""{custom_prompt}

# Tools

Anda dapat memanggil satu atau beberapa fungsi untuk membantu kueri pengguna.

Anda diberikan signature fungsi di dalam tag XML <tools></tools>:
<tools>
{tools_content}
</tools>

Untuk setiap pemanggilan fungsi, kembalikan objek json dengan nama fungsi dan argumen di dalam tag XML <tool_call></tool_call>:
<tool_call>
{{"name": <function-name>, "arguments": <args-json-object>}}
</tool_call>"""

messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": "Jam berapa sekarang?"}
]

completion = client.chat.completions.create(
    model="qwen3.6-plus",
    extra_body={"enable_thinking": False},
    messages=messages,
)
print(completion.model_dump_json())

Node.js

Kode contoh

import OpenAI from "openai";

const client = new OpenAI({
    // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey: "sk-xxx",
    apiKey: process.env.DASHSCOPE_API_KEY,
    // Jika Anda menggunakan model di wilayah China (Beijing), ganti baseURL dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
    baseURL: "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
});

// Prompt Sistem kustom
const customPrompt = "Anda adalah asisten cerdas yang bertanggung jawab untuk memanggil berbagai alat guna membantu pengguna menyelesaikan masalah. Anda dapat memilih alat yang tepat dan memanggilnya dengan benar berdasarkan kebutuhan pengguna.";

const tools = [
    // Alat 1: Dapatkan waktu saat ini
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna ketika Anda ingin mengetahui waktu saat ini.",
            "parameters": {}
        }
    },
    // Alat 2: Dapatkan cuaca di kota tertentu
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
];

// Iterasi melalui daftar alat untuk membangun deskripsi untuk setiap alat
const toolsDescriptions = [];
for (const tool of tools) {
    const toolJson = JSON.stringify(tool, null, 2);
    toolsDescriptions.push(toolJson);
}

// Gabungkan semua deskripsi alat menjadi satu string
const toolsContent = toolsDescriptions.join("\n");

const systemPrompt = `${customPrompt}

# Tools

Anda dapat memanggil satu atau beberapa fungsi untuk membantu kueri pengguna.

Anda diberikan signature fungsi di dalam tag XML <tools></tools>:
<tools>
${toolsContent}
</tools>

Untuk setiap pemanggilan fungsi, kembalikan objek json dengan nama fungsi dan argumen di dalam tag XML <tool_call></tool_call>:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>`;

const messages = [
    {"role": "system", "content": systemPrompt},
    {"role": "user", "content": "Jam berapa sekarang?"}
];

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

main(); 

DashScope

Python

Kode contoh

import os
from dashscope import MultiModalConversation
import json
# Jika Anda menggunakan model di Wilayah China (Beijing), ganti base_http_api_url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1
dashscope.base_http_api_url = 'https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1'

# Prompt Sistem kustom
custom_prompt = "Anda adalah asisten cerdas yang bertanggung jawab untuk memanggil berbagai alat untuk membantu pengguna menyelesaikan masalah. Anda dapat memilih alat yang sesuai dan memanggilnya dengan benar berdasarkan kebutuhan pengguna."

tools = [
    # Alat 1: Dapatkan waktu saat ini
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Berguna saat Anda ingin mengetahui waktu saat ini.",
            "parameters": {}
        }
    },  
    # Alat 2: Dapatkan cuaca di kota tertentu
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Berguna saat Anda ingin membuat kueri cuaca di kota tertentu.",
            "parameters": {  
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Sebuah kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang."
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# Lakukan iterasi pada daftar alat untuk membuat deskripsi untuk setiap alat
tools_descriptions = []
for tool in tools:
    tool_json = json.dumps(tool, ensure_ascii=False)
    tools_descriptions.append(tool_json)

# Gabungkan semua deskripsi alat menjadi satu string tunggal
tools_content = "\n".join(tools_descriptions)

system_prompt = f"""{custom_prompt}

# Alat

Anda dapat memanggil satu atau beberapa fungsi untuk membantu kueri pengguna.

Anda diberikan tanda tangan fungsi di dalam tag XML <tools></tools>:
<tools>
{tools_content}
</tools>

Untuk setiap pemanggilan fungsi, kembalikan objek json dengan nama fungsi dan argumen di dalam tag XML <tool_call></tool_call>:
<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(
    # Kunci API bervariasi menurut Wilayah. Untuk mendapatkan kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan kunci API Model Studio Anda: api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    model="qwen3.6-plus",
    enable_thinking=False,
    messages=messages,
    result_format="message",  # Atur output ke format paket
)

print(response)

Java

Kode contoh

// 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 {

        // Prompt Sistem kustom
        String customPrompt = "Anda adalah asisten cerdas yang bertanggung jawab untuk memanggil berbagai alat guna membantu pengguna menyelesaikan masalah. Anda dapat memilih alat yang tepat dan memanggilnya dengan benar berdasarkan kebutuhan pengguna.";

        // Bangun deskripsi alat
        String[] toolsDescriptions = {
                // Alat 1: Dapatkan waktu saat ini
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_time\",\n" +
                        "        \"description\": \"Berguna ketika Anda ingin mengetahui waktu saat ini.\",\n" +
                        "        \"parameters\": {}\n" +
                        "    }\n" +
                        "}",
                // Alat 2: Dapatkan cuaca di kota tertentu
                "{\n" +
                        "    \"type\": \"function\",\n" +
                        "    \"function\": {\n" +
                        "        \"name\": \"get_current_weather\",\n" +
                        "        \"description\": \"Berguna ketika Anda ingin menanyakan cuaca di kota tertentu.\",\n" +
                        "        \"parameters\": {\n" +
                        "            \"type\": \"object\",\n" +
                        "            \"properties\": {\n" +
                        "                \"location\": {\n" +
                        "                    \"type\": \"string\",\n" +
                        "                    \"description\": \"Kota atau distrik, seperti Beijing, Hangzhou, atau Yuhang.\"\n" +
                        "                }\n" +
                        "            },\n" +
                        "            \"required\": [\"location\"]\n" +
                        "        }\n" +
                        "    }\n" +
                        "}"
        };

        // Gabungkan semua deskripsi alat menjadi satu string
        String toolsContent = String.join("\n", toolsDescriptions);

        // Bangun prompt sistem
        String systemPrompt = String.format("%s\n\n" +
                        "# Tools\n\n" +
                        "Anda dapat memanggil satu atau beberapa fungsi untuk membantu kueri pengguna.\n\n" +
                        "Anda diberikan signature fungsi di dalam tag XML <tools></tools>:\n" +
                        "<tools>\n%s\n</tools>\n\n" +
                        "Untuk setiap pemanggilan fungsi, kembalikan objek json dengan nama fungsi dan argumen di dalam tag XML <tool_call></tool_call>:\n"
                        +
                        "<tool_call>\n" +
                        "{\"name\": <function-name>, \"arguments\": <args-json-object>}\n" +
                        "</tool_call>",
                customPrompt, toolsContent);

        // Bangun daftar pesan
        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", "Jam berapa sekarang?")))
                .build();

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

        // Bangun parameter permintaan
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                .model("qwen3.6-plus")
                .enableThinking(false)
                // Kunci API bervariasi berdasarkan wilayah. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key
                // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan: .apiKey("sk-xxx")
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .messages(messages)
                .build();

        // Panggil API percakapan multimodal. Jika Anda menggunakan model di wilayah China (Beijing), ganti url dengan: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1
        MultiModalConversation conv = new MultiModalConversation(Protocol.HTTP.getValue(), "https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1");
        MultiModalConversationResult result = conv.call(param);

        // Cetak hasilnya
        System.out.println(JsonUtils.toJson(result));
    }
}
Setelah menjalankan kode di atas, Anda dapat menggunakan parser XML untuk mengekstrak informasi pemanggilan alat—termasuk nama fungsi dan parameter input—dari antara tag <tool_call> dan </tool_call>.

Kode error

Jika panggilan model gagal dan mengembalikan pesan error, lihat Kode Error untuk menyelesaikan masalah.