すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Model Studio:リアルタイム音声認識 - Qwen

最終更新日:Jan 31, 2026

ライブストリーミング、オンライン会議、音声チャット、スマートアシスタントなどのシナリオでは、連続したオーディオストリームをリアルタイムでテキストに変換する必要があります。Qwen のリアルタイム音声認識を使用すると、即座に字幕を提供したり、会議の議事録を生成したり、音声コマンドに応答したりできます。

主な特徴

  • 高精度な多言語認識:標準中国語や、広東語、四川語などの様々な中国語方言を含む、複数の言語に対して高精度な認識を提供します。詳細については、「モデルの特徴」をご参照ください。

  • 複雑な環境への適応:複雑な音響環境に適応し、自動言語検出と人間以外の音声のインテリジェントなフィルタリングをサポートします。

  • コンテキストバイアス:コンテキストを提供することで認識精度を向上させます。この機能は、従来のホットワードソリューションよりも柔軟で強力です。

  • 感情認識:驚き、平静、喜び、悲しみ、嫌悪、怒り、恐怖など、複数の感情状態を認識します。

利用可能状況

サポートされるモデル:

国際

国際デプロイメントモードでは、エンドポイントとデータストレージはシンガポールリージョンに配置されます。モデル推論のコンピューティングリソースは、中国本土を除き、グローバルに動的にスケジューリングされます。

以下のモデルを呼び出すには、シンガポールリージョンの API キーを使用してください:

Qwen3-ASR-Flash-Realtime: qwen3-asr-flash-realtime (安定版、qwen3-asr-flash-realtime-2025-10-27 のエイリアス)、qwen3-asr-flash-realtime-2025-10-27 (スナップショット版)

中国本土

中国本土のデプロイメントモードでは、エンドポイントとデータストレージは北京リージョンに配置されます。モデル推論のコンピューティングリソースは中国本土に限定されます。

以下のモデルを呼び出すには、北京リージョンの API キーを使用してください:

Qwen3-ASR-Flash-Realtime: qwen3-asr-flash-realtime (安定版、現在は qwen3-asr-flash-realtime-2025-10-27 のエイリアス) および qwen3-asr-flash-realtime-2025-10-27 (スナップショット版)

詳細については、「モデルリスト」をご参照ください。

モデルの選択

シナリオ

推奨モデル

理由

カスタマーサービスのインテリジェントな品質検査

qwen3-asr-flash-realtime、qwen3-asr-flash-realtime-2025-10-27

通話内容と顧客の感情をリアルタイムで分析し、オペレーターを支援し、サービス品質を監視します。

ライブストリーミング/ショートビデオ

ライブコンテンツのリアルタイム字幕を生成し、多言語の視聴者にリーチします。

オンライン会議/インタビュー

会議の発言をリアルタイムで記録し、テキスト要約を迅速に生成して、情報整理の効率を向上させます。

専門分野 (医療、法律など) の文字起こし

コンテキストバイアスをサポートし、ドメイン固有のホットワードを動的に注入してヒット率を向上させます。

詳細については、「モデルの特徴」をご参照ください。

クイックスタート

DashScope SDK

Java

  1. SDK のインストール。DashScope SDK のバージョンが 2.22.5 以降であることを確認してください。

  2. API キーの取得。コードにハードコーディングすることを避けるため、API キーを環境変数として設定します。

  3. サンプルコードの実行。

    import com.alibaba.dashscope.audio.omni.*;
    import com.alibaba.dashscope.exception.NoApiKeyException;
    import com.google.gson.JsonObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.sound.sampled.LineUnavailableException;
    import java.io.File;
    import java.io.FileInputStream;
    import java.util.Base64;
    import java.util.Collections;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicReference;
    
    public class Qwen3AsrRealtimeUsage {
        private static final Logger log = LoggerFactory.getLogger(Qwen3AsrRealtimeUsage.class);
        private static final int AUDIO_CHUNK_SIZE = 1024; // オーディオチャンクサイズ (バイト)
        private static final int SLEEP_INTERVAL_MS = 30;  // スリープ間隔 (ミリ秒)
    
        public static void main(String[] args) throws InterruptedException, LineUnavailableException {
            CountDownLatch finishLatch = new CountDownLatch(1);
    
            OmniRealtimeParam param = OmniRealtimeParam.builder()
                    .model("qwen3-asr-flash-realtime")
                    // 次の URL はシンガポールリージョン用です。北京リージョンのモデルを使用する場合は、URL を wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
                    .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                    // シンガポールリージョンと北京リージョンの API キーは異なります。API キーを取得するには、https://www.alibabacloud.com/help/ja/model-studio/get-api-key をご参照ください。
                    // 環境変数を設定していない場合は、次の行をご利用の Model Studio API キーに置き換えてください:.apikey("sk-xxx")
                    .apikey(System.getenv("DASHSCOPE_API_KEY"))
                    .build();
    
            OmniRealtimeConversation conversation = null;
            final AtomicReference<OmniRealtimeConversation> conversationRef = new AtomicReference<>(null);
            conversation = new OmniRealtimeConversation(param, new OmniRealtimeCallback() {
                @Override
                public void onOpen() {
                    System.out.println("connection opened");
                }
                @Override
                public void onEvent(JsonObject message) {
                    String type = message.get("type").getAsString();
                    switch(type) {
                        case "session.created":
                            System.out.println("start session: " + message.get("session").getAsJsonObject().get("id").getAsString());
                            break;
                        case "conversation.item.input_audio_transcription.completed":
                            System.out.println("transcription: " + message.get("transcript").getAsString());
                            finishLatch.countDown();
                            break;
                        case "input_audio_buffer.speech_started":
                            System.out.println("======VAD Speech Start======");
                            break;
                        case "input_audio_buffer.speech_stopped":
                            System.out.println("======VAD Speech Stop======");
                            break;
                        case "conversation.item.input_audio_transcription.text":
                            System.out.println("transcription: " + message.get("text").getAsString());
                            break;
                        default:
                            break;
                    }
                }
                @Override
                public void onClose(int code, String reason) {
                    System.out.println("connection closed code: " + code + ", reason: " + reason);
                }
            });
            conversationRef.set(conversation);
            try {
                conversation.connect();
            } catch (NoApiKeyException e) {
                throw new RuntimeException(e);
            }
    
            OmniRealtimeTranscriptionParam transcriptionParam = new OmniRealtimeTranscriptionParam();
            transcriptionParam.setLanguage("zh");
            transcriptionParam.setInputAudioFormat("pcm");
            transcriptionParam.setInputSampleRate(16000);
    
            OmniRealtimeConfig config = OmniRealtimeConfig.builder()
                    .modalities(Collections.singletonList(OmniRealtimeModality.TEXT))
                    .transcriptionConfig(transcriptionParam)
                    .build();
            conversation.updateSession(config);
    
            String filePath = "your_audio_file.pcm";
            File audioFile = new File(filePath);
            if (!audioFile.exists()) {
                log.error("Audio file not found: {}", filePath);
                return;
            }
    
            try (FileInputStream audioInputStream = new FileInputStream(audioFile)) {
                byte[] audioBuffer = new byte[AUDIO_CHUNK_SIZE];
                int bytesRead;
                int totalBytesRead = 0;
    
                log.info("Starting to send audio data from: {}", filePath);
    
                // オーディオデータをチャンクで読み取り、送信します
                while ((bytesRead = audioInputStream.read(audioBuffer)) != -1) {
                    totalBytesRead += bytesRead;
                    String audioB64 = Base64.getEncoder().encodeToString(audioBuffer);
                    // オーディオチャンクを会話に送信します
                    conversation.appendAudio(audioB64);
    
                    // リアルタイムのオーディオストリーミングをシミュレートするために、わずかな遅延を追加します
                    Thread.sleep(SLEEP_INTERVAL_MS);
                }
    
                log.info("Finished sending audio data. Total bytes sent: {}", totalBytesRead);
    
            } catch (Exception e) {
                log.error("Error sending audio from file: {}", filePath, e);
            }
    
            // session.finish を送信し、セッションが終了するのを待ってから接続を閉じます。
            conversation.endSession();
            log.info("Task finished");
    
            System.exit(0);
        }
    }

