所见即可说
目录
1 简介
1.1 介绍
所见即可说指的是电视助手在应用的不同界面状态下,当用户触发语音交互时,语音助手获取当前界面热词元素,当用户说指定热词元素时,语音助手发送语音指令给当前界面,由当前界面解析语音指令完成业务处理,从而达到所见即可说的效果。为了解决大屏交互的痛点,支持预定义指令,包括但不限于:播放控制,选集,翻页跳转。
1.2 技术方案
交互流程图:
1)启动语音交互时,语音助手会通知当前的前台应用来提交场景热词。
2)应用通过所见即可说能力接口将当前的场景信息提交给电视助手。
3)电视助手做语音识别时将结合场景信息,优先命中当前应用场景下热词并通知应用,应用收到指令后,完成业务处理。
4) 应用根据业务需要,决定是否需要反馈给语音助手继续执行(语音助手界面实现显示、播报和关闭的功能)。
2 技术对接
2.1 集成AAR文件
应用集成讯 飞提供的 OpenPlatformSDK-release.aar 文件 , 用于场景交互和在场景交互下,应用 在语音助手界面实现显示、播报和关闭的功能。
2.2 实现ISceneListener接口
在应用中对应的界面模块实现 ISceneListener 接口。
示意代码如下:
public class DemoActivity extends Activity implements ISceneListener {
@Override
public String onQuery() {
return null;
}
@Override
public void onExecute(Intent intent) {
}
}
2.3 实例化Scene和 Feedback对象
创建 Scene
和 Feedback
对象,并在Activity 生命周期方法 onResume()
和onPause()
中分别调用 Scene.init(this)
, Scene.release()
。也就是Activity处于活动状态时与语音助手建立通道,非活动状态断开通道。
注意:
Scene.init(this)
,Scene.release()
需要成对调用。
示意代码如下:
public class DemoActivity extends Activity implements ISceneListener {
private Scene mScene;
private Feedback mFeedback;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScene = new Scene(this);
mFeedback = new Feedback(this);
}
@Override
protected void onResume() {
super.onResume();
mScene.init(this);
}
@Override
protected void onPause() {
super.onPause();
mScene.release();
}
@Override
public String onQuery() {
return null;
}
@Override
public void onExecute(Intent intent) {
}
}
2.4 查询场景信息
实现接口ISceneListener
,存在两个方法,其中String onQuery()
方法会在语音语音助手需要查询当前场景信息时自动回调,该方法 请勿做耗时操作 ,执行时间控制在 100毫秒 以内。该方法的返回值为JSON格式字符串。字段定义如下:
字段 | 值类型 | 说明 |
---|---|---|
_scene | String | 场景标识,值命名规则为前面是应用包名+场景名(如界面类名) |
_commands | JSONObject | 场景描述标识。值为热词定义描述。 |
_commands.{自定义键} | Array[String] | 其中{自定义键}是命令 id(给热词组定义的键),值为该功能的各种可能的说法,分为如下三类: 模糊词:即模糊匹配的热词,语法:短语即可(如:"关闭","退出")。 精确词:即精确匹配热词,语法:添加前缀"^"符号(如:“^3D游戏”)。 预定义命令:即预先定义的热词,语法:"$P"开头(如:$P(_PLAY))。详细定义见下文。 |
注意事项:
- 场景字符串长度控制在 1万 以内,超过此值热词可能不生效,。
- 优先使用预定义命令,效果会更好。
- 热词应为页面所呈现元素,请勿注册非当前页面无关元素。
示例如下:
{
"_scene": "com.iflytek.xiri2.MyScene",
"_commands": {
"close": [
"关闭",
"退出"
],
"playCmd": [
"$P(_PLAY)"
],
"3dGame": [
"^3D游戏"
],
"game": [
"游戏"
],
"openDetails": [
"^打开详情"
]
}
}
预定义命令定义
预定义值 | 说明 | _action | 其他参数 |
---|---|---|---|
$P(_PLAY) | 播放相关指令 | / | / |
播放 | PLAY | / | |
暂停 | PAUSE | / | |
继续播放 | RESUME | / | |
重头播放 | RESTART | / | |
跳转到指定位置 | SEEK | position 位置 (整数值型,单位:秒) |
|
跳转到指定时间点 | SEEKTIME | date 日期(例:2019-12-26) time 时间(例:16:25:25) |
|
快进指定时间 | FORWARD | offset 偏移 (整数值型,单位:秒) |
|
后退指定时间 | BACKWARD | offset 偏移 (整数值型,单位:秒) |
|
退出 | EXIT | / | |
$P(_PAGE) | 翻页相关指令 | / | / |
跳到下一页 | NEXT | / | |
跳到上一页 | PREV | / | |
跳到指定页 | INDEX | index 集/期号 (整数值型,负数表示倒数) |
|
$P(_EPISODE) | 选集(期)相关指令 | / | / |
跳到下一集/期 | NEXT | / | |
跳到上一集/期 | PREV | / | |
跳到指定集/期 | INDEX | index 集/期号 (整数值型,负数表示倒数) |
|
$P(_SELECT) | 选择相关指令 | / | / |
选择 | / | row:第x行 (整数值型,负数表示倒数)可选 index:第 x 个(整数值型,负数表示倒 数) |
|
$P(_VOLUME) | 音量相关指令 | / | / |
增大音量 | volume_plus | / | |
减小音量 | volume_minus | / | |
最大音量 | volume_max | / | |
最小音量 | volume_min | / | |
中间音量 | volumeMid | / | |
设置指定音量 | volume | volumeChange: 音量设置值(int) 取值范围 [0,100] | |
降低指定音量 | volumeLower | volumeChange: 音量减低值(int) 取值范围 [0,100] | |
增加指定音量 | volumeAdd | volumeChange: 音量增加值(int) 取值范围 [0,100] | |
静音 | mute | / | |
取消静音 | unmute | / |
2.5 执行场景指令
实现接口ISceneListener
,存在两个方法,其中void onExecute(Intent intent)
方法会在语音语音助手命中当前场景热词时,回调该方法。该方法中参数intent
内部参数说明如下:
1)获取命中命令词 id (命令id) 通过命令 id 结合场景信息,进行下一步业务处理。
// 获取命令 id 注意key
intent.getStringExtra("_command");
2) 获取用户说的话
intent.getStringExtra("_rawtext");
3) 获取场景标识 主要用于判断执行场景和查询场景是否一致,建议添加校验。
intent.getStringExtra("_scene");
4) 如果是预定义命令
结合 预定义命令定义 进行业务处理.
// 获取预定义命令的 action
intent.getStringExtra ("_action")
// 结合参数key及值类型获取
intent.getStringExtra ("<参数名>")
// 或
intent.getIntExtra("<参数名>", 0)
参考示例如下:
@Override
public void onExecute(Intent intent) {
if (intent == null) {
return;
}
// 场景是否匹配,结合业务定义值
String sceneName = "com.iflytek.xiri2.MyScene";
if (!sceneName.equals(intent.getStringExtra("_scene"))) {
// 场景不匹配
retrun;
}
// 关联反馈功能上下文,不调用则反馈无效。
mFeedback.begin(intent);
// 获取命令id,注意key为:_command 而不是 _commands
String commands = intent.getStringExtra("_command");
if ("close".equals(commands)) {
//close--> 模糊词(关闭,退出)
// TODO 处理业务
// 反馈执行结果(按需)
mFeedback.feedback("退出", Feedback.SILENCE);
} else if ("playCmd".equals(commands)) {
//playCmd--> 预定义命令 $P(_PLAY)
String action = intent.getStringExtra("_action");
// TODO 结合 action 判断处理
if ("PLAY".equals(action)) {
// TODO 播放指令处理
// 反馈执行结果(按需)
mFeedback.feedback("播放xxx", Feedback.SILENCE);
} else {
// TODO 继续判断其他指令处理。
}
}
}
5)获取命中的命令词
该功能仅在版本(语音助手基线版本2.6.6及之后) 中支持,用于语音助手匹配了应用注册匹配的热词,注意:预定义命令没有该字段。
intent.getStringExtra("_commandword");
2.6 接口功能说明
类名 | 方法签名 | 说明 |
---|---|---|
Scene | Scene(Context context) | 场景实现,构造方法 |
void init(ISceneListener listenner) | 当前界面和语音助手建立连接,在界面可交互(onResume)的时候调用 | |
void release() | 当前界面释放连接,在对应界面不可交互(onPause)的时调用 以上两个方法要成对使用。 |
|
ISceneListener | / | 场景回调接口 |
String onQuery() | 语音助手向当前界面请求场景信息时回调,并返回场景信息。 | |
void onExecute(Intent intent) | 语音助手命中场景后,通知当前界面执行对应功能的回调。 | |
Feedback | Scene(Context context) | 反馈实现,构造方法 |
void begin(Intent intent) | 应用需要助手需要使用 onExecute(Intent intent) 回调过来的 intent。 |
|
void feedback(String text, int type) | 语音助手界面的反馈。 text:需要反馈的文本 type:反馈类型,主要有以下四种方式: Feedback.EXECUTION : 语音提示执行(立即关闭并同时播报,Toast 提示) Feedback.SILENCE:静默执行(立即关闭,Toast 提示) Feedback.DIALOG: 聊天对话(播报并保持) Feedback.ERROR: 错误反馈(播报后关闭) |
3 常见问题
- 热词匹配不准确,如何提高匹配准确度?
注册场景指令时,除了预定义指令外其余的都是应用注册的词汇,这些词汇是通过命令词匹配的方式实现响应的,且默认是模糊匹配,这就会存在注册“退出XXX应用”时,用户说“打开XXX应用”也会响应“退出XXX应用”的执行,很显然这不符合用户的预期,因此这种指令需要使用精确匹配,即在前面加上^前缀。
建议:对于内容性的词汇,比如页面展现的栏目内容等等使用模糊匹配,对于指令操控性的词汇,比如打开、退出、关闭等等,使用精确匹配。
- 热词是灵活的无法枚举列出,比如快进5分钟,该如何实现?
试试 预定义命令 ,它是讯飞统一实现的一些通用的指令,通过对接它,应用方可以更加快捷的实现相关功能,且这些指令不同于应用注册的命令词,它能够区分语义,因此不会出现命令词注册时由于模糊匹配到其他指令而导致实际执行指令不是用户预期指令的问题。在预定义命令不能满足需求的时候,可以再使用独立注册指令补充实现。
- 页面中注册大量热词,导致识别卡顿,体验不好,如何规避?
页面不应该注册与当前页面场景不符的热词,或者不支持的指令。例如:非播放页面不支持播放、暂停等操作,不应该注册播控相关的指令。另外注册的热词应为用户可见的热词,而不是用于搜索存在的大批量热词。同时语音助手对注册的热词添加限制,当达到限制阈值时,可能被忽略。甚至不合理的注册大量热词,引发崩溃。
建议: 尽量仅注册页面显示元素,并结合业务是否需要使用预定义命令来实现。