Skip to main content

AI 语音对话(文字 / 语音输入)

通过 WebSocket 与 AI 进行实时语音对话,支持文字输入语音输入两种方式,AI 以语音形式回复。每次连接为独立的一轮对话。


接口信息

  • 接口路径/api/v1/audio/conversation/stream-text
  • 通信协议:WebSocket
  • 认证方式:URL 参数 api_key

连接示例

const ws = new WebSocket(
'wss://api.vuilabs.cn/api/v1/audio/conversation/stream-text?api_key=YOUR_KEY'
);
ws.binaryType = 'arraybuffer';

认证参数

参数名类型必填说明
api_keystring应用密钥,拼在 WebSocket URL 的 query 参数中

注意:api_key 用于验证账户信息,进入 控制台 → 项目空间,选择对应项目,左侧导航进入「API Key 管理」即可创建/查看。


一、客户端发送消息

1. Meta 消息(必须,连接后第一条)

连接建立后立即以 JSON 文本消息发送会话元信息:

{
"meta": {
"session_id": "",
"voice_id": "qinyao",
"audio_format": "pcm16",
"text_input": "你好,请介绍一下你自己"
}
}

请求参数

参数名类型必填说明
session_idstring会话 ID;每次连接均为全新一轮,留空即可
voice_idstring角色 ID,见下方角色列表
audio_formatstring音频格式,固定填 pcm16(16kHz / 16bit / 单声道)
text_inputstring文字输入时填写内容;留空则进入语音输入模式

2. 音频帧(语音输入模式)

text_input 留空时,发送 PCM 二进制消息

属性
采样率16000 Hz
位深16-bit Signed Little-Endian
声道数1(单声道)
建议帧长约 100ms

说完后发送结束标记:

{ "is_final": true }

二、推送事件

session_created — 会话创建

{ "type": "session_created", "session_id": "abc123..." }
字段类型说明
session_idstring本轮会话 ID

asr_partial — 实时识别中间结果(语音输入模式)

{ "type": "asr_partial", "asr_text": "你好" }
字段类型说明
asr_textstring当前识别到的文本(可能不完整)

asr_final — 识别最终结果(语音输入模式)

{ "type": "asr_final", "asr_text": "你好,请介绍一下你自己" }
字段类型说明
asr_textstring最终识别文本

llm_first_token / llm_token — AI 回复文本(流式)

{ "type": "llm_first_token", "first_token": "你" }
{ "type": "llm_token", "token": "好" }
字段类型说明
first_tokenstring首个生成字符(llm_first_token 事件)
tokenstring后续字符(llm_token 事件)

按顺序拼接即为完整回复文本。

音频(二进制消息)

MP3 格式音频分片,多帧顺序到达,可边收边播放:

ws.onmessage = (event) => {
if (event.data instanceof ArrayBuffer) {
playAudioChunk(event.data); // 直接入队播放
}
};
属性
格式MP3
编码原始二进制(非 Base64)

done — 本轮对话完成

{
"type": "done",
"reply_text": "你好,我是沁瑶……",
"audio_duration_sec": 5
}
字段类型说明
reply_textstringAI 回复完整文本
audio_duration_secnumberAI 回复音频时长(秒),用于计费

error — 错误事件

{ "type": "error", "message": "错误描述" }
字段类型说明
messagestring错误描述

三、代码示例

文字输入模式

const ws = new WebSocket(
'wss://api.vuilabs.cn/api/v1/audio/conversation/stream-text?api_key=YOUR_KEY'
);
ws.binaryType = 'arraybuffer';

const audioQueue = [];
let isPlaying = false;

ws.addEventListener('open', () => {
ws.send(JSON.stringify({
meta: {
session_id: '',
voice_id: 'qinyao',
audio_format: 'pcm16',
text_input: '你好,请介绍一下你自己',
},
}));
});

ws.addEventListener('message', (event) => {
if (event.data instanceof ArrayBuffer) {
audioQueue.push(event.data);
if (!isPlaying) playNext();
return;
}
const msg = JSON.parse(event.data);
if (msg.type === 'done') {
console.log('AI 回复:', msg.reply_text);
ws.close();
} else if (msg.type === 'error') {
console.error(msg.message);
ws.close();
}
});

function playNext() {
if (!audioQueue.length) { isPlaying = false; return; }
isPlaying = true;
const url = URL.createObjectURL(new Blob([audioQueue.shift()], { type: 'audio/mp3' }));
const audio = new Audio(url);
audio.onended = () => { URL.revokeObjectURL(url); playNext(); };
audio.play();
}

语音输入模式

const ws = new WebSocket(
'wss://api.vuilabs.cn/api/v1/audio/conversation/stream-text?api_key=YOUR_KEY'
);
ws.binaryType = 'arraybuffer';

ws.addEventListener('open', async () => {
// text_input 留空 → 语音模式
ws.send(JSON.stringify({
meta: { session_id: '', voice_id: 'qinyao', audio_format: 'pcm16', text_input: '' },
}));

const stream = await navigator.mediaDevices.getUserMedia({
audio: { sampleRate: 16000, channelCount: 1 },
});
const audioCtx = new AudioContext({ sampleRate: 16000 });
const source = audioCtx.createMediaStreamSource(stream);
const processor = audioCtx.createScriptProcessor(2048, 1, 1);

processor.onaudioprocess = (e) => {
if (ws.readyState !== WebSocket.OPEN) return;
const input = e.inputBuffer.getChannelData(0);
const buf = new ArrayBuffer(input.length * 2);
const view = new DataView(buf);
for (let i = 0; i < input.length; i++) {
const s = Math.max(-1, Math.min(1, input[i]));
view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
ws.send(buf);
};

source.connect(processor);
processor.connect(audioCtx.destination);

// 说完后停止录音并发结束标记
setTimeout(() => {
processor.disconnect();
stream.getTracks().forEach(t => t.stop());
ws.send(JSON.stringify({ is_final: true }));
}, 5000); // 示例:录制 5 秒
});

四、计费说明

具体计费规则、价格和免费额度请参见 产品计费


五、角色列表

通过 GET /api/v1/audio/characters 获取完整列表。

voice_id名称
qinyao沁瑶
xiyue曦月
ruoxi若兮
bocen柏辰
daisyDaisy
fionaFiona

六、错误码

错误码说明
0成功
20001未授权,请检查 api_key
20003参数错误
40011余额不足
50000服务异常