1. 控制协议说明
腾讯端渲染 Windows SDK 包含了多个独立的工具程序,有 ASR 语音识别,浮层播放器,浮层网页等部分,集成商也可以在此基础上自行开发用用程序,集成数智人的功能。
1.1 连接方式
多个应用程序之间,通过 UDP 协议进行通讯。通讯方式为星型方式通讯,每个应用均与主控连接。主控负责统计各应用的状态,和中转和分发指令。
发送给主控的命令, 除了心跳命令,其他的都会被转发到其他活跃的应用,类似于广播。
1.2 连接端口
主控启动后,会监听 2个 UDP 端口 54200 和 54300。
54300 端口为 UDP 直接通讯端口,命令字直接通过 UDP 数据包进行发送。 命令格式支持 Json 和 Protobuf 二进制数据。
54200 端口为 KCP over UDP 通讯端口,由于 UDP 为不可靠连接,在通讯过程中可能会丢包,所以引入了 KCP 协议,KCP 协议工作在 UDP 协议上层, 实现了包确认机制和流量控制,保证通讯过程中不丢包。命令格式支持 Json 和 Protobuf 二进制数据。
两个端口发送的数据是一样的, 在实现时二选一即可。
一般局域网内应用,采用 UDP 协议已经可以满足日常需求。集成商可根据项目需求和研发周期自行评估使用。
2. 命通用格式
发送和接收的命令报文数据,支持 Json 和 Protobuf 二进制两种。
主控会自动检测命令的格式,主控回复的命令报文格式和最后一次心跳包使用的报文格式相同。
协议数据包的通用结构示例如下:
{"traceId":"8b0fa692a958407c84bed9a0ab52777c","sessionId":"622598dbbe8c4c5eaad2c3856c818d87","senderRole": "UnrealEngine","senderId": "unreal_engine_01","speakCommand":{"actorName":"xiaowei","text":"你好呀"}}
其中第一层的参数结构如下:
参数名称 | 必选 | 类型 | 描述 |
traceId | 是 | String | 追踪单个命令的 ID,必须保证每次调用,都传入不同的字符串,不能传空, 长度必须为32位。 |
sessionId | 是 | String | 追踪整个 Session 的 ID,可以每次调用传入相同的字符串,不能传空, 长度必须为32位。 |
senderRole | 否 | String | 发送者角色,可以区分是那个角色的应用发出来的命令 |
senderId | 否 | String | 发送者 ID, 可以区分是哪个发送者发出来的命令 |
XXXCommand | 是 | Object | 具体的命令参数,后面会详细介绍每个命令 |
了书写简洁,后面的命令会省略 traceID 和 sessionID 字段,在正式使用的时候需要填写的。
3. 心跳和状态报告
UDP 协议是无连接的,主控通过心跳判断对方是否存活。每个应用均需要发送心跳给主控, 主控会定时返回状态报告,告知主控存活,并告知所有连接的节点状态。
3.1 心跳
所有连接主控的节点,都需要每隔1秒给主控发送一次心跳。 超过3秒未发送心跳,主控认为该连接节点下线。 可以通过
-CleanClientDuration=XX
来设置心跳检测的时间。设置为 -1 可以禁止节点下线。
心跳格式为:
{"heartbeat":{"extendedInfoJson": "{}"},}
参数结构如下:
参数名称 | 必选 | 类型 | 描述 |
extendedInfoJson | 否 | String | 节点的额外信息,信息会被主控存储并附加在状态报告中发送给其他节点。 |
主控收到心跳后,会认为该节点为活跃状态,其他节点发送的命令会被转发给活跃的节点。
3.2 过滤器
因为通讯方式类似广播,我们会收到所有节点发送的信息, 但有时我们并不关心某些命令,可以使用过滤器功能,过滤掉某些命令,只处理希望收到的数据。
过滤器通过心跳命令设置, 在发送心跳命令时,添加过滤器参数。
{"heartbeat":{"onlyReceiveMessageList":[1012, 1013],"onlyReceiveSenderRoleList": ["UnrealEngine"],"onlyReceiveSenderIdList":["unreal_engine_01"],},}
参数结构如下:
参数名称 | 必选 | 类型 | 描述 |
onlyReceiveMessageList | 否 | List<int> | 仅接收列表里的命令 |
onlyReceiveSenderRoleList | 否 | List<String> | 仅接收某些角色发送的命令 |
onlyReceiveSenderIdList | 否 | List<String> | 仅接收某个发送者发送的命令 |
这3个过滤器可以组合使用, 如果都不使用的话, 就会收到所有的命令。
其中,每个命令的 ID, 会标在下面的命令介绍中, 也可以参考 proto 文件。
3.3 状态报告
主控每间隔 1 秒会向所有活跃的节点发送状态报告,各节点通过状态报告判断主控存活,也可以通过状态报告判断其他节点的存活状态。
{"statusReport":{"nodeInfoList":[{"nodeRole": "UnrealEngine","nodeId":"unreal_engine_01","ip":"127.0.0.1","port":52108,"dataType":"NODE_DATA_TYPE_JSON","extendedInfoJson":"{}"}]}}
收到的状态报告,其中 nodeInfoList 为活跃节点的信息,包括节点的 IP 地址,端口, 还有心跳包里上报的信息。
4. 播报对话命令
4.1 播报命令(1010 - speakCommand)
播报命令用来控制数智人播报对应的文本,可以配合动作。
{"speakCommand":{"actorName":"xiaowei","text":"你好呀,<insert-action type=\\"left_side1\\"/> 我正在做动作"}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,统一填写 "xiaowei" |
text | 是 | String | 要播报的文本,可以带动作标签 |
播报命令发送后, 数智人会回复播报的状态指令。
如需要插入动作,请获取动作名称列表, 并在播报文本中插入动作标签。 动作标签格式为:
<insert-action type="2hands_forward2" />你好啊</insert-action>
回复指令: 播报开始(1012 - speakStartCommand)
表示开始说话。
{"speakStartCommand":{"actorName":"xiaowei"}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
回复指令:播报内容(1015 - speakContentCommand)
表示正在说话的内容,并带了说话的时间戳, 可以方便显示字幕。 如果文本很长,会收到多条播报内容指令。
{"speakContentCommand":{"actorName":"xiaowei","ttsResult":"[{\\"Word\\":\\"你好\\",\\"T1\\":\\"300000\\",\\"T2\\":\\"4100000\\"},{\\"Word\\":\\"呀\\",\\"T1\\":\\"4100000\\",\\"T2\\":\\"7000000\\"}]"}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
ttsResult | 是 | String | TTS 的结果,也是个 json 字符串, 其中 T1 为开始时间, T2 为结束时间 |
回复指令: 播报结束(1013 - speakFinishCommand)
表示播报结束。
{"speakFinishCommand":{"actorName":"xiaowei"}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
4.2 流式播报命令(1011 - streamSpeakCommand)
流式播报命令用来控制数智人流式播报对应的文本。
{"streamSpeakCommand":{"actorName":"xiaowei","text":"大家好,","sequenceNumber":1,"isFinal":false,"isSmartAction":true,"isSentence":false}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,统一填写 "xiaowei" |
text | 是 | String | 要播报的文本,可以带动作标签 |
sequenceNumber | 是 | Int | 流式请求分片的序号 |
isFinal | 是 | Bool | 是否最后一个流式分片 |
isSmartAction | 是 | Bool | 是否开启智能动作 |
isSentence | 是 | Bool | true 表示为完整子句,不需要服务端分句处理。false 表示需要在服务端分句。 |
流式播报命令发送后, 数智人会回复播报的状态指令。
当isSentence为true时,数智人还会回复子句级别的状态,如 speakSentenceStartCommand,speakSentenceNextCommand 和speakSentenceOverCommand。
当 isSentence 为 false 时,数智人回复的状态指令与非流式相同。
注意:
1. 流式播报命令 streamSpeakCommand 的多个子句的 traceId 需要保持一致,sessionId 也需要保持一致。当一个流式播报结束后(isFinal 为true),需要更换 traceId。
2. 当 isSentence 为 true 时,每个子句都会有
speakSentenceStartCommand
, speakSentenceNextCommand
和 speakSentenceOverCommand
。3. 当 isSentence 为 true 时,当一个子句被服务端处理完成,就会发送对应的
speakSentenceNextCommand
,因此会发生后面子句的 speakSentenceNextCommand
早于前面子句的 speakSentenceStartCommand
的情形。如果希望控制发送速度,可以等收到之前发送子句对于的 speakSentenceNextCommand 回复后,再发送下一个子句。4. 流式播报命令中不能在播报文本中插入动作标签,只能使用 isSmartAction: true 开启智能动作。
回复指令: 播报子句开始(1016 - speakSentenceStartCommand)
表示播报子句开始。
{"speakSentenceStartCommand":{"actorName":"xiaowei","sequenceNumber":1}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
sequenceNumber | 是 | Int | 流式请求分片的序号 |
回复指令: 播报子句结束(1017 - speakSentenceOverCommand)
表示播报子句结束。
{"speakSentenceOverCommand)":{"actorName":"xiaowei","sequenceNumber":1}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
sequenceNumber | 是 | Int | 流式请求分片的序号 |
回复指令: 可以发送下一个流式请求(1018 - speakSentenceNextCommand)
表示可以发送下一个流式请求,在需要进行发送速度控制时使用。
{"speakSentenceNextCommand":{"actorName":"xiaowei","sequenceNumber":1}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
sequenceNumber | 是 | Int | 流式请求分片的序号 |
4.3 停止播报指令 (1030 - stopSpeakCommand)
停止播报指令用来在播报过程中停止播报。
{"stopSpeakCommand":{"actorName":"xiaowei"}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,一定是 "xiaowei" |
发送停止播报指令后,会收到停止完成的指令。
同时之前播报指令也会返回播报完成的指令。
4.4 请求对话指令(1020 - answerCommand)
请求对话指令,用来让数智人回答问题, 问题可以在平台配置。
{"answerCommand":{"actorName":"xiaowei","text":"请介绍下你自己","newRound":true}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,统一填写 "xiaowei" |
text | 是 | String | 要问的问题文本,为纯文本 |
newRound | 是 | Bool | 是否新一轮对话, 每一轮对话有自己的上下文。 如果需要新开一轮对话, 请传 true. 第一次发送对话请求时, 一定要传 true. |
发送请求对话指令后, 会收到对话结果指令和播报相关的指令。
回复指令: 问答完成指令(1021 - answerFinishCommand)
{"answerFinishCommand": {"actorName": "xiaowei","nlpResult": "{\\"Header\\":{\\"RequestID\\":\\"191cd2024a9446968a1360443bcf6434\\",\\"SessionID\\":\\"bjed97692a17150632309977895\\",\\"Code\\":0,\\"Message\\":\\"\\"},\\"Payload\\":{\\"ReplyType\\":\\"yunxiaowei\\",\\"ReplyPro\\":\\"你好呀,请问有什么问题要问我吗?\\",\\"ReplyDisplay\\":\\"你好呀,请问有什么问题要问我吗?\\",\\"InteractionType\\":\\"\\",\\"InteractionContent\\":\\"\\",\\"Uninterrupt\\":true,\\"Muted\\":false,\\"SeqNo\\":1,\\"ContentType\\":0,\\"TtsSupport\\":true,\\"IsFinal\\":true,\\"IsHighLight\\":true}}"}}
参数名称 | 必选 | 类型 | 描述 |
actorName | 是 | String | 演员名称, 现在数智人只有一个演员,统一填写 "xiaowei" |
nlpResult | 是 | String | 问答回复的结果 |
回复的问答结果是在对话平台配置的, 可以配置额外的指令。
其中 nlpResult 数据格式:
{"Header": {"RequestID": "191cd2024a9446968a1360443bcf6434","SessionID": "bjed97692a17150632309977895","Code": 0,"Message": ""},"Payload": {"ReplyType": "yunxiaowei","ReplyPro": "你好呀,请问有什么问题要问我吗?","ReplyDisplay": "你好呀,请问有什么问题要问我吗?","InteractionType": "","InteractionContent": "","Uninterrupt": true,"Muted": false,"SeqNo": 1,"ContentType": 0,"TtsSupport": true,"IsFinal": true,"IsHighLight": true}}
参数名称 | 必选 | 类型 | 描述 |
ReplyType | 是 | String | 回复语类型 cloudAiGpt:腾讯云大模型对话 yunxiaowei:云小微客服 cloudAiWaiting: 首包超时等待话术 cloudAiTimeOut: 超时未返回话术,会话结束 sensitive:敏感内容固定话术 input:纯文本输入或流式文本输入的内容 enhanceText:纯文本驱动匹配上了话术管理中的内容 |
ReplyPro | 是 | String | 用于播报的内容,含 ssml 标签和动作 |
ReplyDisplay | 是 | String | 用于展示在端上的内容,含富文本标签 |
InteractionType | 是 | String | 特殊消息类型 |
InteractionContent | 是 | String | 特殊消息内容,用于下发弹窗、图片等非文本类的特殊消息 |
Uninterrupt | 是 | String | 当前播报句是否可打断 |
Muted | 是 | String | 播报当前句时是否关闭收音 |
SeqNo | 是 | Number | 子句序号,大模型正常文本 SeqNo 从 1 开始,兜底话术从 0 开始 |
ContentType | 是 | Number | 区分内容类型 0:未知 1:普通字符串 2:有序列表 3:无序列表 4:图片链接 5:http链接 6:表格 7:代码块 |
TtsSupport | 是 | String | 当前子句是否播报 |
IsFinal | 是 | String | 是否为最后一句 |
IsHighLight | 是 | String | 是否需要高亮展示 |
5. ASR 命令
5.1 开始和停止 ASR (2000 - asrControlCommand)
控制 ASR 程序启动或停止收音。
{"asrControlCommand":{"enableAsr": true,}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
enableAsr | 是 | bool | 启动或停止 ASR 收音 |
5.2 监听指令:语音识别结果 (2002 - asrResultUpdateCommand)
ASR 程序会持续发出语音的识别结果,包括中间结果。
{"asrResultUpdateCommand":{"text": "今天天气不错","sentenceComplete": false}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
text | 是 | String | 识别的文本 |
sentenceComplete | 是 | bool | 是否是一个完整的句子 |
6. 多模客户端 命令
6.1 预览窗口保持在屏幕顶层 (2003 - bodyAnalysisWindowStayOnTopCommand)
如果需要多模检测客户端程序预览窗口保持在屏幕顶层,在浏览器/播放器/游戏窗口全屏后,需要通过此对动作检测窗口置顶。
{"bodyAnalysisWindowStayOnTopCommand":{"stayOnTop": true,}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
stayOnTop | 是 | bool | 动作检测窗口保持在屏幕顶层 |
6.2 监听指令:人脸和手势识别更新 (2004 - bodyAnalysisResultUpdateCommand)
多模客户端程序检测到人脸和手势的变更后,会通过此命令更新识别结果。
{"bodyAnalysisResultUpdateCommand":{"faceCount": 1,"faceRect": {"x": 100.0,"y": 100.0,"width": 60.0,"height": 60.0},"gestureName": "label_ok","probability": 0.9,"gestureRect": {"x": 300.0,"y": 50.0,"width": 40.0,"height": 40.0}}}
参数说明:
参数名称 | 必选 | 类型 | 描述 |
faceCount | 是 | int | 人脸张数 |
faceRect | 否 | Object | 选中人脸的位置(face_count为0时无效) |
gestureName | 是 | String | 识别的手势名 |
probability | 是 | Float | 识别手势的置信度 |
gestureRect | 否 | Object | 手势的位置(gesture_name为空时无效) |