All Products
Search
Document Center

Alibaba Cloud Model Studio:Bangun percakapan suara real-time dengan qwen-audio-3.0-realtime-plus melalui AOQ

Last Updated:Sep 02, 2026

Gunakan AOQ untuk terhubung ke qwen-audio-3.0-realtime-plus dan manfaatkan VAD sisi server guna membangun percakapan suara real-time berlatensi rendah. Kode klien menggunakan Android Java.

Ikhtisar solusi

Qwen-Audio adalah model interaksi suara real-time end-to-end untuk skenario latensi rendah seperti asisten suara, layanan pelanggan, dan teman AI. AOQ mengirimkan audio dan event pada track yang terpisah. Track Audio membawa PCM uplink dari mikrofon dan PCM downlink dari model, sedangkan Track Data membawa event protokol Realtime.

Tutorial ini menggunakan server_vad. Klien terus-menerus mengirimkan audio, dan layanan mendeteksi kapan pengguna mulai dan berhenti berbicara serta memicu respons.

Prasyarat

  1. Aktifkan Model Studio dan ikuti Dapatkan dan konfigurasikan Kunci API. Simpan Kunci API hanya di server aplikasi Anda. Jangan sertakan dalam kode klien atau commit ke repositori kode.
  2. Konfirmasi endpoint AOQ untuk wilayah tempat aplikasi Anda diterapkan. Untuk panduan pemilihan, lihat Pilih wilayah, cakupan penerapan, dan endpoint.
  3. Unduh AOQ Client SDK versi terbaru sebagaimana dijelaskan dalam Unduhan SDK.
  4. Bangun server aplikasi dan implementasikan otentikasi proxy sebagaimana dijelaskan dalam Otentikasi token. Sebelum setiap koneksi baru, klien harus memperoleh kredensial koneksi baru dari server aplikasi.

Impor SDK

Impor SDK untuk platform pengembangan Anda. Implementasi klien menggunakan Android Java. Platform lain menyediakan antarmuka dan alur event yang sama. Tutorial ini menggunakan aliran audio PCM. Pengkodean Opus disediakan oleh plugin. Impor plugin Opus jika uplink menggunakan Opus.

Android

  1. Letakkan AoqClientSdk-release.aar di app/libs, lalu konfigurasikan dependensi serta ABIs yang didukung SDK di app/build.gradle:
android {
    defaultConfig {
        minSdk 21
        ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar'])
}
  1. Deklarasikan izin berikut di AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  1. Minta izin RECORD_AUDIO saat runtime sebelum perangkat terkait digunakan.

iOS

  1. Tambahkan AoqClientSdk.framework ke proyek Xcode dan pilih Embed & Sign di bawah Target > General > Frameworks, Libraries, and Embedded Content. SDK mendukung perangkat arm64 yang menjalankan iOS 13.0 atau lebih baru.
  2. Tambahkan NSMicrophoneUsageDescription ke Info.plist dan minta otorisasi sebelum perangkat terkait digunakan.
  3. Gunakan import AoqClientSdk di Swift atau #import <AoqClientSdk/AoqClientSdk.h> di Objective-C.

HarmonyOS

  1. Letakkan AoqClientSdk.har di entry/libs dan deklarasikan dependensi di entry/oh-package.json5. SDK kompatibel dengan API 12 dan mendukung arm64-v8a:
{
  "dependencies": {
    "@aoq/client-sdk": "file:./libs/AoqClientSdk.har"
  }
}
  1. Deklarasikan izin berikut di entry/src/main/module.json5:
"requestPermissions": [
  { "name": "ohos.permission.INTERNET" },
  { "name": "ohos.permission.MICROPHONE",
    "reason": "$string:perm_mic_reason",
    "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }
]
  1. Sebelum perangkat terkait digunakan, panggil abilityAccessCtrl.createAtManager().requestPermissionsFromUser untuk meminta ohos.permission.MICROPHONE.

Linux (Python)

  1. Ekstrak SDK dan simpan aoq_client_sdk.py, libAoqClientSdk.so, dan libonnxruntime.so.1.16.3 dalam direktori yang sama.
  2. Tambahkan direktori SDK ke jalur pencarian Python dan pustaka dinamis:
export PYTHONPATH="$PWD/AoqClientSdk:$PYTHONPATH"
export LD_LIBRARY_PATH="$PWD/AoqClientSdk:$LD_LIBRARY_PATH"
  1. Gunakan import aoq_client_sdk di Python. Anda juga dapat menentukan jalur mutlak libAoqClientSdk.so dengan menggunakan AOQ_CLIENT_SDK_LIB.

Coba demo

Gunakan demo Android dari Alibaba Cloud Model Studio untuk memverifikasi konektivitas AOQ secara cepat. Unduh APK dan konfigurasikan Kunci API serta workspaceId untuk mencoba model yang dipilih.

Pindai kode QR berikut untuk mengunduh demo:

QR code for downloading the demo

Alur implementasi

  1. Server aplikasi memperoleh kredensial untuk koneksi AOQ saat ini ke qwen-audio-3.0-realtime-plus dari URL token Realtime.
  2. Klien mengonfigurasi penyandi uplink SDK dan decoder downlink sesuai model yang dipilih dan format audio aplikasi.
  3. Klien menginisialisasi perangkat perekaman dan pemutaran serta membuat AoqConnectConfig. Klien mengisi bidang kredensial untuk koneksi saat ini dan mengonfigurasi Track Audio dan Data untuk dipublikasikan dan dilanggani. Klien menonaktifkan pengiriman Track Audio dan memanggil connect untuk membuat koneksi AOQ.
  4. Setelah koneksi terbentuk, klien mengirim session.update. Klien hanya mengaktifkan Track Audio setelah menerima session.updated.
  5. VAD sisi server secara otomatis menentukan batas giliran. Audio model diputar melalui Track Audio dan event percakapan dikembalikan melalui Track Data.
  6. Untuk mengakhiri, putuskan koneksi dan hapus engine. SDK secara otomatis menutup perangkat audio.
Sequence diagram for real-time voice conversations over AOQ

Peroleh token dari server aplikasi

Tetapkan DASHSCOPE_API_KEY di server aplikasi dan kirim permintaan ke endpoint untuk wilayah yang dipilih. clientIp adalah Alamat IP publik aktual dari klien. Bidang ini opsional, tetapi menentukannya membantu layanan mengalokasikan endpoint relay yang sesuai.

curl -X POST \
  "https://{endpoint}/api/v1/webrtc/realtime?model=qwen-audio-3.0-realtime-plus" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${DASHSCOPE_API_KEY}" \
  -H "x-dashscope-rtc-transport: moq" \
  -d "{\"clientIp\": \"${CLIENT_REAL_IP}\"}"

CatatanJika server aplikasi tidak dapat memperoleh Alamat IP publik aktual dari klien, hilangkan clientIp daripada mengirimkan string kosong.

Server aplikasi mengembalikan bidang respons berikut ke klien. Token AOQ hanya dapat digunakan untuk satu koneksi. Sebelum setiap pemanggilan connect, klien harus meminta token baru daripada menyimpan cache atau menggunakan ulang token lama. Jangan pernah mengembalikan Kunci API ke klien di lingkungan produksi. Untuk semua bidang permintaan dan respons, lihat Otentikasi token.

Bidang respons

Bidang SDK

aoqTokenForClient

AoqConnectConfig.token

sid

AoqConnectConfig.sid

clientRelayCertFingerprint

AoqConnectConfig.certFingerprint

clientRelayEndpoints

AoqConnectConfig.relayEndpoints

extraInfo.workspaceIdHash

AoqConnectConfig.workspaceIdHash

Implementasikan client Android

Sebelum setiap koneksi, klien memperoleh kredensial koneksi baru dari server aplikasi dan membuat AoqConnectConfig. Petakan bidang respons token dan tambahkan pengaturan koneksi sisi klien seperti track publish dan subscribe. Ikuti langkah-langkah berikut untuk mengimplementasikan percakapan suara real-time di Android.

1. Buat engine dan daftarkan callback

Buat engine AOQ singleton dan daftarkan callback event. Konfigurasikan sesi setelah koneksi berhasil, dan salurkan event server ke UI dan mesin keadaan aplikasi.

AoqClientListener listener = new AoqClientListener() {
    @Override
    public void onConnectionStatusChange(AoqClientEngine.AoqConnectionStatus status) {
        if (status == AoqClientEngine.AoqConnectionStatus.AoqConnectionStatusConnected) {
            configureSession();
        }
    }

    @Override
    public void onDataMsg(AoqClientEngine.AoqDataMsg msg) {
        handleServerEvent(msg);
    }
};

AoqClientEngine.AoqCreateConfig createConfig = new AoqClientEngine.AoqCreateConfig();
createConfig.workDir = context.getFilesDir().getAbsolutePath();
engine = AoqClientEngine.createEngine(context, createConfig, listener);

2. Konfigurasikan kodek audio

Konfigurasikan penyandi uplink SDK dan decoder downlink sesuai model yang dipilih dan format audio aplikasi. Nilai berikut adalah contoh PCM untuk tutorial ini dan tidak membatasi format audio aplikasi Anda.

AoqClientEngine.AoqAudioCodecConfig audioEncoderConfig =
        new AoqClientEngine.AoqAudioCodecConfig();
audioEncoderConfig.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
audioEncoderConfig.codecType = AoqClientEngine.AoqEncoderType.AoqEncoderTypeAudioPCM;
audioEncoderConfig.sampleRate = 16000; // Contoh. Sesuaikan dengan model dan format aplikasi.
audioEncoderConfig.channel = 1;
engine.setAudioEncoderConfig(audioEncoderConfig);

AoqClientEngine.AoqAudioCodecConfig audioDecoderConfig =
        new AoqClientEngine.AoqAudioCodecConfig();
audioDecoderConfig.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
audioDecoderConfig.codecType = AoqClientEngine.AoqEncoderType.AoqEncoderTypeAudioPCM;
audioDecoderConfig.sampleRate = 24000; // Contoh. Sesuaikan dengan format output model.
audioDecoderConfig.channel = 1;
engine.setAudioDecoderConfig(audioDecoderConfig);

3. Konfigurasikan track dan hubungkan

Gunakan antarmuka SDK untuk memulai penangkapan dan pemutaran audio. Petakan respons token server aplikasi saat ini ke bidang kredensial di AoqConnectConfig, lalu konfigurasikan Track Audio dan Data di publishTracks dan subscribeTracks. Nonaktifkan pengiriman Track Audio saat Anda memanggil connect. Aktifkan pengiriman hanya setelah menerima session.updated.

AoqClientEngine.AoqAudioCaptureConfig captureConfig =
        new AoqClientEngine.AoqAudioCaptureConfig();
captureConfig.channel = 1;
captureConfig.isVoipMode = true;
engine.startAudioCapture(captureConfig);

AoqClientEngine.AoqAudioPlaybackConfig playbackConfig =
        new AoqClientEngine.AoqAudioPlaybackConfig();
playbackConfig.channel = 1;
playbackConfig.isVoipMode = true;
playbackConfig.isDefaultSpeaker = true;
engine.startAudioPlayer(playbackConfig);

AoqClientEngine.AoqTrackParam publishAudioTrack = new AoqClientEngine.AoqTrackParam();
publishAudioTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
connectConfig.publishTracks.add(publishAudioTrack);

AoqClientEngine.AoqTrackParam publishDataTrack = new AoqClientEngine.AoqTrackParam();
publishDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
connectConfig.publishTracks.add(publishDataTrack);

AoqClientEngine.AoqTrackParam subscribeAudioTrack = new AoqClientEngine.AoqTrackParam();
subscribeAudioTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
connectConfig.subscribeTracks.add(subscribeAudioTrack);

AoqClientEngine.AoqTrackParam subscribeDataTrack = new AoqClientEngine.AoqTrackParam();
subscribeDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
connectConfig.subscribeTracks.add(subscribeDataTrack);

engine.enableSendMediaStream(
        AoqClientEngine.AoqTrackType.AoqTrackTypeAudio, false);
engine.connect(connectConfig);

4. Kirim session.update

Setelah koneksi berhasil, konfigurasikan modalities output, voice, format audio, instruksi, dan VAD. Baik input_audio_format maupun output_audio_format menggunakan pcm. Konfigurasi kodek SDK menentukan laju sampel. Untuk semua parameter, lihat Event klien.

JSONObject vad = new JSONObject()
        .put("type", "server_vad")
        .put("threshold", 0.5)
        .put("silence_duration_ms", 800);
JSONObject session = new JSONObject()
        .put("modalities", new JSONArray().put("text").put("audio"))
        .put("voice", "longanqian")
        .put("input_audio_format", "pcm")
        .put("output_audio_format", "pcm")
        .put("instructions", "You are a helpful voice assistant.")
        .put("turn_detection", vad);
JSONObject sessionUpdate = new JSONObject()
        .put("type", "session.update")
        .put("session", session);
AoqClientEngine.AoqDataMsg dataMessage = new AoqClientEngine.AoqDataMsg();
dataMessage.data = sessionUpdate.toString().getBytes(StandardCharsets.UTF_8);
engine.sendDataMsg(dataMessage);

session.updated menunjukkan bahwa konfigurasi sesi aktif. Aktifkan pengiriman Track Audio hanya pada titik ini agar audio yang ditangkap sebelumnya tidak dikirim ke model.

if ("session.updated".equals(type)) {
    engine.enableSendMediaStream(
            AoqClientEngine.AoqTrackType.AoqTrackTypeAudio, true);
}

6. Menangani event server

Di onDataMsg, gunakan type untuk menampilkan transkrip pengguna dan model serta menangani error. Untuk semua field event, lihat Event server.

if ("response.audio_transcript.delta".equals(type)) {
    String delta = event.optString("delta");
    // Tambahkan delta ke transkrip model di UI.
} else if ("conversation.item.input_audio_transcription.completed".equals(type)) {
    String transcript = event.optString("transcript");
    // Tampilkan transkrip pengguna akhir di UI.
} else if ("error".equals(type)) {
    // Baca field error dan perbarui status aplikasi.
}

7. Putuskan koneksi dan hapus engine

Saat percakapan berakhir, putuskan koneksi dan hapus engine singleton. disconnect atau destroy secara otomatis menutup penangkapan dan pemutaran audio, sehingga Anda tidak perlu menghentikan perangkat secara terpisah.

engine.disconnect();
AoqClientEngine.destroy();

Event server utama

Event Track Data diidentifikasi berdasarkan type. Klien harus menangani event kunci berikut. Untuk skema event lengkap, lihat Event server.

Event

Deskripsi

session.created

Sesi dibuat dan pengaturan default dikembalikan

session.updated

Pengaturan client aktif dan uplink audio dapat diaktifkan

input_audio_buffer.speech_started

Layanan mendeteksi bahwa pengguna mulai berbicara

input_audio_buffer.speech_stopped

Layanan mendeteksi bahwa pengguna berhenti berbicara

input_audio_buffer.committed

Audio untuk giliran telah dikomit.

response.created

Model mulai menghasilkan respons

response.audio_transcript.delta

Transkrip model inkremental

conversation.item.input_audio_transcription.completed

Transkrip pengguna akhir tersedia

response.done

Respons selesai

error

Terjadi error server

Contoh lengkap

Kelas berikut menerima AoqConnectConfig yang diisi dengan kredensial untuk koneksi saat ini dan menambahkan pengaturan perangkat audio, track publish, dan track subscribe. Peroleh kredensial baru dan buat konfigurasi koneksi baru untuk setiap rekoneksi. Tambahkan izin, status UI, dan logika rekoneksi di lingkungan produksi.

import android.content.Context;

import com.alibaba.aoq.clientsdk.AoqClientEngine;
import com.alibaba.aoq.clientsdk.AoqClientListener;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.nio.charset.StandardCharsets;

public final class RealtimeVoiceChatClient {
    private AoqClientEngine engine;

    public RealtimeVoiceChatClient(Context context, AoqClientEngine.AoqConnectConfig connectConfig) {
        AoqClientListener listener = new AoqClientListener() {
            @Override
            public void onConnectionStatusChange(AoqClientEngine.AoqConnectionStatus status) {
                if (status == AoqClientEngine.AoqConnectionStatus.AoqConnectionStatusConnected) {
                    configureSession();
                }
            }

            @Override
            public void onDataMsg(AoqClientEngine.AoqDataMsg msg) {
                try {
                    JSONObject event = new JSONObject(
                            new String(msg.data, StandardCharsets.UTF_8));
                    String type = event.optString("type");
                    if ("session.updated".equals(type)) {
                        engine.enableSendMediaStream(
                                AoqClientEngine.AoqTrackType.AoqTrackTypeAudio, true);
                    } else if ("response.audio_transcript.delta".equals(type)) {
                        String delta = event.optString("delta");
                        // Tampilkan delta di UI.
                    } else if ("conversation.item.input_audio_transcription.completed".equals(type)) {
                        String transcript = event.optString("transcript");
                        // Tampilkan transkrip di UI.
                    } else if ("error".equals(type)) {
                        // Baca field error dan perbarui status aplikasi.
                    }
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Invalid server event", e);
                }
            }
        };

        AoqClientEngine.AoqCreateConfig createConfig = new AoqClientEngine.AoqCreateConfig();
        createConfig.workDir = context.getFilesDir().getAbsolutePath();
        engine = AoqClientEngine.createEngine(context, createConfig, listener);

        // Nilai contoh. Sesuaikan pengaturan ini dengan model dan format audio aplikasi.
        AoqClientEngine.AoqAudioCodecConfig audioEncoderConfig =
                new AoqClientEngine.AoqAudioCodecConfig();
        audioEncoderConfig.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
        audioEncoderConfig.codecType = AoqClientEngine.AoqEncoderType.AoqEncoderTypeAudioPCM;
        audioEncoderConfig.sampleRate = 16000;
        audioEncoderConfig.channel = 1;
        engine.setAudioEncoderConfig(audioEncoderConfig);

        AoqClientEngine.AoqAudioCodecConfig audioDecoderConfig =
                new AoqClientEngine.AoqAudioCodecConfig();
        audioDecoderConfig.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
        audioDecoderConfig.codecType = AoqClientEngine.AoqEncoderType.AoqEncoderTypeAudioPCM;
        audioDecoderConfig.sampleRate = 24000;
        audioDecoderConfig.channel = 1;
        engine.setAudioDecoderConfig(audioDecoderConfig);

        AoqClientEngine.AoqAudioCaptureConfig captureConfig =
                new AoqClientEngine.AoqAudioCaptureConfig();
        captureConfig.channel = 1;
        captureConfig.isVoipMode = true;
        engine.startAudioCapture(captureConfig);

        AoqClientEngine.AoqAudioPlaybackConfig playbackConfig =
                new AoqClientEngine.AoqAudioPlaybackConfig();
        playbackConfig.channel = 1;
        playbackConfig.isVoipMode = true;
        playbackConfig.isDefaultSpeaker = true;
        engine.startAudioPlayer(playbackConfig);

        AoqClientEngine.AoqTrackParam publishAudioTrack =
                new AoqClientEngine.AoqTrackParam();
        publishAudioTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
        connectConfig.publishTracks.add(publishAudioTrack);

        AoqClientEngine.AoqTrackParam publishDataTrack =
                new AoqClientEngine.AoqTrackParam();
        publishDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
        connectConfig.publishTracks.add(publishDataTrack);

        AoqClientEngine.AoqTrackParam subscribeAudioTrack =
                new AoqClientEngine.AoqTrackParam();
        subscribeAudioTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
        connectConfig.subscribeTracks.add(subscribeAudioTrack);

        AoqClientEngine.AoqTrackParam subscribeDataTrack =
                new AoqClientEngine.AoqTrackParam();
        subscribeDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
        connectConfig.subscribeTracks.add(subscribeDataTrack);

        engine.enableSendMediaStream(AoqClientEngine.AoqTrackType.AoqTrackTypeAudio, false);
        engine.connect(connectConfig);
    }

    private void configureSession() {
        try {
            JSONObject vad = new JSONObject()
                    .put("type", "server_vad")
                    .put("threshold", 0.5)
                    .put("silence_duration_ms", 800);
            JSONObject session = new JSONObject()
                    .put("modalities", new JSONArray().put("text").put("audio"))
                    .put("voice", "longanqian")
                    .put("input_audio_format", "pcm")
                    .put("output_audio_format", "pcm")
                    .put("turn_detection", vad);
            JSONObject sessionUpdate = new JSONObject()
                    .put("type", "session.update")
                    .put("session", session);
            AoqClientEngine.AoqDataMsg dataMessage = new AoqClientEngine.AoqDataMsg();
            dataMessage.data = sessionUpdate.toString().getBytes(StandardCharsets.UTF_8);
            engine.sendDataMsg(dataMessage);
        } catch (JSONException e) {
            throw new IllegalStateException("Failed to create session.update", e);
        }
    }

    public void close() {
        engine.disconnect();
        AoqClientEngine.destroy();
    }
}

Jalankan dan verifikasi

  1. Audio mikrofon mulai dialirkan hanya setelah menerima session.updated.
  2. Setelah pengguna berhenti berbicara, layanan mengirimkan audio dan mulai merespons. Event teks dan audio dari Audio-Track dikembalikan secara berkelanjutan.

Skenario umum

Ubah mode interaksi

Gunakan server_vad untuk deteksi giliran berbasis kesenyapan, smart_turn untuk deteksi giliran akustik dan semantik, atau atur turn_detection ke null untuk push-to-talk. turn_detection hanya dapat diubah sebelum input audio pertama. Buat sesi baru untuk mengubah mode.

Ubah suara

Tetapkan session.voice di session.update pertama. Voice sistem yang didukung bervariasi tergantung model. Untuk voice yang didukung dan kloning suara, lihat Percakapan suara real-time Qwen-Audio.

Speaker atau earpiece

Tetapkan perangkat output default dengan menggunakan AoqAudioPlaybackConfig.isDefaultSpeaker, dan panggil enableSpeakerphone untuk beralih selama sesi aktif.

Panggilan latar belakang di Android

Di Android 10 atau lebih baru, gunakan foreground service dengan foregroundServiceType="microphone|mediaPlayback" untuk melanjutkan penangkapan dan pemutaran di latar belakang. Mulai saat aplikasi terlihat oleh pengguna.

Pemecahan masalah

Masalah

Solusi

Koneksi gagal

Pastikan token valid, endpoint sesuai dengan wilayah penerapan, dan bidang AoqConnectConfig dipetakan dengan benar.

Sesi terbentuk tetapi tidak ada respons yang dikembalikan

Pastikan track Audio diaktifkan setelah session.updated dan penyandi uplink SDK sesuai dengan format audio model dan aplikasi.

Respons tidak memiliki audio

Pastikan track Audio dilanggani dan pemutar audio berjalan, lalu verifikasi bahwa decoder downlink SDK sesuai dengan format audio output model.

Informasi terkait

Untuk semua parameter, field event, dan antarmuka untuk platform lain, lihat: