本文将介绍如何基于 TRTC SDK 实现 AI 实时对话的解决方案。
方案介绍
方案基于 TRTC SDK 调用TRTC服务,通过调用 AI 实时对话的接口,可以实现极低延迟的 AI 实时对话服务。本方案为您提供了非常灵活的集成方案,您可以根据业务的实际需求接入第三方的 LLM 和 TTS,实现最佳的业务实践效果。在整体方案中,我们针对语音实时降噪、AI 智能打断、上下文管理都有较多的技术优化,不断提升用户体验。
方案架构图
业务流程图
集成指引
前提条件
注意:
1. 创建 TRTC 应用。
2. 创建腾讯云 TTS(可使用第三方)。
3. 创建 LLM 应用:可以自选适合的大模型厂商进行注册。
一、集成TRTC SDK
第一步:导入TRTC SDK到项目中
第二步:进入TRTC房间
第三步:发布音频流
您可以调用 startLocalAudio 来开启麦克风采集,该接口需要您通过 quality 参数确定采集模式。虽然这个参数的名字叫做 quality,但并不是说质量越高越好,不同的业务场景有最适合的参数选择(这个参数更准确的含义是 scene)。
AI 对话场景下推荐使用 SPEECH 模式,该模式下的 SDK 音频模块会专注于提炼语音信号,尽最大限度的过滤周围的环境噪音,同时该模式下的音频数据也会获得较好的差质量网络的抵抗能力,因此该模式特别适合于“视频通话”和“在线会议”等侧重于语音沟通的场景。
// 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)mCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH );
// 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)//对于高噪声抑制能力、强弱网抗性AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];[appDelegate.trtcCloud startLocalAudio:TRTCAudioQualitySpeech];
二、发起 AI 对话
开始 AI 对话
以下接口推荐由业务后台来调用,客户端只调用业务后台提供的接口,来发起AI对话。
TRTC 提供以下云 API 用于发起和管理对话任务,具体如下:
目前支持的 TTS 和 LLM 模型调用方法:
LLM 交互
openai:
"LLMConfig": {"LLMType": "openai","Model":"gpt-4o","APIKey":"api-key","APIUrl":"https://api.openai.com/v1/chat/completions","Streaming": true,"SystemPrompt": "你是一个个人助手","Timeout": 3.0,"History": 5 // 最大支持 50 轮对话, 默认为 0}
minimax:
"LLMConfig":{"APIKey": "eyJhbGcixxxx","LLMType": "minimax","Model": "abab6.5s-chat","Streaming": true,"SystemPrompt": "你是一个个人助手","APIUrl": "https://api.minimax.chat/v1/text/chatcompletion_v2","History": 5 // 最大支持 50 轮对话}
混元:
"LLMConfig":{"LLMType": "openai","Model": "hunyuan-standard", # hunyuan-turbo、hunyuan-standard"APIKey": "hunyuan-apikey","APIUrl": "https://hunyuan.cloud.tencent.com/openai/v1/chat/completions","Streaming": true,"History": 10}
此外我们会在 http header 中增加多个参数来辅助用户支持更复杂的逻辑:
X-Task-Id: <task_id_value> // 此任务的 id,X-Rquest-Id: <request_id> // 此次请求的id, 重试会携带相同的requestIdX-Sdk-App-Id: SdkAppIdX-User-Id:UserIdX-Room-Id:RoomIdX-Room-Id-Type: "0" // "0"表示数字房间号 "1"表示字符串房间号
TTS 交互
TTS 参数使用用户自己的第三方账号。
自定义 TTS
{"TTSType": "custom", // String 必填"APIKey": "ApiKey", // String 必填 用来鉴权"APIUrl": "http://0.0.0.0:8080/stream-audio" // String,必填,TTS API URL"AudioFormat": "wav", // String, 非必填,期望输出的音频格式,如mp3, ogg_opus,pcm,wav,默认为 wav,目前只支持pcm和wav,"SampleRate": 16000, // Integer,非必填,音频采样率,默认为16000(16k),推荐值为16000"AudioChannel": 1, // Integer,非必填,音频通道数,取值:1 或 2 默认为1}
Tencent TTS:
{"TTSType": "tencent", // String TTS类型,"AppId": "您的应用ID", // String 必填"SecretId": "您的密钥ID", // String 必填, 账号级别 SecretId"SecretKey": "您的密钥Key", // String 必填,账号级别 SecretKey"VoiceType": 101001, // Integer 必填,音色 ID,包括标准音色与精品音色,精品音色拟真度更高,价格不同于标准音色,请参见语音合成计费概述。完整的音色 ID 列表请参见语音合成音色列表。"Speed": 1.25, // Integer 非必填,语速,范围:[-2,6],分别对应不同语速: -2: 代表0.6倍 -1: 代表0.8倍 0: 代表1.0倍(默认) 1: 代表1.2倍 2: 代表1.5倍 6: 代表2.5倍 如果需要更细化的语速,可以保留小数点后 2 位,例如0.5/1.25/2.81等。 参数值与实际语速转换,可参考 语速转换"Volume": 5, // Integer 非必填,音量大小,范围:[0,10],分别对应11个等级的音量,默认值为0,代表正常音量。"PrimaryLanguage": 1, // Integer 可选 主要语言 1-中文(默认) 2-英文 3-日文"FastVoiceType": "xxxx" // 可选参数, 快速声音复刻的参数}
minimax TTS
{"TTSType": "minimax", // String TTS类型,"Model": "speech-01-turbo-240228", // String 使用的模型,可选[speech-01-turbo, speech-01-turbo-240228, speech-01-240228]"ApiUrl": "https://api.minimax.chat/v1/t2a_v2", //"GroupId": "181000000000000", // String,需要在MiniMax管理后台获取:https://platform.minimaxi.com/user-center/basic-information"ApiKey": "eyxxxx", // String,需要在MiniMax管理后台获取:https://platform.minimaxi.com/user-center/basic-information/interface-key"VoiceType":"audiobook_female_1", // String,音色选择可以参考MiniMax文档"Speed": 1.2 // Numer,范围[0.5,2],默认值为1.0}
接口名 | T2A v2(语音生成) | T2A Pro(语音生成) | T2A(语音生成) | T2A Stream(流式语音生成) | T2A Stream(流式语音生成) |
模型 | speech-01-turbo、speech-01-240228、speech-01-turbo-240228 | speech-01、speech-02 | speech-01、speech-02 | speech-01 | speech-01 |
客户类型\\限制类型 | RPM | RPM | RPM | RPM | CONN(最大并行运行任务数) |
免费用户 | 3 | 3 | 3 | 3 | 1 |
充值用户 | 20 | 20 | 20 | 20 | 3 |
Azure TTS
{"TTSType": "azure", // 必填:String TTS类型"SubscriptionKey": "xxxxxxxx", // 必填:String 订阅的Key"Region": "chinanorth3", // 必填:String 订阅的地区"VoiceName": "zh-CN-XiaoxiaoNeural", // 必填:String 音色名必填"Language": "zh-CN", // 必填:String 合成的语言"Rate": 1 // 选填:float 语速 0.5~2 默认为 1}
查询 AI 对话任务
停止 AI 对话
控制 AI 对话任务
三、接收AI对话及AI状态
接收实时字幕
消息格式
{"type": 10000, // 10000表示是下发的实时字幕"sender": "user_a", // 说话人的userid"receiver": [], // 接收者userid列表,该消息实际是在房间内广播"payload": {"text":"", // 语音识别出的文本"translation_text":"", // 翻译的文本"start_time":"00:00:01", // 这句话的开始时间"end_time":"00:00:02", // 这句话的结束时间"roundid": "xxxxx", // 唯一标识一轮对话"end": true // 如果为true,代表这是一句完整的话}}
接收机器人状态
消息格式
{"type": 10001, // 机器人的状态"sender": "user_a", // 发送者userid,这里是机器人的id"receiver": [], // 接受者userid列表,该消息实际是在房间内广播"payload": {"roundid": "xxx", // 唯一标识一轮对话"timestamp": 123,"state": 1, // 1 聆听中 2 思考中 3 说话中 4 被打断}}
示例代码
@Overridepublic void onRecvCustomCmdMsg(String userId, int cmdID, int seq, byte[] message) {String data = new String(message, StandardCharsets.UTF_8);try {JSONObject jsonData = new JSONObject(data);Log.i(TAG, String.format("receive custom msg from %s cmdId: %d seq: %d data: %s", userId, cmdID, seq, data));} catch (JSONException e) {Log.e(TAG, "onRecvCustomCmdMsg err");throw new RuntimeException(e);}}
func onRecvCustomCmdMsgUserId(_ userId: String, cmdID: Int, seq: UInt32, message: Data) {if cmdID == 1 {do {if let jsonObject = try JSONSerialization.jsonObject(with: message, options: []) as? [String: Any] {print("Dictionary: \\(jsonObject)")// handleMessage(jsonObject)} else {print("The data is not a dictionary.")}} catch {print("Error parsing JSON: \\(error)")}}}
四、发送自定义消息
统一通过端上发送 TRTC 自定义消息,cmdID 固定是2。
可以通过发送自定义的文本,跳过 asr 过程,直接跟 ai service 进行文字沟通。
{"type": 20000, // 端上发送自定义文本消息"sender": "user_a", // 发送者userid, 服务端会check该userid是否有效"receiver": ["user_bot"], // 接受者userid列表,只需要填写机器人userid,服务端会check该userid是否有效"payload": {"id": "uuid", // 消息id,可以使用uuid,排查问题使用"message": "xxx", // 消息内容"timestamp": 123 // 时间戳,排查问题使用}}
可以通过发送打断信令来进行打断。
{"type": 20001, // 端上发送打断信令"sender": "user_a", // 发送者userid, 服务端会check该userid是否有效"receiver": ["user_bot"], // 接受者userid列表,只需要填写机器人userid,服务端会check该userid是否有效"payload": {"id": "uuid", // 消息id,可以使用uuid,排查问题使用"timestamp": 123 // 时间戳,排查问题使用}}
示例代码
public void sendInterruptCode() {try {int cmdID = 0x2;long time = System.currentTimeMillis();String timeStamp = String.valueOf(time/1000);JSONObject payLoadContent = new JSONObject();payLoadContent.put("timestamp", timeStamp);payLoadContent.put("id", String.valueOf(GenerateTestUserSig.SDKAPPID) + "_" + mRoomId);String[] receivers = new String[]{robotUserId};JSONObject interruptContent = new JSONObject();interruptContent.put("type", AICustomMsgType.AICustomMsgType_Send_Interrupt_CMD);interruptContent.put("sender", mUserId);interruptContent.put("receiver", new JSONArray(receivers));interruptContent.put("payload", payLoadContent);String interruptString = interruptContent.toString();byte[] data = interruptString.getBytes("UTF-8");Log.i(TAG, "sendInterruptCode :" + interruptString);mTRTCCloud.sendCustomCmdMsg(cmdID, data, true, true);} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (JSONException e) {throw new RuntimeException(e);}}
@objc func interruptAi() {print("interruptAi")let cmdId = 0x2let timestamp = Int(Date().timeIntervalSince1970 * 1000)let payload = ["id": userId + "_\\(roomId)" + "_\\(timestamp)", // 消息id,可以使用uuid,排查问题使用"timestamp": timestamp // 时间戳,排查问题使用] as [String : Any]let dict = ["type": 20001,"sender": userId,"receiver": [botId],"payload": payload] as [String : Any]do {let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])self.trtcCloud.sendCustomCmdMsg(cmdId, data: jsonData, reliable: true, ordered: true)} catch {print("Error serializing dictionary to JSON: \\(error)")}}
五、停止 AI 对话,退出 TRTC 房间
1. 停止 AI 对话任务 :StopAIConversation
同样推荐由业务后台来调用,客户端只调用业务后台提供的接口,来停止 AI 对话。
2. 退出 TRTC 房间建议参见 退出房间(Android&iOS&Windows&Mac)。
六、使用高级功能
1、接入回调接口
注意:
回调地址在 TRTC 控制台设置,AI实时对话回调。
字段名 | 类型 | 含义 |
EVENT_TYPE_AI_SERVICE_START | 901 | AI 任务开始,调用 start 接口,启动任务时产生 |
EVENT_TYPE_AI_SERVICE_STOP | 902 | AI 任务结束,调用 stop 接口,结束任务时产生 |
EVENT_TYPE_AI_SERVICE_MSG | 903 | 识别出完整的一句话后回调 以及在 LLM 产生完整的回答时回调 |
EVENT_TYPE_AI_SERVICE_START_OF_SPEECH | 904 | 识别到用户开始说话回调 |
EVENT_TYPE_AI_SPEAKING_FINISHED | 905 | 在一轮对话中,AI机器人说完话时回调 |
说明:
开始事件901
{"EventGroupId": 9, // 事件组ID,AI服务固定为9,EVENT_GROUP_AI_SERVICE"EventType": 901, // 事件类型,详细事件类型见下方"CallbackTs": 1687770730166, // 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒"EventInfo": {"EventMsTs": 1622186275757, // 事件触发的ms时间戳"TaskId": "xx", // 任务ID"RoomId": "1234","RoomIdType": 0, // 0表示数字房间号,1表示字符串房间号"Payload": {"Status": 0}}}
字段 | 类型 | 含义 |
Status | Number | 0:启动 AI 任务成功 1:启动 AI 任务失败 |
结束事件902
{"EventGroupId": 9, // 事件组ID,AI服务固定为9,EVENT_GROUP_AI_SERVICE"EventType": 902, // 事件类型,详细事件类型见下方"CallbackTs": 1687770730166, // 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒"EventInfo": {"EventMsTs": 1622186275757, // 事件触发的ms时间戳"TaskId": "xx", // 任务ID"RoomId": "1234","RoomIdType" 0, // 0表示数字房间号,1表示字符串房间号"Payload": {"LeaveCode": 0}}}
字段 | 类型 | 含义 |
LeaveCode | Number | 0:正常调用停止接口后任务退出 1:业务自己踢掉转录机器人后任务退出 2:业务自己解散房间后任务退出 3:TRTC 服务端踢掉机器人 4:TRTC 服务端解散房间 98:内部异常错误,建议业务进行重试 99:代表房间内除了转录机器人没有其他用户流,超过指定时间退出 |
识别完成一句话回调 903
{"EventGroupId": 9, // 事件组ID,AI服务固定为9,EVENT_GROUP_AI_SERVICE"EventType": 903, // 事件类型,详细事件类型见下方"CallbackTs": 1687770730166, // 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒"EventInfo": {"EventMsTs": 1622186275757, // 事件触发的ms时间戳"TaskId": "xx", // 任务ID"RoomId": "1234","RoomIdType" 0, // 0表示数字房间号,1表示字符串房间号"Payload": {"type": "subtitle", // subtitle是字幕消息 transcription是转录消息"userid": "xxx", // 消息对应的用户"text": "xxxx", // 源语言文本"translation_text": "xxx", // 翻译语言文本,没有翻译时为空字符串"start_time": "00:30:00", // 开始时间"end_time": "00:30:02" // 结束时间"roundid": "xxxxx" // 一轮对话的唯一id"start_ms_ts": 123245678 // 开始的毫秒时间戳"end_ms_ts": 123245678 // 内容结束的毫秒时间戳(asr代表识别结束,llm代表回复结束)}}}
用户开始说话回调 904
{"EventGroupId": 9, // 事件组ID,AI服务固定为9,EVENT_GROUP_AI_SERVICE"EventType": 904, // 事件类型,开始识别第一个字 "CallbackTs": 1687770730166, // 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒"EventInfo": {"EventMsTs": 1622186275757, // 事件触发的ms时间戳"TaskId": "xx", // 任务ID"RoomId": "1234","RoomIdType" 0, // 0表示数字房间号,1表示字符串房间号"Payload": {"userid": "xxx","start_time": "00:30:00", // 开始时间"roundid": "xxxxx" // 一轮对话的唯一id}}}
AI机器人说完话时回调 905
{"EventGroupId": 9, // 事件组ID,AI服务固定为9,EVENT_GROUP_AI_SERVICE"EventType": 905, // 事件类型,详细事件类型见下方"CallbackTs": 1687770730166, // 事件回调服务器向您的服务器发出回调请求的 Unix 时间戳,单位为毫秒"EventInfo": {"EventMsTs": 1622186275757, // 事件触发的ms时间戳"TaskId": "xx", // 任务ID"RoomId": "1234","RoomIdType" 0, // 0表示数字房间号,1表示字符串房间号"Payload": {"UserId": "UserId", // AI机器人的UserId"RoundId": "RoundId", // 当前对话的RoundId"Text": "Text" // 在本轮对话中,AI机器人已经说了文本}}}
2、AI 对话的控制和更新:
控制 AI 对话
更新 AI 对话
3、其余高级功能介绍
功能 | 操作指引 |
智能打断 | |
实现上下文管理 | |
监听AI对话 | |
实现实时字幕 | |
调用 function call |