Python

  1. SDK のインストール。DashScope SDK のバージョンが 1.25.6 以降であることを確認してください。

  2. API キーの取得。コードにハードコーディングすることを避けるため、API キーを環境変数として設定します。

  3. サンプルコードの実行。

    import logging
    import os
    import base64
    import signal
    import sys
    import time
    import dashscope
    from dashscope.audio.qwen_omni import *
    from dashscope.audio.qwen_omni.omni_realtime import TranscriptionParams
    
    
    def setup_logging():
        """ロギングを設定します。"""
        logger = logging.getLogger('dashscope')
        logger.setLevel(logging.DEBUG)
        handler = logging.StreamHandler(sys.stdout)
        handler.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.propagate = False
        return logger
    
    
    def init_api_key():
        """API キーを初期化します。"""
        # シンガポールリージョンと北京リージョンの API キーは異なります。API キーを取得するには、https://www.alibabacloud.com/help/ja/model-studio/get-api-key をご参照ください。
        # 環境変数を設定していない場合は、次の行をご利用の Model Studio API キーに置き換えてください:dashscope.api_key = "sk-xxx"
        dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY', 'YOUR_API_KEY')
        if dashscope.api_key == 'YOUR_API_KEY':
            print('[Warning] Using placeholder API key, set DASHSCOPE_API_KEY environment variable.')
    
    
    class MyCallback(OmniRealtimeCallback):
        """リアルタイム認識のコールバックを処理します。"""
        def __init__(self, conversation):
            self.conversation = conversation
            self.handlers = {
                'session.created': self._handle_session_created,
                'conversation.item.input_audio_transcription.completed': self._handle_final_text,
                'conversation.item.input_audio_transcription.text': self._handle_stash_text,
                'input_audio_buffer.speech_started': lambda r: print('======Speech Start======'),
                'input_audio_buffer.speech_stopped': lambda r: print('======Speech Stop======')
            }
    
        def on_open(self):
            print('Connection opened')
    
        def on_close(self, code, msg):
            print(f'Connection closed, code: {code}, msg: {msg}')
    
        def on_event(self, response):
            try:
                handler = self.handlers.get(response['type'])
                if handler:
                    handler(response)
            except Exception as e:
                print(f'[Error] {e}')
    
        def _handle_session_created(self, response):
            print(f"Start session: {response['session']['id']}")
    
        def _handle_final_text(self, response):
            print(f"Final recognized text: {response['transcript']}")
    
        def _handle_stash_text(self, response):
            print(f"Got stash result: {response['stash']}")
    
    
    def read_audio_chunks(file_path, chunk_size=3200):
        """オーディオファイルをチャンクで読み取ります。"""
        with open(file_path, 'rb') as f:
            while chunk := f.read(chunk_size):
                yield chunk
    
    
    def send_audio(conversation, file_path, delay=0.1):
        """オーディオデータを送信します。"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"Audio file {file_path} does not exist.")
    
        print("Processing audio file... Press 'Ctrl+C' to stop.")
        for chunk in read_audio_chunks(file_path):
            audio_b64 = base64.b64encode(chunk).decode('ascii')
            conversation.append_audio(audio_b64)
            time.sleep(delay)
    
    def main():
        setup_logging()
        init_api_key()
    
        audio_file_path = "./your_audio_file.pcm"
        conversation = OmniRealtimeConversation(
            model='qwen3-asr-flash-realtime',
            # 次の URL はシンガポールリージョン用です。北京リージョンのモデルを使用する場合は、URL を wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
            url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime',
            callback=MyCallback(conversation=None)  # 一時的に None を渡し、後で注入します。
        )
    
        # コールバックに self を注入します。
        conversation.callback.conversation = conversation
    
        def handle_exit(sig, frame):
            print('Ctrl+C pressed, exiting...')
            conversation.close()
            sys.exit(0)
    
        signal.signal(signal.SIGINT, handle_exit)
    
        conversation.connect()
    
        transcription_params = TranscriptionParams(
            language='zh',
            sample_rate=16000,
            input_audio_format="pcm"
        )
    
        conversation.update_session(
            output_modalities=[MultiModality.TEXT],
            enable_input_audio_transcription=True,
            transcription_params=transcription_params
        )
    
        try:
            send_audio(conversation, audio_file_path)
            # session.finish を送信し、セッションが終了するのを待ってから接続を閉じます。
            conversation.end_session()
        except Exception as e:
            print(f"Error occurred: {e}")
        finally:
            conversation.close()
            print("Audio processing completed.")
    
    
    if __name__ == '__main__':
        main()

WebSocket API

次の例は、ローカルのオーディオファイルを送信し、WebSocket 接続を介して認識結果を取得する方法を示しています。

  1. API キーの取得:API キーの取得。セキュリティのため、API キーを環境変数として設定してください。

  2. コードの作成と実行:認証、接続、オーディオの送信、結果の受信の完全なフローを実装します。詳細については、「インタラクションフロー」をご参照ください。

    Python

    この例を実行する前に、次のコマンドを実行して依存関係をインストールしてください:

    pip uninstall websocket-client
    pip uninstall websocket
    pip install websocket-client

    サンプルコードのファイル名を websocket.py にしないでください。そうしないと、次のエラーが発生する可能性があります:AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?

    # pip install websocket-client
    import os
    import time
    import json
    import threading
    import base64
    import websocket
    import logging
    import logging.handlers
    from datetime import datetime
    
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    
    # シンガポールリージョンと北京リージョンの API キーは異なります。API キーを取得するには、https://www.alibabacloud.com/help/ja/model-studio/get-api-key をご参照ください。
    # 環境変数を設定していない場合は、次の行をご利用の Model Studio API キーに置き換えてください:API_KEY="sk-xxx"
    API_KEY = os.environ.get("DASHSCOPE_API_KEY", "sk-xxx")
    QWEN_MODEL = "qwen3-asr-flash-realtime"
    # 次はシンガポールリージョン用のベース URL です。北京リージョンのモデルを使用する場合は、ベース URL を wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
    baseUrl = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime"
    url = f"{baseUrl}?model={QWEN_MODEL}"
    print(f"Connecting to server: {url}")
    
    # 注:VAD モードでない場合、連続して送信されるオーディオの累積時間は 60 秒を超えてはなりません。
    enableServerVad = True
    is_running = True  # 実行フラグを追加します。
    
    headers = [
        "Authorization: Bearer " + API_KEY,
        "OpenAI-Beta: realtime=v1"
    ]
    
    def init_logger():
        formatter = logging.Formatter('%(asctime)s|%(levelname)s|%(message)s')
        f_handler = logging.handlers.RotatingFileHandler(
            "omni_tester.log", maxBytes=100 * 1024 * 1024, backupCount=3
        )
        f_handler.setLevel(logging.DEBUG)
        f_handler.setFormatter(formatter)
    
        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        console.setFormatter(formatter)
    
        logger.addHandler(f_handler)
        logger.addHandler(console)
    
    def on_open(ws):
        logger.info("Connected to server.")
    
        # セッション更新イベント。
        event_manual = {
            "event_id": "event_123",
            "type": "session.update",
            "session": {
                "modalities": ["text"],
                "input_audio_format": "pcm",
                "sample_rate": 16000,
                "input_audio_transcription": {
                    # 言語識別子、オプション。明確な言語情報がある場合は設定します。
                    "language": "zh"
                },
                "turn_detection": None
            }
        }
        event_vad = {
            "event_id": "event_123",
            "type": "session.update",
            "session": {
                "modalities": ["text"],
                "input_audio_format": "pcm",
                "sample_rate": 16000,
                "input_audio_transcription": {
                    "language": "zh"
                },
                "turn_detection": {
                    "type": "server_vad",
                    "threshold": 0.0,
                    "silence_duration_ms": 400
                }
            }
        }
        if enableServerVad:
            logger.info(f"Sending event: {json.dumps(event_vad, indent=2)}")
            ws.send(json.dumps(event_vad))
        else:
            logger.info(f"Sending event: {json.dumps(event_manual, indent=2)}")
            ws.send(json.dumps(event_manual))
    
    def on_message(ws, message):
        global is_running
        try:
            data = json.loads(message)
            logger.info(f"Received event: {json.dumps(data, ensure_ascii=False, indent=2)}")
            if data.get("type") == "session.finished":
                logger.info(f"Final transcript: {data.get('transcript')}")
                logger.info("Closing WebSocket connection after session finished...")
                is_running = False  # オーディオ送信スレッドを停止します。
                ws.close()
        except json.JSONDecodeError:
            logger.error(f"Failed to parse message: {message}")
    
    def on_error(ws, error):
        logger.error(f"Error: {error}")
    
    def on_close(ws, close_status_code, close_msg):
        logger.info(f"Connection closed: {close_status_code} - {close_msg}")
    
    def send_audio(ws, local_audio_path):
        time.sleep(3)  # セッションの更新が完了するのを待ちます。
        global is_running
    
        with open(local_audio_path, 'rb') as audio_file:
            logger.info(f"Start reading the file: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}")
            while is_running:
                audio_data = audio_file.read(3200)  # ~0.1s PCM16/16kHz
                if not audio_data:
                    logger.info(f"Finished reading the file: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}")
                    if ws.sock and ws.sock.connected:
                        if not enableServerVad:
                            commit_event = {
                                "event_id": "event_789",
                                "type": "input_audio_buffer.commit"
                            }
                            ws.send(json.dumps(commit_event))
                        finish_event = {
                            "event_id": "event_987",
                            "type": "session.finish"
                        }
                        ws.send(json.dumps(finish_event))
                    break
    
                if not ws.sock or not ws.sock.connected:
                    logger.info("The WebSocket is closed. Stop sending audio.")
                    break
    
                encoded_data = base64.b64encode(audio_data).decode('utf-8')
                eventd = {
                    "event_id": f"event_{int(time.time() * 1000)}",
                    "type": "input_audio_buffer.append",
                    "audio": encoded_data
                }
                ws.send(json.dumps(eventd))
                logger.info(f"Sending audio event: {eventd['event_id']}")
                time.sleep(0.1)  # リアルタイム収集をシミュレートします。
    
    # ロガーを初期化します。
    init_logger()
    logger.info(f"Connecting to WebSocket server at {url}...")
    
    local_audio_path = "your_audio_file.pcm"
    ws = websocket.WebSocketApp(
        url,
        header=headers,
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close
    )
    
    thread = threading.Thread(target=send_audio, args=(ws, local_audio_path))
    thread.start()
    ws.run_forever()

    Java

    この例を実行する前に、Java-WebSocket の依存関係をインストールしてください:

    Maven

    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.5.6</version>
    </dependency>

    Gradle

    implementation 'org.java-websocket:Java-WebSocket:1.5.6'
    import org.java_websocket.client.WebSocketClient;
    import org.java_websocket.handshake.ServerHandshake;
    import org.json.JSONObject;
    
    import java.net.URI;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.logging.*;
    
    public class QwenASRRealtimeClient {
    
        private static final Logger logger = Logger.getLogger(QwenASRRealtimeClient.class.getName());
        // シンガポールリージョンと北京リージョンの API キーは異なります。API キーを取得するには、https://www.alibabacloud.com/help/ja/model-studio/get-api-key をご参照ください。
        // 環境変数を設定していない場合は、次の行をご利用の Model Studio API キーに置き換えてください:private static final String API_KEY = "sk-xxx"
        private static final String API_KEY = System.getenv().getOrDefault("DASHSCOPE_API_KEY", "sk-xxx");
        private static final String MODEL = "qwen3-asr-flash-realtime";
    
        // VAD モードを使用するかどうかを制御します。
        private static final boolean enableServerVad = true;
    
        private static final AtomicBoolean isRunning = new AtomicBoolean(true);
        private static WebSocketClient client;
    
        public static void main(String[] args) throws Exception {
            initLogger();
    
            // 次はシンガポールリージョン用のベース URL です。北京リージョンのモデルを使用する場合は、ベース URL を wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
            String baseUrl = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime";
            String url = baseUrl + "?model=" + MODEL;
            logger.info("Connecting to server: " + url);
    
            client = new WebSocketClient(new URI(url)) {
                @Override
                public void onOpen(ServerHandshake handshake) {
                    logger.info("Connected to server.");
                    sendSessionUpdate();
                }
    
                @Override
                public void onMessage(String message) {
                    try {
                        JSONObject data = new JSONObject(message);
                        String eventType = data.optString("type");
    
                        logger.info("Received event: " + data.toString(2));
    
                        // finish イベントを受信したら、送信スレッドを停止し、接続を閉じます。
                        if ("session.finished".equals(eventType)) {
                            logger.info("Final transcript: " + data.optString("transcript"));
                            logger.info("Closing WebSocket connection after session finished...");
    
                            isRunning.set(false); // オーディオ送信スレッドを停止します。
                            if (this.isOpen()) {
                                this.close(1000, "ASR finished");
                            }
                        }
                    } catch (Exception e) {
                        logger.severe("Failed to parse message: " + message);
                    }
                }
    
                @Override
                public void onClose(int code, String reason, boolean remote) {
                    logger.info("Connection closed: " + code + " - " + reason);
                }
    
                @Override
                public void onError(Exception ex) {
                    logger.severe("Error: " + ex.getMessage());
                }
            };
    
            // リクエストヘッダーを追加します。
            client.addHeader("Authorization", "Bearer " + API_KEY);
            client.addHeader("OpenAI-Beta", "realtime=v1");
    
            client.connectBlocking(); // 接続が確立されるまでブロックします。
    
            // 認識するオーディオファイルのパスに置き換えます。
            String localAudioPath = "your_audio_file.pcm";
            Thread audioThread = new Thread(() -> {
                try {
                    sendAudio(localAudioPath);
                } catch (Exception e) {
                    logger.severe("Audio sending thread error: " + e.getMessage());
                }
            });
            audioThread.start();
        }
    
        /** セッション更新イベント (VAD の有効化/無効化)。*/
        private static void sendSessionUpdate() {
            JSONObject eventNoVad = new JSONObject()
                    .put("event_id", "event_123")
                    .put("type", "session.update")
                    .put("session", new JSONObject()
                            .put("modalities", new String[]{"text"})
                            .put("input_audio_format", "pcm")
                            .put("sample_rate", 16000)
                            .put("input_audio_transcription", new JSONObject()
                                    .put("language", "zh"))
                            .put("turn_detection", JSONObject.NULL) // 手動モード。
                    );
    
            JSONObject eventVad = new JSONObject()
                    .put("event_id", "event_123")
                    .put("type", "session.update")
                    .put("session", new JSONObject()
                            .put("modalities", new String[]{"text"})
                            .put("input_audio_format", "pcm")
                            .put("sample_rate", 16000)
                            .put("input_audio_transcription", new JSONObject()
                                    .put("language", "zh"))
                            .put("turn_detection", new JSONObject()
                                    .put("type", "server_vad")
                                    .put("threshold", 0.0)
                                    .put("silence_duration_ms", 400))
                    );
    
            if (enableServerVad) {
                logger.info("Sending event (VAD):\n" + eventVad.toString(2));
                client.send(eventVad.toString());
            } else {
                logger.info("Sending event (Manual):\n" + eventNoVad.toString(2));
                client.send(eventNoVad.toString());
            }
        }
    
        /** オーディオファイルストリームを送信します。*/
        private static void sendAudio(String localAudioPath) throws Exception {
            Thread.sleep(3000); // セッションの準備が整うのを待ちます。
            byte[] allBytes = Files.readAllBytes(Paths.get(localAudioPath));
            logger.info("Start reading the file.");
    
            int offset = 0;
            while (isRunning.get() && offset < allBytes.length) {
                int chunkSize = Math.min(3200, allBytes.length - offset);
                byte[] chunk = new byte[chunkSize];
                System.arraycopy(allBytes, offset, chunk, 0, chunkSize);
                offset += chunkSize;
    
                if (client != null && client.isOpen()) {
                    String encoded = Base64.getEncoder().encodeToString(chunk);
                    JSONObject eventd = new JSONObject()
                            .put("event_id", "event_" + System.currentTimeMillis())
                            .put("type", "input_audio_buffer.append")
                            .put("audio", encoded);
    
                    client.send(eventd.toString());
                    logger.info("Sending audio event: " + eventd.getString("event_id"));
                } else {
                    break; // 切断後の送信を避けます。
                }
    
                Thread.sleep(100); // リアルタイム送信をシミュレートします。
            }
    
            logger.info("Finished reading the file.");
    
            if (client != null && client.isOpen()) {
                // 非 VAD モードでは commit が必要です。
                if (!enableServerVad) {
                    JSONObject commitEvent = new JSONObject()
                            .put("event_id", "event_789")
                            .put("type", "input_audio_buffer.commit");
                    client.send(commitEvent.toString());
                    logger.info("Sent commit event for manual mode.");
                }
    
                JSONObject finishEvent = new JSONObject()
                        .put("event_id", "event_987")
                        .put("type", "session.finish");
                client.send(finishEvent.toString());
                logger.info("Sent finish event.");
            }
        }
    
        /** ロガーを初期化します。*/
        private static void initLogger() {
            logger.setLevel(Level.ALL);
            Logger rootLogger = Logger.getLogger("");
            for (Handler h : rootLogger.getHandlers()) {
                rootLogger.removeHandler(h);
            }
    
            Handler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.ALL);
            consoleHandler.setFormatter(new SimpleFormatter());
            logger.addHandler(consoleHandler);
        }
    }

    Node.js

    この例を実行する前に、次のコマンドを実行して依存関係をインストールしてください:

    npm install ws
    /**
     * Qwen-ASR Realtime WebSocket Client (Node.js 版)
     * 特徴:
     * - VAD モードと手動モードをサポート。
     * - session.update を送信してセッションを開始。
     * - input_audio_buffer.append オーディオチャンクを継続的に送信。
     * - 手動モードで input_audio_buffer.commit を送信。
     * - session.finish イベントを送信。
     * - session.finished イベントを受信後に接続を閉じる。
     */
    
    import WebSocket from 'ws';
    import fs from 'fs';
    
    // ===== 設定 =====
    // シンガポールリージョンと北京リージョンの API キーは異なります。API キーを取得するには、https://www.alibabacloud.com/help/ja/model-studio/get-api-key をご参照ください。
    // 環境変数を設定していない場合は、次の行をご利用の Model Studio API キーに置き換えてください:const API_KEY = "sk-xxx"
    const API_KEY = process.env.DASHSCOPE_API_KEY || 'sk-xxx';
    const MODEL = 'qwen3-asr-flash-realtime';
    const enableServerVad = true; // VAD モードの場合は true、手動モードの場合は false
    const localAudioPath = 'your_audio_file.pcm'; // PCM16、16 kHz オーディオファイルへのパス
    
    // 次はシンガポールリージョン用のベース URL です。北京リージョンのモデルを使用する場合は、ベース URL を wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
    const baseUrl = 'wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime';
    const url = `${baseUrl}?model=${MODEL}`;
    
    console.log(`Connecting to server: ${url}`);
    
    // ===== ステータス制御 =====
    let isRunning = true;
    
    // ===== 接続確立 =====
    const ws = new WebSocket(url, {
        headers: {
            'Authorization': `Bearer ${API_KEY}`,
            'OpenAI-Beta': 'realtime=v1'
        }
    });
    
    // ===== イベントバインディング =====
    ws.on('open', () => {
        console.log('[WebSocket] Connected to server.');
        sendSessionUpdate();
        // オーディオ送信スレッドを開始します。
        sendAudio(localAudioPath);
    });
    
    ws.on('message', (message) => {
        try {
            const data = JSON.parse(message);
            console.log('[Received Event]:', JSON.stringify(data, null, 2));
    
            // finish イベントを受信。
            if (data.type === 'session.finished') {
                console.log(`[Final Transcript] ${data.transcript}`);
                console.log('[Action] Closing WebSocket connection after session finished...');
                
                if (ws.readyState === WebSocket.OPEN) {
                    ws.close(1000, 'ASR finished');
                }
            }
        } catch (e) {
            console.error('[Error] Failed to parse message:', message);
        }
    });
    
    ws.on('close', (code, reason) => {
        console.log(`[WebSocket] Connection closed: ${code} - ${reason}`);
    });
    
    ws.on('error', (err) => {
        console.error('[WebSocket Error]', err);
    });
    
    // ===== セッション更新 =====
    function sendSessionUpdate() {
        const eventNoVad = {
            event_id: 'event_123',
            type: 'session.update',
            session: {
                modalities: ['text'],
                input_audio_format: 'pcm',
                sample_rate: 16000,
                input_audio_transcription: {
                    language: 'zh'
                },
                turn_detection: null
            }
        };
    
        const eventVad = {
            event_id: 'event_123',
            type: 'session.update',
            session: {
                modalities: ['text'],
                input_audio_format: 'pcm',
                sample_rate: 16000,
                input_audio_transcription: {
                    language: 'zh'
                },
                turn_detection: {
                    type: 'server_vad',
                    threshold: 0.0,
                    silence_duration_ms: 400
                }
            }
        };
    
        if (enableServerVad) {
            console.log('[Send Event] VAD Mode:\n', JSON.stringify(eventVad, null, 2));
            ws.send(JSON.stringify(eventVad));
        } else {
            console.log('[Send Event] Manual Mode:\n', JSON.stringify(eventNoVad, null, 2));
            ws.send(JSON.stringify(eventNoVad));
        }
    }
    
    // ===== オーディオファイルストリームの送信 =====
    function sendAudio(audioPath) {
        setTimeout(() => {
            console.log(`[File Read Start] ${audioPath}`);
            const buffer = fs.readFileSync(audioPath);
    
            let offset = 0;
            const chunkSize = 3200; // 約 0.1 秒の PCM16 オーディオ
    
            function sendChunk() {
                if (!isRunning) return;
                if (offset >= buffer.length) {
                    isRunning = false; // オーディオの送信を停止します。
                    console.log('[File Read End]');
                    if (ws.readyState === WebSocket.OPEN) {
                        if (!enableServerVad) {
                            const commitEvent = {
                                event_id: 'event_789',
                                type: 'input_audio_buffer.commit'
                            };
                            ws.send(JSON.stringify(commitEvent));
                            console.log('[Send Commit Event]');
                        }
    
                        const finishEvent = {
                            event_id: 'event_987',
                            type: 'session.finish'
                        };
                        ws.send(JSON.stringify(finishEvent));
                        console.log('[Send Finish Event]');
                    }
                    
                    return;
                }
    
                if (ws.readyState !== WebSocket.OPEN) {
                    console.log('[Stop] WebSocket is not open.');
                    return;
                }
    
                const chunk = buffer.slice(offset, offset + chunkSize);
                offset += chunkSize;
    
                const encoded = chunk.toString('base64');
                const appendEvent = {
                    event_id: `event_${Date.now()}`,
                    type: 'input_audio_buffer.append',
                    audio: encoded
                };
    
                ws.send(JSON.stringify(appendEvent));
                console.log(`[Send Audio Event] ${appendEvent.event_id}`);
    
                setTimeout(sendChunk, 100); // リアルタイム送信をシミュレートします。
            }
    
            sendChunk();
        }, 3000); // セッション設定が完了するのを待ちます。
    }
    

主な使用方法:コンテキストバイアス

コンテキストを提供することで、名前、地名、製品用語などのドメイン固有の語彙の認識を最適化できます。これにより、文字起こしの精度が大幅に向上します。この機能は、従来のホットワードソリューションよりも柔軟で強力です。

長さ制限:コンテキストの内容は 10,000 トークンを超えることはできません。

使用方法:

  • WebSocket API:session.update イベントで session.input_audio_transcription.corpus.text パラメーターを設定します。

  • Python SDK:corpus_text パラメーターを設定します。

  • Java SDK:corpusText パラメーターを設定します。

サポートされるテキストタイプには、以下が含まれますが、これらに限定されません:

  • ホットワードリスト (「ホットワード 1、ホットワード 2、ホットワード 3、ホットワード 4」など、さまざまな区切り文字形式)

  • 任意の形式と長さのテキスト段落または文章

  • 混合コンテンツ:単語リストと段落の任意の組み合わせ

  • 無関係または無意味なテキスト (文字化けを含む)。モデルは無関係なテキストに対して高いフォールトトレランスを持ち、ほとんど悪影響を受けません。

例:

特定のオーディオセグメントの正しい認識結果は、「投資銀行業界の専門用語を何か知っていますか?まず、9つの主要な外資系投資銀行、Bulge Bracket、BB...」であるべきです。

コンテキストバイアスなし

コンテキストバイアスがない場合、一部の投資銀行名が誤って認識されます。例えば、「Bird Rock」は「Bulge Bracket」であるべきです。

認識結果:「投資銀行業界の専門用語を何か知っていますか?まず、9つの主要な外資系投資銀行、Bird Rock、BB...」

コンテキストバイアスあり

コンテキストバイアスを使用すると、投資銀行名が正しく認識されます。

認識結果:「投資銀行業界の専門用語を何か知っていますか?まず、9つの主要な外資系投資銀行、Bulge Bracket、BB...」

上記の結果を達成するには、コンテキストに次のいずれかのコンテンツを追加します:

  • 単語リスト

    • 単語リスト 1:

      Bulge Bracket, Boutique, Middle Market, domestic securities firms
    • 単語リスト 2:

      Bulge Bracket Boutique Middle Market domestic securities firms
    • 単語リスト 3:

      ['Bulge Bracket', 'Boutique', 'Middle Market', 'domestic securities firms']
  • 自然言語:

    投資銀行のカテゴリーを大公開!
    最近、オーストラリアの多くの友人から、投資銀行とは一体何なのかと尋ねられます。今日はそれを説明します。留学生にとって、投資銀行は主に4つのカテゴリーに分けられます:Bulge Bracket、Boutique、Middle Market、そして国内証券会社です。
    Bulge Bracket 投資銀行:これらは私たちがよく言う9大投資銀行のことで、ゴールドマン・サックスやモルガン・スタンレーなどが含まれます。これらの大手銀行は、事業範囲も規模も巨大です。
    Boutique 投資銀行:これらの銀行は比較的小規模ですが、事業分野において高度に専門化しています。例えば、ラザードやエバーコアなどは、特定の分野で深い専門知識と経験を持っています。
    Middle Market 投資銀行:このタイプの銀行は主に中規模企業を対象とし、合併・買収や IPO などのサービスを提供します。大手銀行ほど大きくはありませんが、特定の市場で高い影響力を持っています。
    国内証券会社:中国市場の台頭に伴い、国内証券会社も国際市場でますます重要な役割を果たしています。
    さらに、役職や事業の区分もいくつかあり、関連する図表をご参照ください。この情報が、投資銀行をよりよく理解し、将来のキャリアに備えるのに役立つことを願っています!
  • 干渉を含む自然言語:次の例の名前のように、一部のテキストは認識内容とは無関係です。

    投資銀行のカテゴリが明らかに!
    最近、オーストラリアからの多くの友人から「投資銀行とは一体何なのか?」と聞かれました。今日はそれを説明します。留学生にとって、投資銀行は主に4つのカテゴリに分けられます:バルジ・ブランケット、ブティック、ミドルマーケット、および国内証券会社。
    バルジ・ブランケット投資銀行:これらは私たちがよく「9大投資銀行」と呼ぶもので、ゴールドマン・サックス、モルガン・スタンレーなどが含まれます。これらの大手銀行は、事業範囲と規模の両面で非常に巨大です。
    ブティック投資銀行:これらの銀行は比較的小規模ですが、事業分野において高度に特化しています。例えば、ラザール、エバーコアなどは、特定の分野において深い専門知識と経験を持っています。
    ミドルマーケット投資銀行:このタイプの銀行は主に中規模企業を対象としており、M&A(合併・買収)やIPO(新規公開)などのサービスを提供します。大手銀行ほど規模は大きくありませんが、特定の市場では高い影響力を持っています。
    国内証券会社:中国市場の台頭に伴い、国内証券会社も国際市場でますます重要な役割を果たしています。
    さらに、ポジションやビジネスのいくつかの部門があり、それらは関連チャートをご参照ください。この情報があなたが投資銀行業界をより深く理解し、将来のキャリアに向けて準備するのに役立つことを願っています!
    Wang Haoxuan, Li Zihan, Zhang Jingxing, Liu Xinyi, Chen Junjie, Yang Siyuan, Zhao Yutong, Huang Zhiqiang, Zhou Zimo, Wu Yajing, Xu Ruoxi, Sun Haoran, Hu Jinyu, Zhu Chenxi, Guo Wenbo, He Jingshu, Gao Yuhang, Lin Yifei, 
    Zheng Xiaoyan, Liang Bowen, Luo Jiaqi, Song Mingzhe, Xie Wanting, Tang Ziqian, Han Mengyao, Feng Yiran, Cao Qinxue, Deng Zirui, Xiao Wangshu, Xu Jiashu, 
    Cheng Yinuo, Yuan Zhiruo, Peng Haoyu, Dong Simiao, Fan Jingyu, Su Zijin, Lv Wenxuan, Jiang Shihan, Ding Muchen, 
    Wei Shuyao, Ren Tianyou, Jiang Yichen, Hua Qingyu, Shen Xinghe, Fu Jinyu, Yao Xingchen, Zhong Lingyu, Yan Licheng, Jin Ruoshui, Tao Ranting, Qi Shaoshang, Xue Zhilan, Zou Yunfan, Xiong Ziang, Bai Wenfeng, Yi Qianfan

API リファレンス

リアルタイム音声認識 - Qwen API リファレンス

モデルの特徴

特徴

qwen3-asr-flash-realtime, qwen3-asr-flash-realtime-2025-10-27

サポートされる言語

中国語 (標準中国語、四川語、閩南語、呉語、広東語)、英語、日本語、ドイツ語、韓国語、ロシア語、フランス語、ポルトガル語、アラビア語、イタリア語、スペイン語、ヒンディー語、インドネシア語、タイ語、トルコ語、ウクライナ語、ベトナム語、チェコ語、デンマーク語、フィリピン語、フィンランド語、アイスランド語、マレー語、ノルウェー語、ポーランド語、スウェーデン語

サポートされるオーディオフォーマット

pcm, opus

サンプリングレート

8 kHz, 16 kHz

チャンネル

モノラル

入力フォーマット

バイナリオーディオストリーム

オーディオサイズ/時間

無制限

感情認識

サポート 常時有効

不適切表現フィルター

非サポート

話者分離

非サポート

フィラーワードフィルター

非サポート

タイムスタンプ

非サポート

句読点予測

サポート 常時有効

コンテキストバイアス (ホットワードより強力)

サポート 設定可能

逆テキスト正規化 (ITN)

非サポート

音声アクティビティ検出 (VAD)

サポート 常時有効

レート制限 (RPS)

20

接続タイプ

Java/Python SDK、WebSocket API

料金

国際:$0.00009/秒

中国本土:$0.000047/秒