Reconhecimento de fala em tempo real
Discurso ao vivo para texto
Use para legendas ao vivo, reuniões online, bate-papo de voz, assistentes inteligentes e cenários semelhantes.
Visão geral
O serviço transmite áudio e retorna texto transcrito com baixa latência.
Além do WebSocket, Qwen-Audio-3.x-ASR-Flash-Streaming e Fun-ASR-Realtime Modelos também apoiam o protocolo de QOA. Para integração cliente-side que prioriza latência estável, resiliência em redes fracas, e integração de ruído e cancelamento de eco, o AOQ é recomendado. Para uma comparação de protocolo, veja [Realtime API overview] (/api-reference/realtime-api/overview).
- Reconhece mandarim chinês com alta precisão, além de cantonês, sicuanês e outros dialetos.
- Lida com ambientes acústicos complexos, com detecção automática de linguagem e filtragem inteligente de áudio não-falante.
- Reconhece uma série de estados emocionais, incluindo surpresa, calma, felicidade, tristeza, nojo, raiva e medo.
- Suporta palavras personalizadas para melhorar a precisão de reconhecimento para termos específicos.
- Suporta o aprimoramento do contexto para melhorar a precisão de reconhecimento passando em histórico de conversa ou termos de domínio.
- Horários de saída para produzir resultados de reconhecimento estruturados.
- Aceita taxas flexíveis de amostragem e vários formatos de áudio para se encaixar em diferentes ambientes de gravação.
Para cenários de lote, como transcrição de encontro, análise de chamadas, e geração de legendas, use [reconhecimento de fala em tempo real] (/developer-guides/speech/asr). Para orientação sobre escolher um modelo, veja [Speech-to-text] (/developer-guides/speech/speech-to-text-models).
Para a disponibilidade de modelos, idiomas suportados e comparação de características, veja [Modelos de voz para texto] (/developer-guides/speech/speech-to-text-models).
Pré-requisitos
- [Obter uma chave API] (/api-reference/preparation/api-keyE o define como uma variável de ambiente./api-reference/preparation/export-api-key-env).
- Para ligar para o serviço através do DashScope SDK, [instale o mais recente SDK] (/api-reference/preparation/install-sdk).
- Para se conectar a Qwen-Audio-3.x-ASR-Flash-Streaming Ou Fun-ASR-Realtime sobre o protocolo do AOQ, baixe e integre o cliente do AOQ SDK. Veja [AOQ SDK visão geral] (/api-reference/realtime-api/aoq-sdk-intro).
Começando.
Qwen-Audio-3.x-ASR-Flash-Streaming/Fun-ASR-Realtime
DashScope SDK
Para mais amostras de código, veja [GitHub] (https://github.com/aliyun/alibabacloud-bailian-speech-demo).
[Pegue uma chave API] (/api-reference/preparation/api-keyE o define como uma variável de ambiente./api-reference/preparation/export-api-key-env). Para usar o SDK, [instale-o] (/api-reference/preparation/install-sdk).
### - A disponibilidade do modelo.
| Modelo | Versão | Preço unitário | Quota livre(Nota) |
|---|---|---|---|
| fun-asr-realtime | Estável. | US$ 0.00009/second | 36 mil segundos (10 horas) Válido por 90 dias. |
- Mandarim, cantonês, Wu, Minnan, Hakka, Gan, Xiang e Jin. Também apoia sotaques mandarinos de Zhongyuan, sudoeste, Jilu, Jianghuai, Lanyin, Jiaoliao, nordeste, Pequim, e Hong Kong-Taiwan regiões - incluindo Henan, Shaanxi, Hubei, Sichuan, Chongqing, Yunnan, Guizhou, Guangdong, Guangxi, Hebei, Tianjin, Shandong, Anhui, Nanjing, Jiangsu, Hangzhou, Gansu, e Ningxia. Inglês e japonês também são apoiados.
- Taxa de amostragem 16 kHz
- ** Formatos de áudio**: pcm, wav, mp3, opus, spex, aac, amr
### Reconheça o discurso de um microfone.
Reconhecer fala de um microfone e texto de saída em tempo real, então as palavras aparecem como o falante fala.
```java Java
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.TargetDataLine;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
Constants.baseWebsocketApiUrl = "wss://maas.qwencloudapi.com/api-ws/v1/inference";
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new RealtimeRecognitionTask());
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
}
class RealtimeRecognitionTask implements Runnable {
@Override
public void run() {
RecognitionParam param = RecognitionParam.builder()
.model("qwen-audio-3.0-asr-flash-streaming")
// If you have not configured an environment variable, replace the following line with your API key: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.format("pcm")
.sampleRate(16000)
.build();
Recognition recognizer = new Recognition();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult result) {
if (result.isSentenceEnd()) {
System.out.println("Final Result: " + result.getSentence().getText());
} else {
System.out.println("Intermediate Result: " + result.getSentence().getText());
}
}
@Override
public void onComplete() {
System.out.println("Recognition complete");
}
@Override
public void onError(Exception e) {
System.out.println("RecognitionCallback error: " + e.getMessage());
}
};
try {
recognizer.call(param, callback);
// Create the audio format
AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
// Match the default recording device based on the format
TargetDataLine targetDataLine =
AudioSystem.getTargetDataLine(audioFormat);
targetDataLine.open(audioFormat);
// Start recording
targetDataLine.start();
ByteBuffer buffer = ByteBuffer.allocate(1024);
long start = System.currentTimeMillis();
// Record for 50s and perform real-time transcription
while (System.currentTimeMillis() - start < 50000) {
int read = targetDataLine.read(buffer.array(), 0, buffer.capacity());
if (read > 0) {
buffer.limit(read);
// Send the recorded audio data to the streaming recognition service
recognizer.sendAudioFrame(buffer);
buffer = ByteBuffer.allocate(1024);
// The recording rate is limited; sleep for a short while to prevent excessive CPU usage
Thread.sleep(20);
}
}
recognizer.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close the WebSocket connection after the task is complete
recognizer.getDuplexApi().close(1000, "bye");
}
System.out.println(
"[Metric] requestId: "
+ recognizer.getLastRequestId()
+ ", first package delay ms: "
+ recognizer.getFirstPackageDelay()
+ ", last package delay ms: "
+ recognizer.getLastPackageDelay());
}
}
```
```python Python
import os
import signal # for keyboard events handling (press "Ctrl+C" to terminate recording)
import sys
import dashscope
import pyaudio
from dashscope.audio.asr import *
mic = None
stream = None
# Set recording parameters
sample_rate = 16000 # sampling rate (Hz)
channels = 1 # mono channel
dtype = 'int16' # data type
format_pcm = 'pcm' # the format of the audio data
block_size = 3200 # number of frames per buffer
# Real-time speech recognition callback
class Callback(RecognitionCallback):
def on_open(self) -> None:
global mic
global stream
print('RecognitionCallback open.')
mic = pyaudio.PyAudio()
stream = mic.open(format=pyaudio.paInt16,
channels=1,
rate=16000,
input=True)
def on_close(self) -> None:
global mic
global stream
print('RecognitionCallback close.')
stream.stop_stream()
stream.close()
mic.terminate()
stream = None
mic = None
def on_complete(self) -> None:
print('RecognitionCallback completed.') # recognition completed
def on_error(self, message) -> None:
print('RecognitionCallback task_id: ', message.request_id)
print('RecognitionCallback error: ', message.message)
# Stop and close the audio stream if it is running
if 'stream' in globals() and stream.active:
stream.stop()
stream.close()
# Forcefully exit the program
sys.exit(1)
def on_event(self, result: RecognitionResult) -> None:
sentence = result.get_sentence()
if 'text' in sentence:
print('RecognitionCallback text: ', sentence['text'])
if RecognitionResult.is_sentence_end(sentence):
print(
'RecognitionCallback sentence end, request_id:%s, usage:%s'
% (result.get_request_id(), result.get_usage(sentence)))
def signal_handler(sig, frame):
print('Ctrl+C pressed, stop recognition ...')
# Stop recognition
recognition.stop()
print('Recognition stopped.')
print(
'[Metric] requestId: {}, first package delay ms: {}, last package delay ms: {}'
.format(
recognition.get_last_request_id(),
recognition.get_first_package_delay(),
recognition.get_last_package_delay(),
))
# Forcefully exit the program
sys.exit(0)
# main function
if __name__ == '__main__':
# If you have not configured an environment variable, replace the following line with your API key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.base_websocket_api_url='wss://maas.qwencloudapi.com/api-ws/v1/inference'
# Create the recognition callback
callback = Callback()
# Call recognition service by async mode, you can customize the recognition parameters, like model, format,
# sample_rate
recognition = Recognition(
model='qwen-audio-3.0-asr-flash-streaming',
format=format_pcm,
# 'pcm', 'wav', 'opus', 'speex', 'aac', 'amr', you can check the supported formats in the document
sample_rate=sample_rate,
# support 8000, 16000
semantic_punctuation_enabled=False,
callback=callback)
# Start recognition
recognition.start()
signal.signal(signal.SIGINT, signal_handler)
print("Press 'Ctrl+C' to stop recording and recognition...")
# Create a keyboard listener until "Ctrl+C" is pressed
while True:
if stream:
data = stream.read(3200, exception_on_overflow=False)
recognition.send_audio_frame(data)
else:
break
recognition.stop()
```
Antes de executar o exemplo Python, instale o kit de ferramentas de áudio de terceiros com `pip install pyaudio`Pyaudio requer a biblioteca do porto. Em Ubuntu/Debian: `sudo apt-get install libportaudio2 portaudio19-dev`. Em macOS: `brew install portaudio`.
### Reconheça um arquivo de áudio local.
Reconheça um arquivo de áudio local e dê o resultado. Isso se encaixa em cenários mais curtos, quase em tempo real, como conversas de bate-papo, comandos de voz, métodos de entrada de voz e busca de voz.
```java Java
import com.alibaba.dashscope.api.GeneralApi;
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.base.HalfDuplexParamBase;
import com.alibaba.dashscope.common.GeneralListParam;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.protocol.GeneralServiceOption;
import com.alibaba.dashscope.protocol.HttpMethod;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.protocol.StreamingMode;
import com.alibaba.dashscope.utils.Constants;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Constants.baseWebsocketApiUrl = "wss://maas.qwencloudapi.com/api-ws/v1/inference";
// In real applications, this method only needs to be executed once at the very beginning of the program; there is no need to execute it multiple times.
warmUp();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new RealtimeRecognitionTask(Paths.get(System.getProperty("user.dir"), "{YOUR_AUDIO_FILE}")));
executorService.shutdown();
// Wait for all tasks to complete.
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
public static void warmUp() {
try {
// Lightweight GET request to establish connection
GeneralServiceOption warmupOption = GeneralServiceOption.builder()
.protocol(Protocol.HTTP)
.httpMethod(HttpMethod.GET)
.streamingMode(StreamingMode.OUT)
.path("assistants")
.build();
warmupOption.setBaseHttpUrl(Constants.baseHttpApiUrl);
GeneralApi<HalfDuplexParamBase> api = new GeneralApi<>();
api.get(GeneralListParam.builder().limit(1L).build(), warmupOption);
} catch (Exception e) {
// Reset flag to allow retry if pre-warming failed
}
}
}
class RealtimeRecognitionTask implements Runnable {
private Path filepath;
public RealtimeRecognitionTask(Path filepath) {
this.filepath = filepath;
}
@Override
public void run() {
RecognitionParam param = RecognitionParam.builder()
.model("qwen-audio-3.1-asr-flash-streaming")
// If you have not configured an environment variable, replace the following line with your API key: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.format("wav")
.sampleRate(16000)
.build();
Recognition recognizer = new Recognition();
String threadName = Thread.currentThread().getName();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult message) {
if (message.isSentenceEnd()) {
System.out.println(TimeUtils.getTimestamp()+" "+
"[process " + threadName + "] Final Result:" + message.getSentence().getText());
} else {
System.out.println(TimeUtils.getTimestamp()+" "+
"[process " + threadName + "] Intermediate Result: " + message.getSentence().getText());
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp()+" "+"[" + threadName + "] Recognition complete");
}
@Override
public void onError(Exception e) {
System.out.println(TimeUtils.getTimestamp()+" "+
"[" + threadName + "] RecognitionCallback error: " + e.getMessage());
}
};
try {
recognizer.call(param, callback);
// Please replace the path with your audio file path
System.out.println(TimeUtils.getTimestamp()+" "+"[" + threadName + "] Input file_path is: " + this.filepath);
// Read file and send audio by chunks
FileInputStream fis = new FileInputStream(this.filepath.toFile());
byte[] allData = new byte[fis.available()];
int ret = fis.read(allData);
fis.close();
int sendFrameLength = 3200;
for (int i = 0; i * sendFrameLength < allData.length; i ++) {
int start = i * sendFrameLength;
int end = Math.min(start + sendFrameLength, allData.length);
ByteBuffer byteBuffer = ByteBuffer.wrap(allData, start, end - start);
recognizer.sendAudioFrame(byteBuffer);
Thread.sleep(100);
}
System.out.println(TimeUtils.getTimestamp()+" "+LocalDateTime.now());
recognizer.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close the WebSocket connection after the task is complete
recognizer.getDuplexApi().close(1000, "bye");
}
System.out.println(
"["
+ threadName
+ "][Metric] requestId: "
+ recognizer.getLastRequestId()
+ ", first package delay ms: "
+ recognizer.getFirstPackageDelay()
+ ", last package delay ms: "
+ recognizer.getLastPackageDelay());
}
}
```
```python Python
import os
import time
import dashscope
from dashscope.audio.asr import *
# If you have not set an environment variable, replace the next line with your API key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.base_websocket_api_url='wss://maas.qwencloudapi.com/api-ws/v1/inference'
from datetime import datetime
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
class Callback(RecognitionCallback):
def on_complete(self) -> None:
print(get_timestamp() + ' Recognition completed') # recognition complete
def on_error(self, message: str) -> None:
print('Error occurred: ', message)
print('Error details: ', message)
exit(0)
def on_event(self, result: RecognitionResult) -> None:
sentence = result.get_sentence()
if 'text' in sentence:
print(get_timestamp() + ' RecognitionCallback text: ', sentence['text'])
if RecognitionResult.is_sentence_end(sentence):
print(get_timestamp() +
'RecognitionCallback sentence end, request_id:%s, usage:%s'
% (result.get_request_id(), result.get_usage(sentence)))
callback = Callback()
recognition = Recognition(model='qwen-audio-3.1-asr-flash-streaming',
format='wav',
sample_rate=16000,
callback=callback)
try:
audio_data: bytes = None
f = open("{YOUR_AUDIO_FILE}", 'rb')
if os.path.getsize("{YOUR_AUDIO_FILE}"):
# Read all the file data into the buffer at once
file_buffer = f.read()
f.close()
print("Start Recognition")
recognition.start()
# Send 3200 bytes from the buffer at a time
buffer_size = len(file_buffer)
offset = 0
chunk_size = 3200
while offset < buffer_size:
# Calculate the size of the data chunk to send this time
remaining_bytes = buffer_size - offset
current_chunk_size = min(chunk_size, remaining_bytes)
# Extract the current data chunk from the buffer
audio_data = file_buffer[offset:offset + current_chunk_size]
# Send the audio data frame
recognition.send_audio_frame(audio_data)
# Update the offset
offset += current_chunk_size
# Add a delay to simulate real-time transmission
time.sleep(0.1)
recognition.stop()
else:
raise Exception(
'The supplied file was empty (zero bytes long)')
except Exception as e:
raise e
print(
'[Metric] requestId: {}, first package delay ms: {}, last package delay ms: {}'
.format(
recognition.get_last_request_id(),
recognition.get_first_package_delay(),
recognition.get_last_package_delay(),
))
```
WebSocket API
Os exemplos a seguir demonstram como enviar um arquivo de áudio local e recuperar resultados de reconhecimento através de uma conexão WebSocket nativa.
# Instalar dependências #
```bash Python
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client
```
```xml Maven
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
```
```gradle Gradle
implementation 'org.java-websocket:Java-WebSocket:1.5.6'
implementation 'org.json:json:20240303'
```
Não diga o nome do seu arquivo de exemplo. `websocket.py`. Este nome está em conflito com o `websocket` biblioteca e desencadeia o seguinte erro: `AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?`
```python Python
# pip install websocket-client
import os
import json
import time
import uuid
import threading
import websocket
# If you have not configured the environment variable, replace the line below with your API Key: api_key = "sk-xxx"
api_key = os.environ.get('DASHSCOPE_API_KEY')
url = 'wss://maas.qwencloudapi.com/api-ws/v1/inference'
audio_file = '{YOUR_AUDIO_FILE}' # Replace with your audio file path
TASK_ID = uuid.uuid4().hex[:32]
task_started = False
def send_run_task(ws):
run_task_message = {
'header': {
'action': 'run-task',
'task_id': TASK_ID,
'streaming': 'duplex'
},
'payload': {
'task_group': 'audio',
'task': 'asr',
'function': 'recognition',
'model': 'qwen-audio-3.1-asr-flash-streaming',
'parameters': {
'sample_rate': 16000,
'format': 'wav'
},
'input': {}
}
}
ws.send(json.dumps(run_task_message))
def send_finish_task(ws):
finish_task_message = {
'header': {
'action': 'finish-task',
'task_id': TASK_ID,
'streaming': 'duplex'
},
'payload': {
'input': {}
}
}
ws.send(json.dumps(finish_task_message))
def send_audio_stream(ws):
chunk_size = 3200 # 100ms @ 16kHz 16bit mono
try:
with open(audio_file, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
ws.send(chunk, opcode=websocket.ABNF.OPCODE_BINARY)
time.sleep(0.1)
print('Audio stream ended')
send_finish_task(ws)
except Exception as e:
print('Failed to read audio file: ', e)
ws.close()
def on_open(ws):
print('Connected to server')
send_run_task(ws)
def on_message(ws, data):
global task_started
message = json.loads(data)
event = message['header']['event']
if event == 'task-started':
print('Task started')
task_started = True
threading.Thread(target=send_audio_stream, args=(ws,), daemon=True).start()
elif event == 'result-generated':
print('Recognition result: ', message['payload']['output']['sentence']['text'])
if message['payload'].get('usage'):
print('Task billing duration (seconds): ', message['payload']['usage']['duration'])
elif event == 'task-finished':
print('Task finished')
ws.close()
elif event == 'task-failed':
print('Task failed: ', message['header'].get('error_message'))
ws.close()
else:
print('Unknown event: ', event)
def on_close(ws, close_status_code, close_msg):
if not task_started:
print('Task not started, closing connection')
def on_error(ws, error):
print('WebSocket error: ', error)
if __name__ == '__main__':
ws = websocket.WebSocketApp(
url,
header={'Authorization': f'bearer {api_key}'},
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
```
```java Java
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONObject;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
public class FunASRRealtimeClient {
// If you have not configured the environment variable, replace the line below with your API Key: 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 URL = "wss://maas.qwencloudapi.com/api-ws/v1/inference";
private static final String AUDIO_FILE = "{YOUR_AUDIO_FILE}";
private static final String MODEL = "qwen-audio-3.1-asr-flash-streaming";
private static final String TASK_ID = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
private static final AtomicBoolean taskStarted = new AtomicBoolean(false);
private static WebSocketClient client;
public static void main(String[] args) throws Exception {
client = new WebSocketClient(new URI(URL)) {
@Override
public void onOpen(ServerHandshake handshake) {
System.out.println("Connected to server");
sendRunTask();
}
@Override
public void onMessage(String data) {
JSONObject message = new JSONObject(data);
String event = message.getJSONObject("header").getString("event");
switch (event) {
case "task-started":
System.out.println("Task started");
taskStarted.set(true);
new Thread(FunASRRealtimeClient::sendAudioStream).start();
break;
case "result-generated":
JSONObject payload = message.getJSONObject("payload");
String text = payload.getJSONObject("output").getJSONObject("sentence").getString("text");
System.out.println("Recognition result: " + text);
if (payload.has("usage")) {
System.out.println("Task billing duration (seconds): " + payload.getJSONObject("usage").get("duration"));
}
break;
case "task-finished":
System.out.println("Task finished");
close();
break;
case "task-failed":
String errMsg = message.getJSONObject("header").optString("error_message");
System.err.println("Task failed: " + errMsg);
close();
break;
default:
System.out.println("Unknown event: " + event);
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
if (!taskStarted.get()) {
System.err.println("Task not started, closing connection");
}
}
@Override
public void onError(Exception ex) {
System.err.println("WebSocket error: " + ex.getMessage());
}
};
client.addHeader("Authorization", "bearer " + API_KEY);
client.connectBlocking();
}
private static void sendRunTask() {
JSONObject runTask = new JSONObject()
.put("header", new JSONObject()
.put("action", "run-task")
.put("task_id", TASK_ID)
.put("streaming", "duplex"))
.put("payload", new JSONObject()
.put("task_group", "audio")
.put("task", "asr")
.put("function", "recognition")
.put("model", MODEL)
.put("parameters", new JSONObject()
.put("sample_rate", 16000)
.put("format", "wav"))
.put("input", new JSONObject()));
client.send(runTask.toString());
}
private static void sendAudioStream() {
int chunkSize = 3200;
try {
byte[] audio = Files.readAllBytes(Paths.get(AUDIO_FILE));
int offset = 0;
while (offset < audio.length) {
int end = Math.min(offset + chunkSize, audio.length);
byte[] chunk = new byte[end - offset];
System.arraycopy(audio, offset, chunk, 0, end - offset);
client.send(ByteBuffer.wrap(chunk));
offset = end;
Thread.sleep(100);
}
System.out.println("Audio stream ended");
sendFinishTask();
} catch (Exception e) {
System.err.println("Failed to read audio file: " + e.getMessage());
client.close();
}
}
private static void sendFinishTask() {
JSONObject finishTask = new JSONObject()
.put("header", new JSONObject()
.put("action", "finish-task")
.put("task_id", TASK_ID)
.put("streaming", "duplex"))
.put("payload", new JSONObject()
.put("input", new JSONObject()));
client.send(finishTask.toString());
}
}
```
Qwen3-ASR-Flash-Realtime
### - A disponibilidade do modelo.
| Modelo | Versão | Preço unitário | Quota livre(Nota) |
|---|---|---|---|
| qwen3-asr-flash-realtime | Estável. | US$ 0.00009/second | 36 mil segundos (10 horas) Válido por 90 dias. |
DashScope SDK
Instale o SDK
[Instalar o SDK] (/api-reference/preparation/install-sdk). Certifique-se que a versão SDK DashScope seja 2.22.5 ou posterior (Java) ou 1.25.6 ou posterior (Python).
Get an API key
[Pegue uma chave API] (/api-reference/preparation/api-key). Defina a chave API como uma variável de ambiente para evitar codificação difícil em seu código.
Execute o código de amostra.
```java Java
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; // Audio chunk size in bytes
private static final int SLEEP_INTERVAL_MS = 30; // Sleep interval in milliseconds
public static void main(String[] args) throws InterruptedException, LineUnavailableException {
CountDownLatch finishLatch = new CountDownLatch(1);
OmniRealtimeParam param = OmniRealtimeParam.builder()
.model("qwen3-asr-flash-realtime")
.url("wss://maas.qwencloudapi.com/api-ws/v1/realtime")
// If you have not configured an environment variable, replace the following line with your API key: .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);
// Read and send audio data in chunks
while ((bytesRead = audioInputStream.read(audioBuffer)) != -1) {
totalBytesRead += bytesRead;
String audioB64 = Base64.getEncoder().encodeToString(audioBuffer);
// Send audio chunk to conversation
conversation.appendAudio(audioB64);
// Add small delay to simulate real-time audio streaming
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);
}
// Send session.finish, wait for the session to finish, and then close the connection.
conversation.endSession();
log.info("Task finished");
System.exit(0);
}
}
```
```python Python
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():
"""Configure 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():
"""Initialize the API key."""
# If you have not configured an environment variable, replace the following line with your API key: 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):
"""Handle real-time recognition callbacks."""
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):
"""Read the audio file in chunks."""
with open(file_path, 'rb') as f:
while chunk := f.read(chunk_size):
yield chunk
def send_audio(conversation, file_path, delay=0.1):
"""Send audio data."""
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='wss://maas.qwencloudapi.com/api-ws/v1/realtime',
callback=MyCallback(conversation=None) # Temporarily pass None and inject it later.
)
# Inject self into the callback.
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)
# Send session.finish, wait for the session to finish, and then close the connection.
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
O exemplo a seguir mostra como enviar um arquivo de áudio local e recuperar resultados de reconhecimento sobre uma conexão WebSocket.
Get an API key
[Pegue uma chave API] (/api-reference/preparation/api-key). Por segurança, defina a chave API como uma variável de ambiente.
Instalar dependências
?Python: ?
Antes de executar o exemplo, instale as dependências:
```bash
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client
```
Não nomeie o arquivo de código de exemplo. `websocket.py`. Caso contrário, o seguinte erro pode ocorrer: o módulo 'websocket' não tem nenhum atributo 'WebSocketApp'. Você quis dizer: 'WebSocket'?
Java:
Adicione a dependência Java-WebSocket:
```xml Maven
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.6</version>
</dependency>
```
```gradle Gradle
implementation 'org.java-websocket:Java-WebSocket:1.5.6'
```
#Node.js:#
```bash
npm install ws
```
Escreva e execute o código.
Implemente o fluxo completo de autenticação, conexão, envio de áudio e recebimento de resultados. Para mais informações, veja [ Fluxo de interação] (/developer-guides/speech/asr-realtime#interaction-flow-qwen3-asr-flash-realtime).
```python Python
# 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)
# If you have not configured an environment variable, replace the following line with your API key: API_KEY="sk-xxx"
API_KEY = os.environ.get("DASHSCOPE_API_KEY", "sk-xxx")
QWEN_MODEL = "qwen3-asr-flash-realtime"
baseUrl = "wss://maas.qwencloudapi.com/api-ws/v1/realtime"
url = f"{baseUrl}?model={QWEN_MODEL}"
print(f"Connecting to server: {url}")
# Note: If you are not in VAD mode, the cumulative duration of continuously sent audio should not exceed 60s.
enableServerVad = True
is_running = True # Add a running flag.
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.")
# Session update event.
event_manual = {
"event_id": "event_123",
"type": "session.update",
"session": {
"modalities": ["text"],
"input_audio_format": "pcm",
"sample_rate": 16000,
"input_audio_transcription": {
# Language identifier, optional. If you have clear language information, set it.
"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 # Stop the audio sending thread.
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) # Wait for the session update to complete.
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) # Simulate real-time collection.
# Initialize the logger.
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
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());
// If you have not configured an environment variable, replace the following line with your API key: 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";
// Controls whether to use VAD mode.
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();
String baseUrl = "wss://maas.qwencloudapi.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));
// When the finish event is received, stop the sending thread and close the connection.
if ("session.finished".equals(eventType)) {
logger.info("Final transcript: " + data.optString("transcript"));
logger.info("Closing WebSocket connection after session finished...");
isRunning.set(false); // Stop the audio sending thread.
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());
}
};
// Add request headers.
client.addHeader("Authorization", "Bearer " + API_KEY);
client.addHeader("OpenAI-Beta", "realtime=v1");
client.connectBlocking(); // Block until the connection is established.
// Replace with the path of the audio file to recognize.
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();
}
/** Session update event (enable/disable 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) // Manual mode.
);
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());
}
}
/** Send the audio file stream. */
private static void sendAudio(String localAudioPath) throws Exception {
Thread.sleep(3000); // Wait for the session to be ready.
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; // Avoid sending after disconnection.
}
Thread.sleep(100); // Simulate real-time sending.
}
logger.info("Finished reading the file.");
if (client != null && client.isOpen()) {
// A commit is required in non-VAD mode.
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.");
}
}
/** Initialize the logger. */
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);
}
}
```
```javascript Node.js
/**
* Qwen-ASR Realtime WebSocket Client (Node.js version)
* Features:
* - Supports VAD and Manual modes.
* - Sends session.update to start a session.
* - Continuously sends input_audio_buffer.append audio chunks.
* - Sends input_audio_buffer.commit in Manual mode.
* - Sends a session.finish event.
* - Closes the connection after receiving a session.finished event.
*/
import WebSocket from 'ws';
import fs from 'fs';
// ===== Configuration =====
// If you have not configured an environment variable, replace the following line with your API key: const API_KEY = "sk-xxx"
const API_KEY = process.env.DASHSCOPE_API_KEY || 'sk-xxx';
const MODEL = 'qwen3-asr-flash-realtime';
const enableServerVad = true; // true for VAD mode, false for Manual mode
const localAudioPath = 'your_audio_file.pcm'; // Path to the PCM16, 16 kHz audio file
const baseUrl = 'wss://maas.qwencloudapi.com/api-ws/v1/realtime';
const url = `${baseUrl}?model=${MODEL}`;
console.log(`Connecting to server: ${url}`);
// ===== Status Control =====
let isRunning = true;
// ===== Establish Connection =====
const ws = new WebSocket(url, {
headers: {
'Authorization': `Bearer ${API_KEY}`,
'OpenAI-Beta': 'realtime=v1'
}
});
// ===== Event Binding =====
ws.on('open', () => {
console.log('[WebSocket] Connected to server.');
sendSessionUpdate();
// Start the audio sending thread.
sendAudio(localAudioPath);
});
ws.on('message', (message) => {
try {
const data = JSON.parse(message);
console.log('[Received Event]:', JSON.stringify(data, null, 2));
// Received finish event.
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);
});
// ===== Session Update =====
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));
}
}
// ===== Send Audio File Stream =====
function sendAudio(audioPath) {
setTimeout(() => {
console.log(`[File Read Start] ${audioPath}`);
const buffer = fs.readFileSync(audioPath);
let offset = 0;
const chunkSize = 3200; // Approx. 0.1s of PCM16 audio
function sendChunk() {
if (!isRunning) return;
if (offset >= buffer.length) {
isRunning = false; // Stop sending audio.
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); // Simulate real-time sending.
}
sendChunk();
}, 3000); // Wait for session configuration to complete.
}
```
Recursos avançados
Pegue as datas.
Qwen-Audio-3.x-ASR-Flash-Streaming e Fun-ASR-Realtime Tempos de saída no nível de sentença** e nível de palavras** por padrão, que suporta alinhamento de legendas, destaque de palavras-chave, leitura de karaoke e cenários semelhantes.Qwen3-ASR-Flash-Realtime (qwen3-asr-flash-realtimeSe você precisar de datas, use Qwen-Audio-3.1-ASR-Flash-Streaming, Qwen-Audio-3.0-ASR-Flash-Streaming, ou Fun-ASR-Realtime. Para transcrição de arquivos, o modelo de transcrição de arquivos de gravação qwen3-asr-flash-filetrans suporta horários de nível de palavras. Para detalhes, veja [Reconhecimento de fala em tempo real] (/developer-guides/speech/asr).
As datas são devolvidas em milissegundos em dois níveis:
- ** Nível de sentença**:
payload.output.sentence.begin_timeepayload.output.sentence.end_timeMarque o início e o fim de uma frase completa no áudio. Em um resultado intermediário,end_timePode sernullE é preenchido com o valor final quando a sentença termina (sentence_end = true). - ** Nível de palavras**:
payload.output.sentence.wordsArray, onde cada elemento contémbegin_time,end_time,text(a palavra ou texto de caráter), epunctuation(a pontuação que segue a palavra, ou uma corda vazia se nenhuma).
O trecho a seguir mostra a estrutura de resposta:
{
"payload": {
"output": {
"sentence": {
"begin_time": 170,
"end_time": 920,
"text": "OK, I got it",
"sentence_end": true,
"words": [
{ "begin_time": 170, "end_time": 295, "text": "OK", "punctuation": "," },
{ "begin_time": 295, "end_time": 503, "text": "I", "punctuation": "" },
{ "begin_time": 503, "end_time": 711, "text": "got", "punctuation": "" },
{ "begin_time": 711, "end_time": 920, "text": "it", "punctuation": "" }
]
}
}
}
}
Os nomes de campo acima seguem os caminhos do WebSocket JSON. Diferentes SDKs expõem esses campos com suas próprias convenções de nomes (chaves dicionárias, propriedades de objetos, métodos getter, e assim por diante). Para o mapeamento completo do campo, veja [Eventos de servidor] (/api-reference/speech-recognition/fun-asr-realtime/server-events).
Reconhecimento de emoções
Qwen3-ASR-Flash-Realtime (qwen3-asr-flash-realtime) pode incluir o estado emocional do falante no resultado da transcrição. Está sempre ligado e não requer nenhuma configuração. A emoção é devolvida através de um nível superior emotion campo em ambos os conversation.item.input_audio_transcription.text e conversation.item.input_audio_transcription.completed eventos. O valor é uma das sete emoções de grãos finos: surprised, neutral, happy, sad, disgusted, angry, e fearful.
{
"type": "conversation.item.input_audio_transcription.text",
"emotion": "neutral",
"text": "The weather is nice today",
"stash": ""
}
Os nomes de campo acima seguem os caminhos do WebSocket JSON. Diferentes SDKs expõem esses campos com suas próprias convenções de nomes (chaves dicionárias, propriedades de objetos, métodos getter, e assim por diante). Para as definições de campo completo, restrições de valor, e exemplos, veja [Eventos de servidor] (/api-reference/speech-recognition/qwen-asr-realtime/server-events).
Indo ao vivo
Reutilizar conexões (WebSocket)
Conexões WebSocket para Qwen-Audio-3.x-ASR-Flash-Streaming e Fun-ASR-Realtime Pode ser reutilizado: depois que uma tarefa de reconhecimento terminar, você pode começar a próxima tarefa sem estabelecer uma nova conexão.
O cliente envia finish-task. Depois que o servidor retornar task-finishedO cliente pode enviar run-task novamente para começar uma nova tarefa.
- Espere o servidor devolver o
task-finishedevento antes de começar uma nova tarefa. - Diferentes tarefas em uma conexão reutilizada devem usar diferentes
task_idValores. - Quando uma tarefa falha, o servidor retorna um evento de erro e fecha a conexão. Essa conexão não pode ser reutilizada.
- Se nenhuma nova tarefa começar em 60 segundos após o fim da tarefa, a conexão será fechada automaticamente.
Qwen3-ASR-Flash-Realtime usa um modelo de sessão. Você deve fechar a conexão depois que cada sessão terminar, e a reutilização da conexão não é suportada.
Para os eventos de cada modelo, veja a correspondente [referência API] (#api-reference).
Melhores práticas de alta concorrência.
O SDK DashScope inclui um mecanismo de agrupamento integrado que reutiliza conexões WebSocket e objetos de reconhecimento, evitando o aumento da criação e destruição frequentes.
Só o Java SDK suporta essa característica.
View high-concurrency best practices
Pré-requisitos
- [Obter uma chave API] (/api-reference/preparation/api-keyE o define como uma variável de ambiente./api-reference/preparation/export-api-key-env).
- Instale um DashScope SDK que atenda ao requisito de versão. Nós recomendamos [instalando a última versão] (/api-reference/preparation/install-sdkJava SDK 2.16.9. ou posterior.
O Java SDK combina uma piscina de conexão com uma piscina de objetos personalizada para melhor desempenho:
- A piscina de conexão OkHttp3 integrada ao SDK gerencia e reutiliza as conexões subjacentes do WebSocket, reduzindo o aperto de mão. Está ativado por padrão.
- "Object Pool"
commons-pool2Ele mantém um conjunto deRecognitionobjetos cujas conexões já estão estabelecidas. Pegar um objeto emprestado da piscina remove a latência da configuração da conexão e reduz significativamente a latência do primeiro pacote.
Etapas de implementação
**1. Adicione dependências
Adicione Dashscope-sdk-java e Commons-pool2 ao seu arquivo de configuração de dependência, de acordo com sua ferramenta de construção.
Maven
Adicione as seguintes dependências dentro do `<dependencies>` -Etiqueta de `pom.xml`, então corra `mvn clean install` Ou `mvn compile` para atualizar as dependências.
```xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<!-- Replace 'the-latest-version' with 2.16.9 or later -->
<version>the-latest-version</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- Replace 'the-latest-version' with the latest version -->
<version>the-latest-version</version>
</dependency>
```
Gradle
Adicione as seguintes dependências dentro do `dependencies` Bloco de `build.gradle`, então corra `./gradlew build --refresh-dependencies` da raiz do projeto (`gradlew build --refresh-dependencies` - No Windows).
```groovy
dependencies {
// Replace 'the-latest-version' with 2.16.9 or later
implementation group: 'com.alibaba', name: 'dashscope-sdk-java', version: 'the-latest-version'
// Replace 'the-latest-version' with the latest version
implementation group: 'org.apache.commons', name: 'commons-pool2', version: 'the-latest-version'
}
```
**2. Configura a piscina de conexão
Configure os parâmetros de conjunto de conexão através de variáveis de ambiente:
| Variável ambiente | Descrição |
|---|---|
DASHSCOPE_CONNECTION_POOL_SIZE |
Tamanho da piscina de conexão. Recomendado: pelo menos o dobro da sua concorrência. Padrão: 32. |
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS |
Número máximo de pedidos de sincronização. Recomendado: o mesmo queDASHSCOPE_CONNECTION_POOL_SIZE. Padrão: 32. |
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST |
Número máximo de pedidos de sincronização por host. Recomendado: o mesmo queDASHSCOPE_CONNECTION_POOL_SIZE. Padrão: 32. |
**3. Configura o conjunto de objetos
Configure o tamanho do pool de objetos através de uma variável de ambiente:
| Variável ambiente | Descrição |
|---|---|
RECOGNITION_OBJECTPOOL_SIZE |
Tamanho da piscina de objetos. Recomendado: 1,5 a 2 vezes sua máxima concorrência. Predefinição: 500. |
- O tamanho da piscina de objetos (`RECOGNITION_OBJECTPOOL_SIZE` Deve ser menor ou igual ao tamanho da piscina de conexão.`DASHSCOPE_CONNECTION_POOL_SIZE`). Caso contrário, quando o objeto pede um objeto e o conjunto de conexão está cheio, os blocos de linha de chamada.
- O tamanho do conjunto de objetos não deve exceder o limite QPS da sua conta.
Crie o conjunto de objetos com o seguinte código:
class RecognitionObjectPool {
// For the complete example, see the full code below
public static GenericObjectPool<Recognition> getInstance() {
lock.lock();
if (recognitionGenericObjectPool == null) {
int objectPoolSize = getObjectivePoolSize();
RecognitionObjectFactory recognitionObjectFactory =
new RecognitionObjectFactory();
GenericObjectPoolConfig<Recognition> config =
new GenericObjectPoolConfig<>();
config.setMaxTotal(objectPoolSize);
config.setMaxIdle(objectPoolSize);
config.setMinIdle(objectPoolSize);
recognitionGenericObjectPool =
new GenericObjectPool<>(recognitionObjectFactory, config);
}
lock.unlock();
return recognitionGenericObjectPool;
}
}
**4. Pegue um objeto de reconhecimento emprestado da piscina de objetos.
Quando o número de objetos não retornados excede o limite de agrupamento de objetos, o sistema cria mais Recognition objetos. Esses novos objetos devem estabelecer uma nova conexão WebSocket e não podem ser reutilizados.
recognizer = RecognitionObjectPool.getInstance().borrowObject();
**5. Correr reconhecimento de fala
Chame o call Ou streamCall método do Recognition Objeção para fazer reconhecimento de fala.
**6. Devolva o objeto de reconhecimento
Depois que a tarefa de reconhecimento terminar, devolva o objeto de reconhecimento para que possa ser reutilizado. Não devolva objetos cujas tarefas estão inacabadas ou falhadas.
RecognitionObjectPool.getInstance().returnObject(recognizer);
Código completo.
package com.example.speech;
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.ApiKey;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import com.alibaba.dashscope.utils.Constants;
public class Main {
public static void checkoutEnv(String envName, int defaultSize) {
if (System.getenv(envName) != null) {
System.out.println("[ENV CHECK]: " + envName + " "
+ System.getenv(envName));
} else {
System.out.println("[ENV CHECK]: " + envName
+ " Using Default which is " + defaultSize);
}
}
public static void main(String[] args)
throws NoApiKeyException, InterruptedException {
Constants.baseWebsocketApiUrl = "wss://maas.qwencloudapi.com/api-ws/v1/inference";
checkoutEnv("DASHSCOPE_CONNECTION_POOL_SIZE", 32);
checkoutEnv("DASHSCOPE_MAXIMUM_ASYNC_REQUESTS", 32);
checkoutEnv("DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST", 32);
checkoutEnv(RecognitionObjectPool.RECOGNITION_OBJECTPOOL_SIZE_ENV,
RecognitionObjectPool.DEFAULT_OBJECT_POOL_SIZE);
int threadNums = 3;
String currentDir = System.getProperty("user.dir");
Path[] filePaths = {
Paths.get(currentDir, "{YOUR_AUDIO_FILE}"),
Paths.get(currentDir, "{YOUR_AUDIO_FILE}"),
Paths.get(currentDir, "{YOUR_AUDIO_FILE}"),
};
ExecutorService executorService = Executors.newFixedThreadPool(threadNums);
for (int i = 0; i < threadNums; i++) {
executorService.submit(new RealtimeRecognizeTask(filePaths));
}
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.MINUTES);
System.exit(0);
}
}
class RecognitionObjectFactory extends BasePooledObjectFactory<Recognition> {
public RecognitionObjectFactory() {
super();
}
@Override
public Recognition create() throws Exception {
return new Recognition();
}
@Override
public PooledObject<Recognition> wrap(Recognition obj) {
return new DefaultPooledObject<>(obj);
}
}
class RecognitionObjectPool {
public static GenericObjectPool<Recognition> recognitionGenericObjectPool;
public static String RECOGNITION_OBJECTPOOL_SIZE_ENV =
"RECOGNITION_OBJECTPOOL_SIZE";
public static int DEFAULT_OBJECT_POOL_SIZE = 500;
private static Lock lock = new java.util.concurrent.locks.ReentrantLock();
public static int getObjectivePoolSize() {
try {
Integer n = Integer.parseInt(
System.getenv(RECOGNITION_OBJECTPOOL_SIZE_ENV));
return n;
} catch (NumberFormatException e) {
return DEFAULT_OBJECT_POOL_SIZE;
}
}
public static GenericObjectPool<Recognition> getInstance() {
lock.lock();
if (recognitionGenericObjectPool == null) {
int objectPoolSize = getObjectivePoolSize();
System.out.println("RECOGNITION_OBJECTPOOL_SIZE: "
+ objectPoolSize);
RecognitionObjectFactory recognitionObjectFactory =
new RecognitionObjectFactory();
GenericObjectPoolConfig<Recognition> config =
new GenericObjectPoolConfig<>();
config.setMaxTotal(objectPoolSize);
config.setMaxIdle(objectPoolSize);
config.setMinIdle(objectPoolSize);
recognitionGenericObjectPool =
new GenericObjectPool<>(recognitionObjectFactory, config);
}
lock.unlock();
return recognitionGenericObjectPool;
}
}
class RealtimeRecognizeTask implements Runnable {
private static final Object lock = new Object();
private Path[] filePaths;
public RealtimeRecognizeTask(Path[] filePaths) {
this.filePaths = filePaths;
}
private static String getDashScopeApiKey() throws NoApiKeyException {
String dashScopeApiKey = null;
try {
ApiKey apiKey = new ApiKey();
dashScopeApiKey = ApiKey.getApiKey(null);
} catch (NoApiKeyException e) {
System.out.println("No API key found in environment.");
}
if (dashScopeApiKey == null) {
dashScopeApiKey = "your-dashscope-apikey";
}
return dashScopeApiKey;
}
public void runCallback() {
for (Path filePath : filePaths) {
RecognitionParam param = null;
try {
param = RecognitionParam.builder()
.model("fun-asr-realtime")
.format("pcm")
.sampleRate(16000)
.apiKey(getDashScopeApiKey())
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
Recognition recognizer = null;
final boolean[] hasError = {false};
try {
recognizer = RecognitionObjectPool.getInstance().borrowObject();
String threadName = Thread.currentThread().getName();
ResultCallback<RecognitionResult> callback =
new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult message) {
synchronized (lock) {
if (message.isSentenceEnd()) {
System.out.println("[process " + threadName
+ "] Fix:" + message.getSentence().getText());
} else {
System.out.println("[process " + threadName
+ "] Result: " + message.getSentence().getText());
}
}
}
@Override
public void onComplete() {
System.out.println("[" + threadName
+ "] Recognition complete");
}
@Override
public void onError(Exception e) {
System.out.println("[" + threadName
+ "] RecognitionCallback error: " + e.getMessage());
hasError[0] = true;
}
};
System.out.println("[" + threadName
+ "] Input file_path is: " + filePath);
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath.toFile());
} catch (Exception e) {
System.out.println("Error when loading file: " + filePath);
e.printStackTrace();
}
recognizer.call(param, callback);
// chunk size set to 100 ms for 16KHz sample rate
byte[] buffer = new byte[3200];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
ByteBuffer byteBuffer;
if (bytesRead < buffer.length) {
byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead);
} else {
byteBuffer = ByteBuffer.wrap(buffer);
}
recognizer.sendAudioFrame(byteBuffer);
Thread.sleep(100);
buffer = new byte[3200];
}
System.out.println("[" + threadName + "] send audio done");
recognizer.stop();
System.out.println("[" + threadName + "] asr task finished");
} catch (Exception e) {
e.printStackTrace();
hasError[0] = true;
}
if (recognizer != null) {
try {
if (hasError[0] == true) {
recognizer.getDuplexApi().close(1000, "bye");
RecognitionObjectPool.getInstance()
.invalidateObject(recognizer);
} else {
RecognitionObjectPool.getInstance()
.returnObject(recognizer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
runCallback();
}
}
Configuração recomendada
A seguinte configuração é baseada nos resultados dos testes de execução apenas do serviço de reconhecimento de fala em tempo real em servidores dos tamanhos especificados. A concorrência de uma máquina aqui significa o número de tarefas de reconhecimento de fala em tempo real que funcionam ao mesmo tempo (isto é, o número de linhas de trabalho).
| Tamanho da máquina | Max concorda com a máquina. | Tamanho da piscina de objetos | Tamanho da piscina de conexão |
|---|---|---|---|
| 4 núcleos, 8 GiB | 100 | 500 | 2000 |
| 8 núcleos, 16 GiB | 200 | 500 | 2000 |
| 16 núcleos, 32 GiB. | 400 | 500 | 2000 |
Gestão de recursos e manipulação de exceções
-
Você deve ligar para o meu pai.
GenericObjectPool.returnObject()devolver o objeto de reconhecimento para a piscina para reutilização.Não devolva um objeto de reconhecimento cuja tarefa está inacabada ou falhou.
-
Quando uma exceção jogada pelo SDK ou sua lógica de negócios interrompe a tarefa, você deve fechar a conexão WebSocket subjacente e invalidar o objeto na piscina para que ele não seja usado novamente.
// Close the connection
recognizer.getDuplexApi().close(1000, "bye");
// Invalidate the failed recognizer in the object pool
RecognitionObjectPool.getInstance().invalidateObject(recognizer);
- Não é necessário nenhum tratamento extra quando o serviço retorna um erro falhado.
Medição de aquecimento e latência.
Quando você avalia o desempenho, como latência de chamadas simultâneas para o DashScope Java SDK, se aquece completamente antes do teste formal.
** Mecanismo de reaproveitamento da conexão **
O DashScope Java SDK gerencia e reutiliza conexões WebSocket através de uma rede global de conexão singleton. O mecanismo funciona como segue:
- O SDK não cria conexões WebSocket na inicialização de serviço. As conexões são estabelecidas na primeira ligação.
- Após um pedido completo, a conexão fica na piscina por até 60 segundos para reutilização. Um novo pedido em 60 segundos reutiliza a conexão existente e evita outro aperto de mão. Uma conexão ociosa por mais de 60 segundos é fechada automaticamente para liberar recursos.
Por que o aquecimento importa?
Nos seguintes casos, o conjunto de conexões pode não ter conexão ativa reutilizável, então o pedido deve criar um novo:
- A inscrição acabou de começar e ainda não fez nenhuma ligação.
- O serviço está inativo há mais de 60 segundos e conexões combinadas foram encerradas.
Nesses casos, as primeiras ou primeiras solicitações desencadeiam a configuração completa do WebSocket (Aperto de mão TCP, negociação TLS e atualização de protocolo), então sua latência final a final é significativamente maior do que as solicitações que reutilizam uma conexão.
? Prática recomendada ?
Antes de testar a carga ou medir a latência, siga estes passos de aquecimento:
- Chamadas de emissão no nível de concorrência do teste formal com antecedência (por exemplo, por 1 a 2 minutos) para preencher o pool de conexão.
- Confirme que a conexão se estabeleceu e mantém conexões suficientes e comece a coletar dados formais de desempenho.
Melhore a precisão de reconhecimento.
- Escolha um modelo que corresponda à taxa de amostragem para 8 kHz de áudio telefônico, use um modelo de 8 kHz diretamente. Isso evita a perda de informação causada pela atualização para 16 kHz.
- Para substantivos, nomes e nomes específicos de sua empresa, você pode configurar palavras quentes ou realce de contexto para melhorar significativamente a precisão de reconhecimento. Para métodos de configuração detalhados e notas de uso, veja [Melhorar precisão de reconhecimento] (/developer-guides/speech/improve-recognition-accuracy).
- Use um microfone de alta qualidade e grave em um ambiente com uma alta relação sinal-ruído e sem eco. Na camada de aplicação, você pode integrar algoritmos como redução de ruído (por exemplo, RNNoise) e cancelamento de eco acústico (AEC) para pré-processamento.
- Para modelos multilingues, se você pode predeterminar a linguagem de áudio ao fazer uma chamada, ela ajuda o modelo a convergir e evitar confusão entre linguagens pronunciadas de forma semelhante, o que melhora a precisão.
Crie uma estratégia de tolerância a falhas.
- O cliente deve implementar a reconexão automática para lidar com o nervosismo da rede. A seguir, uma implementação de referência para o Python SDK:
- Excepções de captura:
on_errormétodo noCallback- Aula. OdashscopeSDK chama este método quando encontra um erro de rede ou outro problema. - Sinalize o estado: quando
on_erroré ativado, defina um sinal de reconexão. Em Python, você pode usarthreading.EventUma bandeira de sinal segura. - Reconectar a lógica principal em um
for(por exemplo, tente três vezes). Quando o sinal de reconexão é detectado, a rodada de reconhecimento atual é interrompida, os recursos são limpos, e depois de alguns segundos o loop é executado novamente para criar uma conexão novinha.
- Excepções de captura:
- Para manter uma conexão de longa duração com o servidor, defina o [bater do coração] (/api-reference/speech-recognition/fun-asr-realtime/python-sdk) parâmetro para
true. A conexão com o servidor fica aberta mesmo quando o áudio não contém som por muito tempo. - Quando você chama o modelo de API, observe o modelo [Limitação de Rate] (/developer-guides/administration/rate-limits) regras.
Uso principal: preconceito de contexto.Qwen3-ASR-Flash-Realtime)
Ao fornecer contexto, você pode otimizar o reconhecimento do vocabulário específico de domínio, como nomes, lugares e termos de produto.
O conteúdo do contexto não pode exceder 10 mil fichas.
Usagem:
- -Defina o
session.input_audio_transcription.corpus.textparâmetro no [sessão.atualização] (/api-reference/speech-recognition/qwen-asr-realtime/client-events) evento. - -Defina o
corpus_textparâmetro. - -Defina o
corpusTextparâmetro.
** Tipos de texto suportados:** Estes incluem, mas não se limitam a:
- Listas de palavras em vários formatos separadores, como Hotword 1, Hotword 2, Hotword 3, Hotword 4
- Texto parágrafos ou capítulos de qualquer formato e comprimento
- Qualquer combinação de listas de palavras e parágrafos
- Texto irrelevante ou sem sentido, incluindo texto confuso. A característica é altamente tolerante a falhas e quase nunca é afetada negativamente por texto irrelevante.
Exemplo:
A transcrição correta de um segmento de áudio deve ser: "Que jargão interno do círculo bancário de investimento você conhece? Primeiro, os nove maiores bancos de investimento estrangeiros, o Bulge Bracket, BB..."
| Sem o realce do contexto | Com o aprimoramento do contexto |
|---|---|
| Sem o aumento do contexto, alguns nomes de bancos de investimento podem ser mal reconhecidos. Por exemplo, "Pedra do Pássaro" deve ser "Bulge Bracket". Resultado do reconhecimento: "Que jargão interno do círculo bancário de investimento você conhece? Primeiro, os nove maiores bancos de investimento estrangeiros, Bird Rock, BB..." | Com o aprimoramento do contexto, os nomes de bancos de investimento são reconhecidos corretamente. Resultado do reconhecimento: "Que jargão interno do círculo bancário de investimento você conhece? Primeiro, os nove maiores bancos de investimento estrangeiros, o Bulge Bracket, BB..." |
Para alcançar o resultado acima, adicione qualquer um dos seguintes conteúdos ao contexto:
- Listas de palavras:
- Lista de palavras 1:
Bulge Bracket, Boutique, Middle Market, domestic securities firms
- Lista de palavras 2:
Bulge Bracket Boutique Middle Market domestic securities firms
- Lista de palavras 3:
['Bulge Bracket', 'Boutique', 'Middle Market', 'domestic securities firms']
- Linguagem natural:
Investment Banking Categories Revealed!
Recently, many friends from Australia have asked me, what exactly is an investment bank? Today, I'll explain it. For international students, investment banks can be mainly divided into four categories: Bulge Bracket, Boutique, Middle Market, and domestic securities firms.
Bulge Bracket Investment Banks: These are what we often call the nine major investment banks, including Goldman Sachs, Morgan Stanley, etc. These large banks are enormous in both business scope and scale.
Boutique Investment Banks: These banks are relatively small but highly specialized in their business areas. For example, Lazard, Evercore, etc., have deep professional knowledge and experience in specific fields.
Middle Market Investment Banks: This type of bank mainly serves medium-sized companies, providing services such as mergers and acquisitions, and IPOs. Although not as large as the major banks, they have a high influence in specific markets.
Domestic Securities Firms: With the rise of the Chinese market, domestic securities firms are also playing an increasingly important role in the international market.
In addition, there are some Position and business divisions, you can refer to the relevant charts. I hope this information helps you better understand investment banking and prepare for your future career!
- Linguagem natural com interferência: algum texto é irrelevante para o conteúdo do reconhecimento, como os nomes no exemplo abaixo.
Investment Banking Categories Revealed!
Recently, many friends from Australia have asked me, what exactly is an investment bank? Today, I'll explain it. For international students, investment banks can be mainly divided into four categories: Bulge Bracket, Boutique, Middle Market, and domestic securities firms.
Bulge Bracket Investment Banks: These are what we often call the nine major investment banks, including Goldman Sachs, Morgan Stanley, etc. These large banks are enormous in both business scope and scale.
Boutique Investment Banks: These banks are relatively small but highly specialized in their business areas. For example, Lazard, Evercore, etc., have deep professional knowledge and experience in specific fields.
Middle Market Investment Banks: This type of bank mainly serves medium-sized companies, providing services such as mergers and acquisitions, and IPOs. Although not as large as the major banks, they have a high influence in specific markets.
Domestic Securities Firms: With the rise of the Chinese market, domestic securities firms are also playing an increasingly important role in the international market.
In addition, there are some Position and business divisions, you can refer to the relevant charts. I hope this information helps you better understand investment banking and prepare for your future career!
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, Taoranting, Qi Shaoshang, Xue Zhilan, Zou Yunfan, Xiong Ziang, Bai Wenfeng, Yi Qianfan
Uso principal: filtragem sensível de palavras (Qwen-Audio-3.x-ASR-Flash-Streaming/Fun-ASR-Realtime)
Filtragem sensível de palavras substitui ou remove palavras sensíveis no resultado do reconhecimento. Use-o para inspeção de qualidade, conformidade de conteúdo, revisão de legendas e cenários semelhantes.
Modelos apoiados: Qwen-Audio-3.x-ASR-Flash-Streaming e Fun-ASR-Realtime Só.
Você pode configurar até 32 palavras sensíveis.
- Comportamento padrão: * Quando o
special_word_filterO parâmetro não é passado, nenhuma palavra sensível é filtrada.
** Como configurar:** special_word_filter É um objeto JSON com três subcampos:
-
filter_with_signed.word_listUma matriz de cordas que lista as palavras sensíveis para substituir por uma sequência de comprimento igual de*personagens. Por exemplo, com["test"]"Ajude-me a testá-lo" torna-se "Ajude-me a fazer isso". -
filter_with_empty.word_listUma matriz de cordas que lista as palavras sensíveis para remover inteiramente do resultado. Por exemplo, com["start"]"O jogo está prestes a começar" se torna "O jogo está prestes a começar". -
system_reserved_filterUm booleano que é o padrãofalse. Determina se a filtragem sensível de palavras está ativada.
Exemplo de configuração:
{
"special_word_filter": {
"filter_with_signed": {
"word_list": ["test"]
},
"filter_with_empty": {
"word_list": ["start", "occur"]
},
"system_reserved_filter": true
}
}
Diferentes SDKs expõem esses parâmetros com suas próprias convenções de nomes (chaves dicionárias, propriedades de objetos, métodos, etc.). Para o mapeamento completo do campo, veja a referência da API.
Configuração de segmentação VAD
A detecção de atividade vocal determina quando um segmento contínuo de fala termina, o que desencadeia o reconhecimento final do evento. Todas as três famílias de modelos permitem VAD do lado do servidor por padrão, mas seus nomes de parâmetros e afinação de granularidade diferem:
- **Qwen-Audio-3.1-ASR-Flash-Streaming Selecione o modelo VAD com
vad_model:near_meeting_16kPara áudio de campo próximo oufar_field_meeting_16kPara áudio de campo distante (padrão). - **Qwen-Audio-3.0-ASR-Flash-Streaming / Fun-ASR-Realtime - Configurado através de
max_sentence_silence(o limiar de silêncio VAD para segmentação, em milissegundos). Quando o silêncio após um segmento de fala excede esse limiar, o sistema trata a sentença como completa. - **Qwen3-ASR-Flash-RealtimeConfigurado através de
session.turn_detection, que incluisilence_duration_ms(o limite de duração do silêncio que termina uma volta quando ultrapassado; servidor padrão800, com400recomendado para conversas e cenários de bate-papo que precisam de segmentação rápida) ethreshold(Sensitividade de detecção de VAD; padrão do servidor0.2). Qwen3-ASR-Flash-Realtime Também suporta o modo manual, que desativa VAD e usa o commit do lado do cliente para segmentação. Para detalhes, veja a seção de fluxo de interação abaixo.
Nomes dos parâmetros variam de acordo com o protocolo: o mesmo conceito é chamado max_sentence_silence Em Qwen-Audio-3.0-ASR-Flash-Streaming / Fun-ASR-Realtime - Paraformer, e silence_duration_ms Em Qwen3-ASR-Flash-Realtime. Para as definições de campo completo, veja a referência da API abaixo.
Reconhecimento de emoções
Qwen3-ASR-Flash-Realtime e alguns modelos Paraformer podem incluir o estado emocional do falante no resultado da transcrição, mas os dois diferem na granularidade de saída e em como o recurso é habilitado.
**Qwen3-ASR-Flash-Realtime (qwen3-asr-flash-realtime Sempre ligado, nenhuma configuração necessária. A emoção é devolvida através de um nível superior emotion campo em ambos os conversation.item.input_audio_transcription.text e conversation.item.input_audio_transcription.completed eventos. O valor é uma das sete emoções de grãos finos: surprised, neutral, happy, sad, disgusted, angry, e fearful.
{
"type": "conversation.item.input_audio_transcription.text",
"emotion": "neutral",
"text": "The weather is nice today",
"stash": ""
}
Referência da API
Qwen-Audio-3.x-ASR-Flash-Streaming/Fun-ASR-Realtime
- [Qwen-Audio-3.x-ASR-Flash-Streaming/Fun-ASR-Realtime REFERÊNCIA DA API DE RECONHECIMENTO DE LOGAÇÃO EM TEMPO Real/api-reference/speech-recognition/fun-asr-realtime/python-sdk)
- [AOQ Cliente SDK] (/api-reference/realtime-api/aoq-sdk-intro(para Qwen-Audio-3.x-ASR-Flash-Streaming/Fun-ASR-Realtime)
Qwen3-ASR-Flash-Realtime
[Referência API de reconhecimento de fala em tempo real] (/api-reference/speech-recognition/qwen-asr-realtime/python-sdk)
Fluxo de interação (Qwen3-ASR-Flash-Realtime)
Qwen Reconhecimento de fala em tempo real transmite áudio sobre WebSocket. Dois modos estão disponíveis: modo VAD (padrão) e [Modo manual] (#manual-mode).
URL
Substituir <model_name> com seu [modelo] (/developer-guides/speech/speech-to-text-models) nome.
wss://maas.qwencloudapi.com/api-ws/v1/realtime?model=<model_name>
Cabeçalhos
"Authorization": "Bearer $DASHSCOPE_API_KEY"
Modo VAD (padrão)
O servidor detecta limites de fala e segmenta frases. O cliente transmite áudio, e o servidor retorna os resultados quando cada frase termina. Melhor para conversas e transcrição de encontros.
** Ativar: ** Set session.turn_detection Em session.update.
[Modo de interação VAD] (https://help-static-aliyun-doc.aliyuncs.com/assets/img/en-US/5305458671/CAEQUxiBgMDxw_SV3hkiIDIxZDVlODkyYWRiNDQ0YWJiNWFkYjZhNTgwMzc1MjUw5812623_20251022102739.334.svg)
-
O cliente envia
input_audio_buffer.appendpara adicionar áudio ao buffer. -
O servidor retorna.
input_audio_buffer.speech_started](/api-reference/speech-recognition/qwen-asr-realtime/server-events) quando detecta a fala.Se o cliente enviar [
session.finish](/api-reference/speech-recognition/qwen-asr-realtime/client-events Antes deste evento, o servidor retorna.session.finished](/api-reference/speech-recognition/qwen-asr-realtime/server-events) e o cliente deve desconectar. -
O cliente continua enviando
input_audio_buffer.append](/api-reference/speech-recognition/qwen-asr-realtime/client-events). -
Depois que todo o áudio é enviado, o cliente envia
session.finishpara terminar a sessão. -
O servidor retorna.
input_audio_buffer.speech_stopped](/api-reference/speech-recognition/qwen-asr-realtime/server-events) quando detecta o fim da fala. -
O servidor retorna.
input_audio_buffer.committed](/api-reference/speech-recognition/qwen-asr-realtime/server-events). -
O servidor retorna.
conversation.item.created](/api-reference/speech-recognition/qwen-asr-realtime/server-events). -
O servidor retorna.
conversation.item.input_audio_transcription.text](/api-reference/speech-recognition/qwen-asr-realtime/server-events) com resultados de transcrição em tempo real. -
O servidor retorna.
conversation.item.input_audio_transcription.completed](/api-reference/speech-recognition/qwen-asr-realtime/server-events) com o resultado final da transcrição. -
O servidor retorna.
session.finished](/api-reference/speech-recognition/qwen-asr-realtime/server-events) quando o reconhecimento se completa. O cliente deve então desconectar.
Modo manual
O cliente controla a segmentação da sentença enviando áudio para uma sentença completa, e depois enviando input_audio_buffer.commit. Melhor quando o cliente conhece limites de sentenças, por exemplo, em mensagens de voz de aplicativo de chat.
** Ativar: ** Set session.turn_detection para null Em session.update.
[Modo manual de interação flui] (https://help-static-aliyun-doc.aliyuncs.com/assets/img/en-US/5305458671/CAEQUxiBgID0ovyV3hkiIGQyZGZiNDJkMDVhOTQxYjBiMmM2OWJjY2M3MjFjNGVh5812623_20251022102739.334.svg)
-
O cliente envia
input_audio_buffer.appendpara adicionar áudio ao buffer. -
O cliente envia
input_audio_buffer.commitpara criar uma nova mensagem de usuário. -
O cliente envia
session.finishpara terminar a sessão. -
O servidor retorna.
input_audio_buffer.committed](/api-reference/speech-recognition/qwen-asr-realtime/server-events). -
O servidor retorna.
conversation.item.input_audio_transcription.text](/api-reference/speech-recognition/qwen-asr-realtime/server-events) com resultados de transcrição em tempo real. -
O servidor retorna.
conversation.item.input_audio_transcription.completed](/api-reference/speech-recognition/qwen-asr-realtime/server-events) com o resultado final da transcrição. -
O servidor retorna.
session.finished](/api-reference/speech-recognition/qwen-asr-realtime/server-events) quando o reconhecimento se completa. O cliente deve então desconectar.
FAQ
Que formatos de áudio é o suporte de reconhecimento de fala em tempo real?
Qwen-Audio-3.x-ASR-Flash-Streaming e Fun-ASR-Realtime Suporte Pcm, wav, mp3, opus, spex, aac, e amr. Qwen3-ASR-Flash-Realtime, use pcm ou opus ** Outros formatos, como wav, aac, e amr passar o session.update camada de validação, mas a decodificação do lado do servidor pode falhar. Confirme que o fluxo de áudio usa um formato recomendado antes de enviá-lo.
Qual a diferença entre o SDK e a API do WebSocket, e como eu escolho?
O DashScope SDK encapsula o gerenciamento de conexão WebSocket, autenticação, reconexão e outros detalhes, o que o torna adequado para uma integração rápida. Conectar-se à API WebSocket dá-lhe controle mais fino e combina com linguagens de programação que o SDK não cobre ou cenários que precisam de gerenciamento de conexão personalizado. Nós recomendamos o SDK.
Como melhoro a precisão de reconhecimento para substantivos adequados?
Use palavras quentes ou aprimoramento de contexto. Para métodos de configuração detalhados e notas de uso, veja [Melhorar precisão de reconhecimento] (/developer-guides/speech/improve-recognition-accuracy).
O que devo fazer quando a conexão cair com frequência?
Implemente a reconexão do lado do cliente e habilite o parâmetro batimento cardíaco (heartbeat=true) para evitar que a conexão caia quando não há áudio por um longo tempo. Para a estratégia de tolerância à falha, veja#set-up-a-fault-tolerance-strategy).