产品概述
产品优势
应用场景
功能模块 | AI 面试场景应用 |
实时音视频 | 基于 RTC Engine 提供高质量、低延迟的音视频连线,支持720P/1080P/2K 高清画质和48kHz 高音质。无论网络环境如何,均能保障流畅互动,模拟真实面试场景。 |
AI 实时对话 | 腾讯 AI 实时对话解决方案,支持客户灵活接入多家 AI 大模型服务,实现 AI 与用户之间的实时音视频互动,打造符合业务场景的 AI 实时对话能力。基于腾讯 RTC 全球低延迟传输,对话效果自然拟真,接入便捷,开箱即用。 |
大语言模型 LLM | 智能理解候选人的语音内容和语境,精准提炼应答要点,动态生成后续面试问题,实现个性化和结构化的面试流程。LLM 技术还可根据不同岗位算法自动调整评分标准,提高评测的公平性与准确性。 |
文字转语音 TTS | 支持接入第三方 TTS,支持多种语言和语音风格输出,AI 面试官可通过 TTS 技术展现不同语气、性格,最大程度模拟真人面试官,提升候选人体验。 |
即时通信 Chat | 通过 Chat 完成关键业务信令的透传。 |
面试管理后台 | 包含题库与面试设计、自动评分、数据存储、视觉分析、面试日程管理等能力。 |


