# 接入形式

为了简化设备对接难度,提升方案的集成度、安全性,我们提供设备SDK植入的方式进行对接。我们把设备SDK称为微信小微SDK(xiaowei SDK),ASR、NLP、Skill等绝大部分能力整合在云端。xiaowei SDK通过简单有效的接口,提供外部服务,使接入设备具有智能对话、微信音视频通话、微信小程序、IoT设备控制等能力,并能通过SDK来获取QQ音乐、FM、新闻、天气等资源。

在xiaowei SDK中,主要包括以下部分:

  • 底层框架(接口以device开头)
  • 小微Core(接口以xiaowei开头)
  • 音视频通话(接口以voip开头)
  • 微信消息(接口以wechat开头)
  • 小程序框架(接口以miniapp开头,暂未开放)

微信小微是一个大平台,它底层基于微信框架,负责SDK的底层接入,账号体系认证管理,并维护终端与后台的网络通道。底层除了支撑小微核心业务(xiaowei Core)外,还可以为其它业务(如voip)提供runtime。因此微信小微SDK提供给用户的是一个能力集合,在这个集合中除了小微以外,还包括微信音视频通话插件(VOIP)、微信小程序运行框架和微信消息收发。

architecture

小微Core是小微SDK中最重要的部分,它为设备提供智能对话能力,并且整合QQ音乐、企鹅FM、新闻、天气、百科、IOT控制等内容和资源。您可以使用小微与设备进行语音交互、点播音乐,也可以使用小微发起音视频通话、拉起小程序或发送微信消息。

# SDK提供的功能

# 小微的基础对话能力

小微设备APP接受音频或文本数据,对于音频数据进行实时的云端ASR解析。

# 小微丰富的媒体资源

# 微信音视频通话

为了简化交互逻辑,小微设备APP封装了音视频通话(voip)和联系人功能,这部分的activity和UI已经由小微完成,并适配了大部分分辨率,细节接口不对外开放。您可以通过指令来拉起联系人扫码绑定、联系人操作和voip通话界面。如果您发现我们的UI在您的设备上适配有问题,请联系我们解决。

# 腾讯视频语音控制

# SDK目录结构

目录 内容
/DeviceSDK/release SDK二进制文件
/DeviceSDK/interface C风格的SDK接口
/Device/CtrlModule 开源的控制层
/Device/Demo_Android 开源的安卓Demo工程以及控制层和SDK的JNI代码
/Device/Demo_Mac Xcode测试工程,同时也是linuxDemo

# 详细指引

上述内容的详细指引请参考以下说明,在此之前,建议您先阅读后面的内容,以便对SDK有整体的了解。

# 登录并启动小微服务

接入小微前,首先需要在小微的官方平台上注册,并获得产品标识ProductId(PID)。PID唯一标识了您的产品,假如您有两款不同的产品,建议申请两个PID,OTA以及其它定制服务以PID为粒度。

小微后台采用签名认证方式来确保您的设备的合法性。对于每一个PID,您需要使用我们提供的shell脚本或c++算法源代码或windows工具来自己生成一组秘钥对,并将公钥publickKey提供给我们;对应的私钥privateKey您应妥善保存,后续用于对您的设备进行签名,签名算法为ecdsa,具体信息可以参考对应的readme。我们获得公钥后,会为您分配一个秘钥版本KeyVersion(未来您可以更新秘钥并替换版本)。这个流程在网站上有详细说明,这里不赘述。

# Login参数

在与设备APP建立连接后,需要调用登录接口,需要传入以下5个参数:

  • SN 设备serial number,字符串,应保证每台设备拥有唯一的SN
  • Licence 利用私钥对SN的签名结果(SN = ecdsa(SN, privateKey)),显然这也是设备唯一的
  • KeyVersion 签名使用的秘钥版本,在官网上传publicKey时获得
  • PID 一个系列的产品PID均相同
  • appuin 与PID对应,注册时获得

