# 交互约定(客户端&服务端)
客户端通过指令和事件和iFLYOS云端交互。管理输入输出优先级的规则就是交互模型。
# 事件上报
事件是从设备端发送给iFLYOS的消息,通知服务端设备上发生了某些变化。最常见的事件是来自用户的语音请求。
# 生成事件
事件由两个部分组成,一个是可选的上下文内容,一个是必选的事件内容。
{
"context": [...],
"event": {
"header": {...},
"payload": {...}
}
}
# 上下文内容
事件上报时,上下文字段是由若干上下文项组成的数组,用于描述设备当前的状态。例如以下内容用于描述扬声器的相关状态
{
"header": {
"namespace": "Speaker",
"name": "VolumeState"
},
"payload": {
"volume": 78,
"muted": false
}
}
多个不同的描述组成了上下文内容。
# 事件内容
事件的样例如下:
header
┣ namespace 事件类型
┣ name 事件具体名称
┣ dialogRequestId 会话 id
┗ messageId 事件消息 id
payload
┣ ... ...
┗ ... ...
header
用于描述事件
- 事件的具体分类由
namespace
及name
来确定,用于标识事件内容所属分类。 - 事件内容通过
dialogRequestId
及messageId
来标识唯一性,dialogRequestId
决定了与该事件相关联的会话中所返回指令集的dialogRequestId
,messageId
用于标识事件本身。设备端建立事件可通过随机算法来生成dialogRequestId
及messageId
,并需要保证其唯一性。
payload
存放事件的具体数据
# 指令执行
# 概述
设备端在使用过程中会接收到iFLYOS下发的指令,并需要按照约定好的逻辑执行这些指令。所有处于同一会话中的指令有顺序地都包含在一个集合中,称为指令集。
# 内容解析
指令的header
包含指令内容描述:
header
┣ namespace 指令接口类型
┣ name 指令接口具体名称
┣ dialogRequestId 会话 id
┗ messageId 指令消息 id
其中,接口类型及接口具体名称用于确定具体的执行指令,例如namespace
为AudioPlayer
,name
是play
的指令,应当指令音频播放接口的播放能力。
header
中携带同一dialogRequestId
的指令视为同一指令集的指令。包含dialogRequestId
的指令属于用户上报事件(例如SpeechRecognizer
中的Recognize
事件)触发的指令集,此时dialogRequestId
与上报的事件中的dialogRequestId
相同;不包含dialogRequestId
的指令应当立即执行。
messageId
是一个指令的唯一标志。
# 执行顺序
在同一指令集中的指令,应当按照接收顺序依次执行,上一个指令执行完毕才可以开始执行下一个指令。特别地,对于SpeechSynthesizer
的Speak
指令,应当等待播放完成之后才视为指令执行完毕。
当设备端正在执行尚未完毕的指令集时,如果接收到不同dialogRequestId
的指令,应当终止并抛弃尚未执行完毕的指令,并开始执行新的指令集。
不论何时,接收到不带dialogRequestId
的指令时,设备端应当立即执行该指令,且在资源不冲突的情况下不影响其他正在执行或待执行的指令。
注意
当指令无法正确解析或者执行的时候,需要上报System
接口中的ExceptionEncountered
事件。
# 客户端交互
客户端和云端的交互有两种方式:
- 一般情况下,交互从客户端开始。客户端发送事件到云端,云端处理事件并返回 0~n 个指令给客户端。例如:用户询问:“现在几点?”,客户端流传用户语音至服务端,iFLYOS处理该事件后,将下发一个播放TTS的指令给客户端。
- 有的时候,交互也会从云端开始。客户端会在没有上报事件的情况下收到云端下发的指令。例如:当一个用户通过关联APP调整了音量,虽然客户端(设备)没有发送任何事件给云端,但设备将会收到调整音量的指令,此时客户端也需要根据云端指令做出相应的操作。
注意
- 当iFLYOS更新时可能会造成客户端接收到新的/未知的指令。这就要求你的代码需要弹性面对这些变化,当收到未知指令时设备不会出现错误。
- 当iFLYOS添加新特性时,可能会在保证原有指令正常运行的情况下,在指令的payload中增加新参数。这就要求你的代码需要弹性面对这些变化,当收到的指令payload中包含未知的参数时,客户端不会出现错误。
# 语音请求的生命周期
你需要确保在任何时间,客户端都只有一个活跃的语音请求。这要求你的客户端在每一次Recognize
事件中必须生成一个唯一的dialogRequestId
,并跟踪活跃的dialogRequestId
,这个dialogRequestId
将会出现在服务端下发的对应指令中。
注意
在一个会话中,不要重复使用dialogRequestId
,每一次Recognize
事件都需要生成新的唯一的dialogRequestId
。
在下一次上报Recognize
事件时(无论是新的请求还是云端下发了expectSpeech
指令),都必须生成新的dialogRequestId
,与之前活跃的dialogRequestId
相关联的指令必须执行或丢弃。
上报事件至云端时,客户端需遵循以下原则:
- 对每一个
Recognize
事件,你都需要创建一个唯一的dialogRequestId
,并在一个会话内,dialogRequestId
不能重用。 dialogRequestId
必须包含在Recognize
事件的header中。dialogRequestId
在下一次Recognize
事件前都需要是活跃状态。当新的Recognize
事件发生时,和旧dialogRequestId
相关联的所有指令都需要被执行或丢弃。
接收云端指令时,客户端需要遵循以下原则:
- 云端下发的指令header中包含
dialogRequestId
,和当前活跃的dialogRequestId
一致,这些指令需要按顺序执行。 - 没有带
dialogRequestId
的指令需要被立即执行。 - 接收到未知指令时,客户端需要上报
ExceptionEncountered
事件,但客户端本身不应该向用户报错。 - 接收
Speak
指令时,需要完成播放关联音频,除非播放过程被新的指令打断。
# 音频通道
设备音频可以分为三个通道:对话通道,提醒通道和内容通道。通道用来管理音频输入输出的优先级,每一个通道都有活跃和非活跃状态。
通道和设备能力接口的对应关系:
通道 | 设备能力接口 |
---|---|
对话通道 | 语音识别、语音合成 |
提醒通道 | 提醒 |
内容通道 | 音频播放器 |
当有语音输入或输出时,对话通道活跃;当闹钟/提醒/计时器开始响铃时提醒通道活跃;当有音频播放时内容通道活跃。
通道分为前景(foreground)状态和背景(background)状态,在任何时间,只能有一个通道在前景。当多个通道都活跃时,按照通道优先级:对话通道 > 提醒通道 > 内容通道,优先级高的通道在前景,优先级低的通道在背景。在最高优先级的播放通道播放完毕并退出活跃状态后,下一优先级的播放通道从背景移到前景。
客户端需要遵循的音频通道原则:
- 不活跃的通道永远在背景
- 对话通道活跃时一定在前景。
- 提醒通道只有在对话通道不活跃时会在前景。
- 内容通道只有在其他通道都不活跃时才会在前景。
- 当前景通道变为非活跃时,下一个优先级的活跃通道变为前景。