// 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)mCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH );
self.trtcCloud = [TRTCCloud sharedInstance];// 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)[self.trtcCloud startLocalAudio:TRTCAudioQualitySpeech];
// 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)trtcCloud.startLocalAudio(TRTCAudioQuality.speech);
await trtc.startLocalAudio();
// 开启麦克风采集,设置当前场景为:语音模式// 具有高的噪声抑制能力,有强有弱的网络阻力ITRTCCloud* trtcCloud = CRTCWindowsApp::GetInstance()->trtc_cloud_;trtcCloud->startLocalAudio(TRTCAudioQualitySpeech);
// 开启麦克风采集,设置当前场景为:语音模式// 具有高的噪声抑制能力,有强有弱的网络阻力AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];[appDelegate.trtcCloud startLocalAudio:TRTCAudioQualitySpeech];
LLMConfig 和 TTSConfig中。名称 | 类型 | 是否必填 | 描述 |
LLMType | String | 是 | 大模型类型,只要是符合 OpenAI API协议的大模型,都填写openai。 |
Model | String | 是 | 具体的模型名称,例如 gpt-4o、deepseek-chat。 |
APIKey | String | 是 | 大模型的 APIKey。 |
APIUrl | String | 是 | 大模型的 APIUrl。 |
Streaming | Boolean | 否 | 是否流式,默认为 false,建议填true。 |
SystemPrompt | String | 否 | 系统提示词。 |
Timeout | Float | 否 | 超时时间,取值范围 [1~50], 默认为 3 秒(单位:秒)。 |
History | Integer | 否 | 设置 LLM 的上下文轮次,默认值:0(不提供上下文管理),最大值:50(提供最近50轮的上下文管理)。 |
MaxTokens | Integer | 否 | 输出文本的最大 token 限制。 |
Temperature | Float | 否 | 采样温度。 |
TopP | Float | 否 | 采样的选择范围,控制输出 token 的多样性。 |
UserMessages | Object[] | 否 | 用户提示词。 |
MetaInfo | Object | 否 | 自定义参数,会放在请求的 body 中透传给大模型。 |
"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,"MetaInfo": {},"MaxTokens": 4096,"Temperature": 0.8,"TopP": 0.8,"UserMessages": [{"Role": "user","Content": "content"},{"Role": "assistant","Content": "content"}]}
{"TTSType": "tencent", // String TTS类型, 目前支持"tencent" 和 “minixmax”, 其他的厂商支持中"AppId": 您的应用ID, // Integer 必填"SecretId": "您的密钥ID", // String 必填"SecretKey": "您的密钥Key", // String 必填"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" // String 可选参数,快速声音复刻的参数"EmotionCategory":"angry",// String 非必填,控制合成音频的情感,仅支持多情感音色使用。取值: neutral(中性)、sad(悲伤)..."EmotionIntensity":150 //Integer 非必填,控制合成音频情感程度,取值范围为 [50,200],默认为 100;只有 EmotionCategory 不为空时生效。}
STTConfig、LLMConfig 和 TTSConfig 配置说明:RoomId 需要和客户端进房的 RoomId 保持一致,并且房间号的类型(数字房间号、字符串房间号)也必须相同(即机器人和用户需要在同一个房间)。TargetUserId 需要和客户端进房使用的 UserId 一致。LLMConfig 和 TTSConfig 均为 JSON 字符串,需要正确配置才能成功发起 AI 实时对话。STTConfig.VadLevel 设置为2或者3,有较好的远场人声抑制能力。状态代码 | 描述 |
asr_latency | ASR 延迟。注意:指标包含启动 AI 对话时 VadSilenceTime 所设置的时间。 |
llm_network_latency | LLM 请求的网络耗时。 |
llm_first_token | LLM 首 token 耗时,指标包含网络耗时。 |
tts_network_latency | TTS 请求的网络耗时。 |
tts_first_frame_latency | TTS 首帧耗时,指标包含网络耗时。 |
tts_discontinuity | TTS 未连续的次数,代表 TTS 流式请求播放完成之后,下一个请求还没有返回结果,通常是 TTS 延迟比较高导致。 |
interruption | 表示此轮对话被打断。 |
Streaming 设为 true),可以极大降低延迟。不建议选用 DeepSeek-R1等思考型模型,此类 LLM 延迟太大,没办法应用在语音对话中。如果对对话延迟特别敏感,可以选用一些参数更小的模型,很多模型可以把首包耗时控制在500ms左右。{"type": 10000, // 10000表示是下发的实时字幕"sender": "user_a", // 说话人的userid"receiver": [], // 接收者userid列表,该消息实际是在房间内广播"payload": {"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 被打断 5 说完话}}
@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)")} else {print("The data is not a dictionary.")}} catch {print("Error parsing JSON: \\(error)")}}}
trtcClient.on(TRTC.EVENT.CUSTOM_MESSAGE, (event) => {let data = new TextDecoder().decode(event.data);let jsonData = JSON.parse(data);console.log(`receive custom msg from ${event.userId} cmdId: ${event.cmdId} seq: ${event.seq} data: ${data}`);if (jsonData.type == 10000 && jsonData.payload.end == false) {// 字幕中间状态} else if (jsonData.type == 10000 && jsonData.payload.end == true) {// 一句话说完了}});
void onRecvCustomCmdMsg(const char* userId, int cmdID, int seq,const uint8_t* message, uint32_t msgLen) {std::string data;if (message != nullptr && msgLen > 0) {data.assign(reinterpret_cast<const char*>(message), msgLen);}if (cmdID == 1) {try {auto j = nlohmann::json::parse(data);std::cout << "Dictionary: " << j.dump() << std::endl;} catch (const std::exception& e) {std::cerr << "Error parsing JSON: " << e.what() << std::endl;}return;}}
void onRecvCustomCmdMsg(String userId, int cmdID, int seq, String message) {if (cmdID == 1) {try {final decoded = json.decode(message);if (decoded is Map<String, dynamic>) {print('Dictionary: $decoded');} else {print('The data is not a dictionary. Raw: $decoded');}} catch (e) {print('Error parsing JSON: $e');}return;}}

import timefrom fastapi import FastAPI, HTTPExceptionfrom fastapi.middleware.cors import CORSMiddlewarefrom pydantic import BaseModelfrom typing import List, Optionalfrom langchain_core.messages import HumanMessage, SystemMessagefrom langchain_openai import ChatOpenAIapp = FastAPI(debug=True)# 添加 CORS 中间件app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)class Message(BaseModel):role: strcontent: strclass ChatRequest(BaseModel):model: strmessages: List[Message]temperature: Optional[float] = 0.7class ChatResponse(BaseModel):id: strobject: strcreated: intmodel: strchoices: List[dict]usage: dict@app.post("/v1/chat/completions")async def chat_completions(request: ChatRequest):try:# 将请求消息转换为 LangChain 消息格式langchain_messages = []for msg in request.messages:if msg.role == "system":langchain_messages.append(SystemMessage(content=msg.content))elif msg.role == "user":langchain_messages.append(HumanMessage(content=msg.content))# add more historys# 使用 LangChain 的 ChatOpenAI 模型chat = ChatOpenAI(temperature=request.temperature,model_name=request.model)response = chat(langchain_messages)print(response)# 构造符合 OpenAI API 格式的响应return ChatResponse(id="chatcmpl-" + "".join([str(ord(c))for c in response.content[:8]]),object="chat.completion",created=int(time.time()),model=request.model,choices=[{"index": 0,"message": {"role": "assistant","content": response.content},"finish_reason": "stop"}],usage={"prompt_tokens": -1, # LangChain 不提供这些信息,所以我们使用占位值"completion_tokens": -1,"total_tokens": -1})except Exception as e:raise HTTPException(status_code=500, detail=str(e))if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
metainfo, AI 服务探测到 metainfo 后,通过 自定义消息 推送到客户端 SDK 上,完成 metainfo 的透传。chat.completion.chunk 对象时,同时返回 meta.info 的 chunk。{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-xxxx", "system_fingerprint": "fp_xxxx", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-xxxx", "system_fingerprint": "fp_xxxx", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}// 增加如下自定义消息{"id":"chatcmpl-123","type":"meta.info","created":1694268190,"metainfo": {}}{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-xxxx", "system_fingerprint": "fp_xxxx", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
metainfo 之后,通过 RTC Engine 的 自定义消息 进行下发。客户端可通过 SDK 回调中的 onRecvCustomCmdMsg 接口接收。{"type": 10002, // 自定义消息"sender": "user_a", // 发送者userid,这里是机器人的id"receiver": [], // 接受者userid列表,该消息实际是在房间内广播,"roundid": "xxxxxx","payload": {} // metainfo}
AgentConfig.TurnDetectionMode 参数设置为 1,以打开手动轮次模式。此时客户端在收到字幕消息后,自行决定是否手动发送聊天信令触发一轮新的对话。参数 | 类型 | 描述 |
TurnDetectionMode | Integer | 控制新一轮对话的触发方式,默认为0。 0表示当服务端语音识别检测出完整的一句话后,自动触发一轮新的对话。 1表示客户端在收到字幕消息后,自行决定是否手动发送聊天信令触发一轮新的对话。 示例值:0 |
{"type": 20000, // 端上发送自定义文本消息"sender": "user_a", // 发送者userid, 服务端会check该userid是否有效"receiver": ["user_bot"], // 接受者userid列表,只需要填写机器人userid,服务端会check该userid是否有效"payload": {"id": "uuid", // 消息id,可以使用uuid,排查问题使用"message": "xxx", // 消息内容"timestamp": 123 // 时间戳,排查问题使用}}
public void sendMessage() {try {int cmdID = 0x2;long time = System.currentTimeMillis();String timeStamp = String.valueOf(time/1000);JSONObject payLoadContent = new JSONObject();payLoadContent.put("timestamp", timeStamp);payLoadContent.put("message", message);payLoadContent.put("id", String.valueOf(GenerateTestUserSig.SDKAPPID) + "_" + mRoomId);String[] receivers = new String[]{robotUserId};JSONObject interruptContent = new JSONObject();interruptContent.put("type", 20000);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 sendMessage() {let cmdId = 0x2let timestamp = Int(Date().timeIntervalSince1970 * 1000)let payload = ["id": userId + "_\\(roomId)" + "_\\(timestamp)", // 消息id,可以使用uuid,排查问题使用"timestamp": timestamp, // 时间戳,排查问题使用"message": "xxx" // 消息内容] 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)")}}
const message = {"type": 20000,"sender": "user_a","receiver": ["user_bot"],"payload": {"id": "uuid","timestamp": 123,"message": "xxx", // 消息内容}};trtc.sendCustomMessage({cmdId: 2,data: new TextEncoder().encode(JSON.stringify(message)).buffer});
AgentConfig.InterruptSpeechDuration 和 STTConfig.VadSilenceTime 参数设置低一点,以降低打断时延。建议同时打开 远场人声抑制 能力,以降低误打断的概率。参数 | 类型 | 描述 |
AgentConfig.InterruptSpeechDuration | Integer | InterruptMode 为0时使用,单位为毫秒,默认为500ms。表示服务端检测到持续 InterruptSpeechDuration 毫秒的人声则进行打断。 示例值:500 |
STTConfig.VadSilenceTime | Integer | 语音识别 vad 的时间,范围为240 - 2000,默认为1000,单位为ms。更小的值会让语音识别分句更快。 示例值:500 |


RoomId 是否和客户端进房的 RoomId 一致,并且房间号的类型(数字房间号、字符串房间号)也必须相同(即机器人和用户需要在同一个房间)。另外检查 TargetUserId 是否和客户端进房使用的 UserId 一致。服务类别 | 错误码 | 错误描述 |
ASR | 30100 | 请求超时 |
| 30102 | 内部错误 |
LLM | 30200 | 请求 LLM 超时 |
| 30201 | LLM 请求被频率限制 |
| 30202 | LLM 服务返回失败 |
TTS | 30300 | 请求 TTS 服务超时 |
| 30301 | TTS 请求被频率限制 |
| 30302 | TTS 服务返回失败 |
llm error Timeout on reading data from socket ,一般是 LLM 请求超时了,可以将 LLMConfig 里的 Timeout 参数设置大点(默认为3秒)。此外,当 LLM 的首包耗时超过3秒时,较高的对话延迟会影响 AI 对话的体验,如果没有特殊需求,建议可以优化一下 LLM 的首包耗时,可参见 对话延迟优化。TencentTTS chunk error {'Response': {'RequestId': 'xxxxxx', 'Error': {'Code': 'AuthorizationFailed', 'Message': "Please check http header 'Authorization' field or request parameter"}}}
AgentConfig.FilterOneWord 参数设置为 false (默认为 true)。参数 | 类型 | 描述 |
FilterOneWord | Boolean | 是否过滤掉用户只说了一个字的句子,true 表示过滤,false 表示不过滤,默认值为 true 示例值:true |
枚举 | 取值 | 描述 |
ERR_TRTC_INVALID_USER_SIG | -3320 | 进房参数 UserSig 不正确,请检查 TRTCParams.userSig 是否为空。 |
ERR_TRTC_USER_SIG_CHECK_FAILED | -100018 | UserSig 校验失败,请检查参数 TRTCParams.userSig 是否填写正确或已经过期。 |
枚举 | 取值 | 描述 |
ERR_TRTC_CONNECT_SERVER_TIMEOUT | -3308 | 请求进房超时,请检查是否断网或者是否开启 VPN,您也可以切换 4G 进行测试。 |
ERR_TRTC_INVALID_SDK_APPID | -3317 | 进房参数 SDKAppId 错误,请检查 TRTCParams.sdkAppId 是否为空。 |
ERR_TRTC_INVALID_ROOM_ID | -3318 | 进房参数 roomId 错误,请检查 TRTCParams.roomId 或 TRTCParams.strRoomId 是否为空,注意 roomId 和 strRoomId 不可混用。 |
ERR_TRTC_INVALID_USER_ID | -3319 | 进房参数 UserID 不正确,请检查 TRTCParams.userId 是否为空。 |
ERR_TRTC_ENTER_ROOM_REFUSED | -3340 | 进房请求被拒绝,请检查是否连续调用 enterRoom 进入相同 ID 的房间。 |
枚举 | 取值 | 描述 |
ERR_MIC_START_FAIL | -1302 | 打开麦克风失败,例如在 Windows 或 Mac 设备,麦克风的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。 |
ERR_SPEAKER_START_FAIL | -1321 | 打开扬声器失败,例如在 Windows 或 Mac 设备,扬声器的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。 |
ERR_MIC_OCCUPY | -1319 | 麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。 |
文档反馈