# 特别说明

  • 一般的,privateKey应保存云端或产线中,在设备生产时生成上述5个参数,并烧录在设备中。建议不要将privateKey和签名算法保存在设备中,这样安全无法得到保证。
  • 在测试阶段,我们可以提供少量的测试账号供您直接使用。
  • 切勿多台设备以相同账号登录,会导致互相踢掉线

# 语音请求

# 采音

在初始化完成后(DEVICE_NOTIFY.on_login_complete(0)),即可调用小微相关接口来发起小微语音请求。语音请求对音频数据的要求和注意事项:

  • 16位,16kHz,单声道pcm数据
  • 分片提交,每片大小为64~6400个数据点
  • SDK不会对声音进行压缩之外的任何处理,设备需要自行实现软硬件降噪和回声消除,以便达到更佳的识别效果
  • 小微团队会对设备的采音质量进行评估,您的设备采音质量需要达到相关规定

# 唤醒

设备唤醒由厂商自行实现,也可使用小微团队提供的唤醒方案。小微同时支持本地唤醒云端校验功能,可参考接口具体文档。在某些情况下,小微会主动要求设备唤醒,如多轮对话,此时厂商应按照小微的要求直接唤醒设备,并发起语音请求。注:当闹钟响起时,无屏设备唤醒或触碰任意按键即结束闹铃,有屏设备唤醒或触碰按钮即结束闹铃。

# 发起请求

# 简介

文本请求只需调用一次请求接口,语音请求则需要多次调用,以语音请求为例,您需要多次使用xiaowei_request(...)接口不断将语音数据发送给小微SDK,SDK会将数据压缩并上传至后台进行识别,在这个过程中,您会通过相应的回调接口(on_request_callback(...))收到一系列的ASR事件,如检测到说话,获得ASR结果等。在设备端可以进行相应的UI展示。一般的语音请求流程如下:

ASR

其中 Event 一般都会遵循如下顺序回调:

  1. xiaowei_event_on_request_start (一轮对话开始)
  2. xiaowei_event_on_speak(开始说话)
  3. 多个xiaowei_event_on_recognize (持续收到中间结果)
  4. xiaowei_event_on_silent (静音)
  5. xiaowei_event_on_response (收到响应)
  6. xiaowei_event_on_idle (一轮对话结束)

如上文所述,int xiaowei_request(char *voice_id, XIAOWEI_CHAT_TYPE type, const char *chat_data, unsigned int char_data_len, XIAOWEI_PARAM_CONTEXT *context)是小微最关键的接口,语音或文本数据从这里输入。

当语音或文本请求时,每一轮请求有个唯一的ID(voice_id, 需要先初始化长度为33的char,并将指针传入,接口返回时会memcpy voiceID到该指针),将返回空的ID。在一轮语音请求中,您需要多次调用该接口,每次返回的voiceID均相同。这个接口是一个同步接口,返回0意味着请求成功发送。在请求过程中,会有若干事件从on_request_callback(...)接口中返回。

XIAOWEI_PARAM_CONTEXT 是请求的上下文信息,每次请求都需要携带,这是维护多对话的标识,同时也配置了对话的一些属性,首轮请求时context应为空,多轮请求时应携带上一轮请求最终响应中的这个参数。在每一轮请求中,第一次调用xiaowei_request(...)时应将context.voice_request_begin置为true。

# 注意

考虑到性能原因,小微SDK的请求和回调使用同一线程,因此,在小微的回调函数中,不应再直接调用小微SDK的接口,否则可能导致死锁。如果某些情况下线程切换的确有难度或者开销过大,请单独咨询我们。

# 特别说明

小微SDK提供文本请求的方式,即跳过ASR这一步,此时每次请求您只需要调用一次请求接口即可,此时requestData即为请求文本:

xiaowei_request(xiaowei_chat_via_text , requestData, context);

# 流程总结

这里通过下面一张图,简要总结一下使用小微的流程 step

  1. 初始化小微device,并等待成功
  2. 自己唤醒,与小微无关
  3. 调用request接口发送第1个语音包
  4. 收到on_request_start回调
  5. 调用request接口发送第2个语音包
  6. 调用request接口发送第m个语音包
  7. 收到on_speak回调
  8. 调用request接口发送第n个语音包
  9. 收到on_recognize回调,拿到ASR实时结果
  10. 调用request接口发送第k个语音包
  11. 收到on_recognize回调,拿到ASR实时结果
  12. 调用request接口发送第p个语音包
  13. 收到on_silent回调,停止发送语音包
  14. 等待一小会儿
  15. 收到on_response回调,获得NLP结果和资源
  16. 对小微而言,对话到此结束,收到on_idle
  17. 用户自己处理资源和控制命令(或者是我们提供的开源控制层)
  18. 结束

上述流程是一个最标准的流程,当然在这个过程中可以主动停止对话,或者主动发送静音包,这在接口说明中有详细描述。如果用户发起的是文本请求,那么上述过程第3步就变成了发送文本请求包,5到第13步就不存在了。

# 最终响应

在上述中的流程图中,有一个on_response事件,即在回调中拿到了最终响应,也就是获得了NLP的结论和资源。这里详解一下response的数据结构,即XIAOWEI_PARAM_RESPONSE

# XIAOWEI_PARAM_RESPONSE详解


	/**
     * 场景信息,请求命中了哪个技能,根据不同的技能做不同的响应,如音乐就打开播放器播放
     */
    XIAOWEI_PARAM_SKILL skill_info;
    
    /**
     * 上一次的场景信息,(也就是当前的场景)
     */
    XIAOWEI_PARAM_SKILL last_skill_info;
    
    /** 全局错误码,参考deviceCommonDef.h */
    int error_code;
        
    /** voiceID */
    char voice_id[33];
    
    /**
     * 上下文信息,关注里面的sessionID,如果非空则说明对话未结束,设备应该继续发起请求,并携带此context
     */
    XIAOWEI_PARAM_CONTEXT context;
    
    /** 最终识别出的请求文本 */
    const char *request_text;
    
    /** response_data的类型定义,除非特别定制,否则无需关注 */
    unsigned int response_type;
    
    /**
     * 响应扩展数据,json格式
     */
    const char *response_data;
    
    /**
     * 用户扩展的意图信息,json格式,用户定制的需求在这里以json形式返回
     */
    const char *intent_info;
    
    /** resource_groups的大小 */
    unsigned int resource_groups_size;
    
    /**
     * 资源集合list,注意这是一个指针,也就意味着当callback函数return时,其资源会被释放,需要您自己hold这些资源。
     */
    XIAOWEI_PARAM_RES_GROUP *resource_groups;
    
    /**
     * 向下是否有更多资源
     */
    bool has_more_playlist;
    
    /**
     * 是否有历史记录,如果有,就可以拉取历史
     */
    bool has_history_playlist;
    
    /**
     * 资源是否可以暂停恢复
     */
    bool is_recovery;
    
    /** 这个响应的资源是通知或者提示,不应该影响当前该场景的列表变化,只是插播一下。例如音乐场景中询问"现在在放什么歌","周杰伦 稻香"这个TTS就是这种资源。 */
    bool is_notify;
    
    /** 唤醒结果,XIAOWEI_WAKEUP_FLAG,0表示非该类结果,1表示唤醒校验失败,2表示唤醒成功并且没连续说话,3表示说了指令唤醒词,4可能为中间结果,表示唤醒成功了,还在继续检测连续说话或者已经在连续说话了。
 */
    unsigned int wakeup_flag;
    
    /**
     * 资源列表拼接类型,拼接到当前列表前面、后面、覆盖列表等
     */
    XIAOWEI_PLAYLIST_ACTION play_behavior;
    
    /**
     * 资源列表类型,可能为当前列表、历史列表等类型
     */
    XIAOWEI_PLAYLIST_TYPE resource_list_type;
    
    /** 响应文本 */
    const char *response_text;
    
    /** 控制指令,例如播放、暂停、音量大,XIAOWEI_DEVICE_CMD */
    unsigned int control_id;
    
    /** 控制指令对应的控制值 */
    const char *control_value;
    
    /**
     * 向上是否有更多资源
     */
    bool has_more_playlist_up;

# skill_info

这轮请求命中了哪一个技能,如音乐、闲聊等,一般来讲用户应根据命中的不同技能进行不同的操作,如打开音乐播放器,加载UI等。

# last_skill_info

这个和skill_info结构一样,标识了您当前处于的skill状态,这个状态事实上是依赖于您的状态上报的,这在后续有详细说明。

# voice_id

这个响应针对哪一次请求,voiceID唯一标识了一次请求。

# context

上下文信息。每次请求您都需要携带一个context,同样在返回时您会获得一个context。在context中维护了多轮对话的状态,当您收到响应时发现context中申明了id和speak_timeout时,则表明这是一个多轮对话,在下次请求的时候带上这个id标记。关于context的具体说明请参考接口说明。

# response_data

这个字段主要针对一些自定义技能,小微APP在responseData中返回某些技能所需的额外数据或资源。

# intent_info_for_user

这个字段向用户暴露意图槽位以及用户定制的一些返回。该字段默认不返回任何数据,如果您需要定制化某些内容,请与商务联系。

# resource_groups

小微返回的资源全部位于这里。 resource_groups是一个二维数组,在每个元素中又有若干个resources(XIAOWEI_PARAM_RESOURCE),resources代表一个具体的资源。

在正常情况下,是按照二维数组的顺序一个一个进行播放。如果发生用户主动切换“上一首”、“下一首”操作,则需要按照Group这个一维数组的维度进行切换。具体的意义可以参考下图:

正常情况下先播放resource_groups[0].resources[0],然后播放resource_groups[0]. resources[1],当第一个resource_groups的资源全部播放完毕之后,播放第二个resource_groups。而当切歌时,则应该从resource_groups[0]直接切换到resource_groups[1]

# has_more_playlist

这个字段表示是否还有更多的资源可以拉取,在某些skill下,例如音乐,点歌时只会返回若干首(默认6首)资源,当您快要播放完毕的时候,可以调用资源拉取接口来获得更多的资源。

# has_history_playlist

在某些sikill下(目前只支持音乐),我们还为您维护了历史播放列表,当用户使用上一首功能到第一首的时候,可以调用拉取历史列表的接口来获取历史歌单。

# recoveryAble

当前资源(resource_groups)被打断后是否应该继续播放。例如音乐一般就是可以继续播放的,当设备播放音乐时,如果与小微闲聊几句,聊完之后音乐应该恢复播放。天气就是一种不能恢复播放的资源,当询问小微天气后,小微正在播报天气的过程中如果打断她,后续就不应该再播放天气了。

# is_recovery

这个字段表示当前资源是否是一个notify资源。对于notify资源,不应该对当前的资源列表产生任何操作,这里在下面play_behavior里面有体现。notify资源应该单独起一个线程去播放,播完就释放。在我们的Android Demo中,当收到notify资源时,当前正在播放的资源音量会降低到20%,然后另起一个播放器播放notify,二者叠加。当notify播放完毕后,当前资源恢复100%音量。常见的notify例子是,播放音乐的时候对小微说收藏这首歌,然后收到notify:"收藏xxx成功"。

# play_behavior

资源列表拼接类型,如替换当前列表、插入尾部、插入头部等。

# resource_list_type

当前列表或历史列表,某些技能,如音乐,会在云端存储播放的历史列表,以便每当用户主动想要听上一首时,永远能够回到他真正听过的上一首。这个依赖于设备的状态上报。当存在历史列表时,设备可以主动调用通用请求接口去拉取,一般来说历史列表不参与播放循环。

# response_text

小微回答对应的文本,一般来说resource_groups[0].resources[0]就是这句话的TTS。

# control_id

控制指令ID,例如播放、暂停等。当这个值为0时,表示默认操作,也就是按顺序播放(处理)所有资源。注意当某次返回既有资源又有control_id时,应该先按照play_behavior处理资源,然后再执行控制指令。

# control_value

与control_id对应,某些控制没有control_value,例如暂停。而某些控制则有,例如快进到某个位置,这个value就是快进到的offset值。

