本文介绍了如何使用大模型网关平台预置的语音对话智能体。
大模型网关平台预置了一个语音对话智能体。该智能体适用于语音对话场景,具备以下能力:
语音对话智能体支持两种使用模式:单独使用、与您在 Coze 平台搭建的智能体组合使用。
参考调用第三方智能体,完成第三方智能体调用准备工作,具体包括:
完成以上操作后,网关访问密钥便可用于调用您的 Coze 智能体。
编辑网关访问密钥,在 模型选择 中添加 语音对话智能体(渠道类型 为 平台预置智能体)
完成该操作后,网关访问密钥便可用于调用您的 Coze 智能体和平台预置的语音对话智能体。
参考查看密钥(API Key),获取网关访问密钥的 API key。
调用 Realtime API 实现与智能体进行语音对话。关于 API 的说明,请参见 Reamtime API。
Realtime API 是一个有状态的、基于事件的 API,通过 WebSocket 进行通信。您可以使用 Realtime API 与大模型网关的预置语音对话智能体构建语音对话。目前,Realtime API 支持音频作为输入和输出。
注意
Realtime API 仍处于测试阶段。
与 RealTime API 建立 WebSocket 连接需要以下参数:
单独使用语音对话智能体(后端为内置的豆包语言大模型)
wss://ai-gateway.vei.volces.com/v1/realtime
?model=AG-voice-chat-agent
Authorization: Bearer $YOUR_API_KEY
说明
$YOUR_API_KEY
需要替换成绑定了 语音对话智能体 的网关访问密钥的 API key。详情请参见使用流程。
组合使用语音对话智能体和您自己的 Coze 智能体(后端为您的 Coze 智能体)
wss://ai-gateway.vei.volces.com/v1/realtime
?model=AG-voice-chat-agent&ag-coze-bot-id=<Coze Bot ID>
Authorization: Bearer $YOUR_API_KEY
X-Conversation-Id: sess_xxxx
(可选字段)
说明
$YOUR_API_KEY
需要替换成绑定您的 Coze 智能体和 语音对话智能体的网关访问密钥的 API key。详情请参见使用流程。X-Conversation-Id
:用于将服务端返回的 session.created
中的 session id
保存下来以继续之前的会话。Realtime API 兼容 OpenAI 的 Realtime 接口,支持的事件如下表所示。
类型 | 事件 | 说明 |
---|---|---|
客户端 | 将此事件发送以更新会话的默认配置。 | |
发送此事件以将音频字节追加到输入音频缓冲区。 | ||
发送此事件以提交用户输入的音频缓冲区。 | ||
此事件指示服务器创建一个响应,这意味着触发模型推理。 | ||
发送此事件来取消当前正在回复的语音应答。 | ||
服务端 | 在会话创建时返回。新连接建立时自动触发,作为第一个服务器事件。 | |
发送此事件以确认客户端更新的会话配置。 | ||
在创建新响应时返回。响应创建的第一个事件,此时响应处于初始的进行中状态。 | ||
在响应生成过程中创建新项目时返回。 | ||
在模型生成的文本信息流式返回。 | ||
在模型生成的文本信息完成流式传输时返回。 | ||
在模型生成的语音流式返回。 | ||
在模型完成生成的音频流式传输时返回。 | ||
在一次响应完成流式传输时返回。 | ||
在响应完成流式传输时返回。 |
将此事件发送以更新会话的默认配置。如需更新配置,客户端必须在创建会话的一开始就发送该消息(发送音频之前),服务器将以 session.updated
事件响应,显示完整的有效配置。
event_id
: stringtype
: stringsession.update
。session
: objectmodalities
: array["text", "audio"]
或 ["audio"]
。当为 ["audio"]
时服务器只不会返回 response.audio_transcript
中的文本消息。instructions
: string你是一个玩具对话智能体,你的名字叫豆包,你的回答要尽量简短
。该字段对后端为 Coze 不生效。voice
: stringzh_female_tianmeixiaoyuan_moon_bigtts
。input_audio_format
: stringpcm16
。output_audio_format
: stringpcm16
。input_audio_transcription
: objectturn_detection
: objecttools
: arraytool_choice
: stringtemperature
: numbermax_response_output_tokens
: integer or "inf"示例:
{'event_id': '6b0efb37-fc65-4956-b347-2411352282fe', 'session': {'model': None, 'modalities': {'audio', 'text'}, 'instructions': '', 'voice': : 'zh_female_tianmeixiaoyuan_moon_bigtts', 'turn_detection': None, 'input_audio_format': 'pcm16', 'output_audio_format': 'pcm16', 'input_audio_transcription': None, 'tools': None, 'tool_choice': 'auto', 'temperature': 0.8, 'max_response_output_tokens': None}, 'type': 'session.update'}
发送此事件以将音频字节追加到输入音频缓冲区。音频缓冲区是临时存储,您可以在其中写入数据并稍后进行提交。
event_id
: stringtype
: stringinput_audio_buffer.append
。audio
: string示例:
{ "type": "input_audio_buffer.append", "audio": base64_audio }
发送此事件以提交用户输入的音频缓冲区。提交输入音频缓冲区将触发音频转录,但不会生成模型的响应。服务器将以 input_audio_buffer.committed
事件进行响应。
event_id
: stringtype
: stringinput_audio_buffer.commit
。{ "type": "input_audio_buffer.commit" }
此事件指示服务器创建一个响应,这意味着触发模型推理。服务器将以 response.created
事件进行响应,包含为项目和内容创建的事件,最后发送 response.done
事件以指示响应已完成。
event_id
: stringtype
: stringresponse.create
。response
: objectmodalities
: array["text", "audio"]
或 ["audio"]
。当为 ["audio"]
时服务器只不会返回 response.audio_transcript
中的文本消息。instructions
: string你是一个玩具对话智能体,你的名字叫豆包,你的回答要尽量简短
。该字段对后端为 Coze 不生效。voice
: stringzh_female_tianmeixiaoyuan_moon_bigtts
。output_audio_format
: stringpcm16
。示例:
{ "type": "response.create", "response": { "modalities": ["text", "audio"] } }
发送到服务器将来需取消正在应答的语音消息。可以在收到服务端的 response.audio.delta
后发送该消息来取消后续的语音消息,服务端最终会应答 response.done
中带 cancelled
状态以指示响应已完成。
event_id
: stringtype
: stringresponse.create
。示例:
{ "type": "response.cancel", }
示例(服务端最终应答 response.done
中带 cancelled
):
{'event_id': 'event_5fe8c9c224ee4d6d82cf7', 'response': {'id': 'resp_7a4c14b7ac884610a115c', 'output': [], 'object': 'realtime.response', 'status': 'cancelled', 'status_details': None, 'usage': {'total_tokens': 201, 'input_tokens': 131, 'output_tokens': 70, 'input_token_details': {'cached_tokens': 0, 'text_tokens': 123, 'audio_tokens': 8}, 'output_token_details': {'text_tokens': 35, 'audio_tokens': 35}}}, 'type': 'response.done'}
在会话创建时返回。新连接建立时自动触发,作为第一个服务器事件。此事件将包含默认的会话配置。
event_id
: stringtype
: stringsession.created
。session
: objectid
: stringmodel
: stringmodalities
: array["text", "audio"]
或 ["audio"]
。instructions
: stringvoice
: stringinput_audio_format
: stringpcm16
。output_audio_format
: stringpcm16
。input_audio_transcription
: objectturn_detection
: objecttools
: arraytool_choice
: stringtemperature
: numbermax_response_output_tokens
: integer or "inf"示例:
{'event_id': 'event_5408c86192a14ae088d55', 'session': {'id': 'sess_7441921809949130779', 'model': '7441883325217882146', 'expires_at': 1732709032, 'object': 'realtime.session', 'modalities': ['text', 'audio'], 'instructions': None, 'voice': 'zh_male_shaonianzixin_moon_bigtts', 'turn_detection': None, 'input_audio_format': 'pcm16', 'output_audio_format': 'pcm16', 'input_audio_transcription': None, 'tools': [], 'tool_choice': 'auto', 'temperature': 0.8, 'max_response_output_tokens': 'inf'}, 'type': 'session.created'}
发送此事件以确认客户端更新的会话配置。目前只支持在连接刚创建的时候发送该消息以更新配置。服务器将以 session.updated
事件响应,显示完整的有效配置。只有存在的字段会被更新。
event_id
: stringtype
: stringsession.updated
。session
: objectid
: stringmodel
: stringmodalities
: array["text", "audio"]
或 ["audio"]
。instructions
: stringinstructions
。voice
: stringinput_audio_format
: stringpcm16
。output_audio_format
: stringpcm16
。input_audio_transcription
: objectturn_detection
: objecttools
: arraytool_choice
: stringtemperature
: numbermax_response_output_tokens
: integer or "inf"示例:
{'event_id': 'event_a3152381834747f48fff1', 'session': {'id': 'sess_7441921809949130779', 'model': '7441883325217882146', 'expires_at': 1732709032, 'object': 'realtime.session', 'modalities': ['text', 'audio'], 'instructions': None, 'voice': 'zh_female_tianmeixiaoyuan_moon_bigtts', 'turn_detection': None, 'input_audio_format': 'pcm16', 'output_audio_format': 'pcm16', 'input_audio_transcription': None, 'tools': [], 'tool_choice': 'auto', 'temperature': 0.8, 'max_response_output_tokens': 'inf'}, 'type': 'session.updated'} Python
在创建新响应时返回。响应创建的第一个事件,此时响应处于初始的进行中状态。
event_id
: stringtype
: stringresponse.created
。response
: objectid
: stringobject
: stringrealtime.response
。status
: stringin_progress
, completed
。status_details
: objectoutput
: arrayusage
: object示例:
{'event_id': 'event_cb7646e899e648cfb269e', 'response': {'id': 'resp_06348064b26b412196e32', 'output': [], 'object': 'realtime.response', 'status': 'in_progress', 'status_details': None, 'usage': None}, 'type': 'response.created'}
在响应生成过程中创建新项目时返回。
event_id
: stringtype
: stringresponse.output_item.added
。response_id
: stringoutput_index
: integeritem
: objectid
: stringobject
: stringrealtime.item
。type
: stringmessage
。status
: stringin_progress
, completed
。role
: stringuser
, assistant
, system
。content
: array示例:
{'event_id': 'event_81efe6cf663040d6a6579', 'response_id': 'resp_06348064b26b412196e32', 'output_index': 0, 'item': {'content': [], 'id': 'item_73fe51150f4a446abd9d9', 'status': 'in_progress', 'type': 'message', 'role': 'assistant', 'object': 'realtime.item'}, 'type': 'response.output_item.added'}
在模型生成的文本信息流式返回。
event_id
: stringtype
: stringresponse.audio_transcript.delta
。response_id
: stringitem_id
: stringoutput_index
: integercontent_index
: integerdelta
: string示例:
{'event_id': 'event_3ce9f18992e1461fb486d', 'response_id': 'resp_06348064b26b412196e32', 'item_id': 'item_73fe51150f4a446abd9d9', 'output_index': 0, 'content_index': 0, 'delta': '你', 'type': 'response.audio_transcript.delta'}
在模型生成的文本信息完成流式传输时返回。
event_id
: stringtype
: stringresponse.audio_transcript.done
。response_id
: stringitem_id
: stringoutput_index
: integercontent_index
: integertranscript
: string示例:
{'event_id': 'event_bb2c02ef6fa542ed9027d', 'response_id': 'resp_06348064b26b412196e32', 'item_id': 'item_73fe51150f4a446abd9d9', 'output_index': 0, 'content_index': 0, 'transcript': '你好,有没有好玩的事呀?', 'type': 'response.audio_transcript.done'}
在模型生成的语音流式返回。
event_id
: stringtype
: stringresponse.audio.delta
。response_id
: stringitem_id
: stringoutput_index
: integercontent_index
: integerdelta
: string示例:
{'event_id': 'event_3ce9f18992e1461fb486d', 'response_id': 'resp_06348064b26b412196e32', 'item_id': 'item_73fe51150f4a446abd9d9', 'output_index': 0, 'content_index': 0, 'delta': 'base64', 'type': 'response.audio.delta'}
在模型完成生成的音频流式传输时返回。
event_id
: stringtype
: stringresponse.audio.done
。response_id
: stringitem_id
: stringoutput_index
: integercontent_index
: integer示例:
{'event_id': 'event_51bb61cdab5e4ad8ac925', 'response_id': 'resp_06348064b26b412196e32', 'item_id': 'item_73fe51150f4a446abd9d9', 'output_index': 0, 'content_index': 0, 'type': 'response.audio.done'}
在一次响应完成流式传输时返回。
event_id
: stringtype
: stringresponse.output_item.done
。response_id
: stringoutput_index
: integeritem
: objectid
: stringobject
: stringrealtime.item
。type
: stringmessage
。status
: stringin_progress
, completed
。role
: stringuser
, assistant
, system
。content
: arraytype
: stringinput_text
, input_audio
, audio
, text
。text
: stringinput_text
和 text
的情形。audio
: stringinput_audio
的情形。transcript
: stringinput_audio
和 audio
的情形。示例:
{'event_id': 'event_57938730a5764a7c83cb4', 'response_id': 'resp_06348064b26b412196e32', 'output_index': 0, 'item': {'content': [{'type': 'audio', 'transcript': '你好,有没有好玩的事呀?'}], 'id': 'item_73fe51150f4a446abd9d9', 'status': 'completed', 'type': 'message', 'role': 'assistant', 'object': 'realtime.item'}, 'type': 'response.output_item.done'}
在响应完成流式传输时返回。
event_id
: stringtype
: stringresponse.done
。response
: objectid
: stringobject
: stringrealtime.response
。status
: stringin_progress
, completed
。status_details
: objectoutput
: arrayusage
: objecttotal_tokens
: integerinput_tokens
: integeroutput_tokens
: integerinput_token_details
: objectcached_tokens
: integertext_tokens
: integeraudio_tokens
: integeroutput_token_details
: objecttext_tokens
: integeraudio_tokens
: integer示例:
{'event_id': 'event_767d3f51a1c84e99a4185', 'response': {'id': 'resp_33a7a44490354861ab3c0', 'output': [], 'object': 'realtime.response', 'status': 'completed', 'status_details': None, 'usage': {'total_tokens': 157, 'input_tokens': 141, 'output_tokens': 16, 'input_token_details': {'cached_tokens': 0, 'text_tokens': 133, 'audio_tokens': 8}, 'output_token_details': {'text_tokens': 8, 'audio_tokens': 8}}}, 'type': 'response.done'}
在发生错误时返回,这可能是客户端问题或服务器问题。对于一些输入错误的请求,会返回应答但是不会影响连接。对于一些服务端的错误,会返回应答,并会断开连接,客户端这时必须重连。如果服务端在 120 秒内没有收到新的消息,会返回 error,并主动断开 WebSocket 连接。
event_id
: stringtype
: stringerror
。error
: objecttype
: stringinvalid_request_error
, server_error
。code
: stringmessage
: stringparam
: stringevent_id
: string示例:
{'event_id': 'event_f2aac7bbab6f4854a7c2d', 'error': {'type': 'server_error', 'message': 'There is problem in server side, please try again later', 'code': 'server_error', 'param': None, 'event_id': None}, 'type': 'error'}
以下是一个时序图示例:
pip install soundfile scipy numpy websockets==12.0
测试音频
import asyncio import base64 import json import wave import numpy as np import soundfile as sf from scipy.signal import resample import websockets def resample_audio(audio_data, original_sample_rate, target_sample_rate): number_of_samples = round( len(audio_data) * float(target_sample_rate) / original_sample_rate) resampled_audio = resample(audio_data, number_of_samples) return resampled_audio.astype(np.int16) def pcm_to_wav(pcm_data, wav_file, sample_rate=16000, num_channels=1, sample_width=2): print(f"saved to file {wav_file}") with wave.open(wav_file, 'wb') as wav: # Set the parameters # Number of channels (1 for mono, 2 for stereo) wav.setnchannels(num_channels) # Sample width in bytes (2 for 16-bit audio) wav.setsampwidth(sample_width) wav.setframerate(sample_rate) wav.writeframes(pcm_data) async def send_audio(client, audio_file_path: str): sample_rate = 16000 duration_ms = 100 samples_per_chunk = sample_rate * (duration_ms / 1000) bytes_per_sample = 2 bytes_per_chunk = int(samples_per_chunk * bytes_per_sample) audio_data, original_sample_rate = sf.read( audio_file_path, dtype="int16") if original_sample_rate != sample_rate: audio_data = resample_audio( audio_data, original_sample_rate, sample_rate) audio_bytes = audio_data.tobytes() for i in range(0, len(audio_bytes), bytes_per_chunk): await asyncio.sleep((duration_ms - 10)/1000) chunk = audio_bytes[i: i + bytes_per_chunk] base64_audio = base64.b64encode(chunk).decode("utf-8") append_event = { "type": "input_audio_buffer.append", "audio": base64_audio } await client.send(json.dumps(append_event)) commit_event = { "type": "input_audio_buffer.commit" } await client.send(json.dumps(commit_event)) event = { "type": "response.create", "response": { "modalities": ["text", "audio"] } } await client.send(json.dumps(event)) async def receive_messages(client, save_file_name): audio_list = bytearray() while not client.closed: message = await client.recv() if message is None: continue event = json.loads(message) message_type = event.get("type") if message_type == "response.audio.delta": audio_bytes = base64.b64decode(event["delta"]) audio_list.extend(audio_bytes) continue if message_type == 'response.done': pcm_to_wav(audio_list, save_file_name) break print(event) continue def get_session_update_msg(): config = { "modalities": ["text", "audio"], "instructions": "你的名字叫豆包,你是一个智能助手", "voice": "zh_female_tianmeixiaoyuan_moon_bigtts", "input_audio_format": "pcm16", "output_audio_format": "pcm16", "tool_choice": "auto", "turn_detection": None, "temperature": 0.8, } event = { "type": "session.update", "session": config } return json.dumps(event) async def with_realtime(audio_file_path: str, save_file_name: str): ws_url = "wss://ai-gateway.vei.volces.com/v1/realtime?model=AG-voice-chat-agent" key = "xxx" # 修改为你的 key headers = { "Authorization": f"Bearer {key}", } async with websockets.connect(ws_url, extra_headers=headers) as client: session_msg = get_session_update_msg() await client.send(session_msg) await asyncio.gather(send_audio(client, audio_file_path), receive_messages(client, save_file_name)) await asyncio.sleep(0.5) if __name__ == "__main__": audio_file_path = "demo_audio_nihaoya.wav" #下载示例音频 save_file_name = "demo_response.wav" asyncio.run(with_realtime(audio_file_path, save_file_name))
目前,Realtime API 需要使用音频输出或音频输入。只接受如下组合:
支持客户端通过发送 response.cancel
来打断当前输出的语音消息。
目前只支持保留当前 WebSocket 的对话历史,最多保留 10 轮。当 WebSocket 断开后,对话历史不会被保留。
目前暂不支持服务端的 VAD,必须客户端主动提交告知服务端完成当前的语音采集。
目前只支持 pcm16 的格式,默认为 16000Hz 的采样频率。
目前只支持在 语音技术 - 音色列表 通用场景 中 包含中文语种且以 zh_ 开头 的音色。默认为 zh_female_tianmeixiaoyuan_moon_bigtts
。