# 状态上报&厂商约束

由于控制层是开源的,小微SDK并不知道您的设备当前状态。另一方面,您的设备可能有物理按键,用户可以通过按键来对设备进行操作,这个过程小微SDK也是不知道的。然而很多场景下,小微SDK是需要知道设备状态的,如小程序可以实时显示设备状态,又例如您询问小微,这首歌是谁唱的,小微是需要知道设备当前播放状态的。

# 厂商约束

由于上述原因,我们要求您必须按照我们的规则来上报当前设备状态,否则你的服务将得不到保证。状态上报接口为:

  • int xiaowei_report_state(XIAOWEI_PARAM_STATE *state);

您应该在设备状态变化或者收到需要上报的控制指令之后及时上报,只需要上报大资源,即打断后能够恢复的资源(is_recovery = true),例如音乐,FM。无需上报小资源,例如TTS播放。这里列举一下常用场景的上报:

  1. 切歌:报2次,首先第一首歌abort、然后第二首歌start(如果可以的话,先报preload,再start)。
  2. 播放过程中唤醒了开始语音请求(这时候正常音乐是要暂停的):上报paused
  3. 操作2点了一首新歌,并且要准备播放了:先报刚才那首暂停的歌abort,再报新歌start
  4. 操作2问了一下天气或者闲聊:首先无需任何上报,音箱播放天气或者闲聊的TTS,对话结束之后正常逻辑应该恢复播放音乐,然后这时候上报resume。
  5. 该播放的东西播完了,无事可做:上报idle
  6. 一首歌自然播放完毕,开始播放下一首:上报第一首stop,再上报第二首start
  7. 所有的URL资源,如果可以的话,start之前先报preload
  8. 播放列表资源全部播放完毕,退出播放,上报finished,注意携带skilid。此时如果自动去播放别的了,就该报什么报什么,如果什么也不播放了,就上报idle。

服务器有时会主动下发一些消息推送,当设备收到推送的消息时,需要设备去处理播报tts。我们要求您必须按照我们的规则去处理。否则你的服务将得不到保证。如:开机提示;iot设备控制结果或查询结果的推送等。注:服务器会过滤,避免在休息时间下发消息。

# 微信互动

微信互动支持的资源类型包括音乐、图片、文件、视频、地理位置、公众号文章。

音乐资源复用音乐技能,使用“音乐”skillId和数据格式。

其他资源类型使用“微信互动”技能skillId:“8dab4796-fa37-4114-1000-7637fa2b0001”。设备通过解析resources来获取分享的具体内容。

format标识资源内容类型,包含:

/** @brief Resource format define*/
typedef enum _xiaowei_resource_format {
  ...
  xiaowei_resource_image_share = 10,  /// Share image*
  xiaowei_resource_file_share = 11,  /// Share file*
  xiaowei_resource_poi_share = 12,  /// Share poi*
  xiaowei_resource_video_share = 13,  /// Share video*
  xiaowei_resource_url_share = 14,  /// Share url*
  ...
} XIAOWEI_RESOURCE_FORMAT;

extend_buffer放置公共信息,例如分享人昵称,分享人头像。

{
    "share_user": {
        "nickname": "",
        "headimgurl": ""
    }
}

content为资源内容,下面具体介绍每种资源的格式

# 图片

{
    "download_url": "",
    "enckey": "0"
}

下载后的图片是加密的,需要根据enckey解密后才能展示。 解密接口

# 文件

{
    "type": "pdf",
    "name": "xxx.pdf",
    "size": 0,
    "md5": "0",
    "download_url": ""
}

无加密,下载后需要更改文件名和文件后缀

# 视频

 {
    "type": "",
    "size": 0,
    "md5": "",
    "url": ""
}

无加密

# 地理位置

{
    "latitude": 39.983982086,
    "longitude": 116.308128357,
    "scale": 15,
    "label": "北京市北京市海淀区北三环西路66号",
    "name": "彩和坊路"
}

# 公众号文章

{
    "url": "",
    "title": "",
    "des": "",
    "appname": ""
}