在智能语音交互发展的过程中,多模态交互是一个必经阶段。所谓“模态”,英文是modality,用通俗的话说,就是“感官”,多模态即将多种感官融合,即通过文字、语音、视觉、动作、环境等多种方式进行人机交互,充分模拟人与人之间的交互方式。
那么,DuerOS是如何支持多模态交互的呢?一个集中的体现就是DuerOS 新推出的DPL 2.0。
DPL 是 DuerOS Presentation Language 的缩写,是为面向有屏音箱的技能开发者提供的一个解决方案,采用 DPL 方式开发可以让你能够轻松的创造展现丰富、交互灵活的 DuerOS 有屏端技能。本质上,DPL 是一种领域特定语言(DSL,Domain Specified Language)。
A specialized computer language designed for a specific task. 为了解决某一类任务而专门设计的计算机语言。--- 来自wikipedia
DSL以极其高效的方式描述特定领域的对象、规则和运行方式的语言,需要有特定的解释器与其配合。与通用语言相比能极大降级理解和使用难度,同时极大提高开发效率的语言。DSL 通过在表达能力上做的妥协换取在某一领域内的高效。
为什么不在有屏音箱上可以直接支持H5开发语音技能而采用DPL呢?本质上,是资源使用效率和运行效率的折衷。尽管DuerOS是直接支持H5开发语音技能的,但是用DPL 来开发语音技能和多模态交互更加高效,从而可以获得更好的性能效率,从而获得更好的用户体验。
DPL 提供了一套完整的有屏幕智能语音技能开发解决方案,使用 DPL 可以在开发技能中提供如下的优势:
◦ 丰富的组件资源:
DPL 以组件的形式定义页面布局与事件交互,可以使用 文本、图片、Pager、视频、音频多种的组件来构建你的技能模板,并可通过在页面或组件中的事件触发或基于服务端下发的指令执行,完成所希望实现的完整交互逻辑;
◦ 弹性的设计:
DPL 中的内容和布局展现具有灵活性,可以通过指令来定义和动态切换在不同交互阶段时,你所希望呈现在屏幕上的可视组件以及它们的不同展现内容,来满足所需的不同需求场景,并可非常方便的移植复用到其他拥有类似交互和呈现的其他 DPL 技能中;
◦ 易用性:
为了方便快速上手,我们提供了丰富的、已经在实际应用中稳定可用的 DPL documents 实例模板以及相关的组件实例模板,你可以通过简单的修改来构建属于你自己的技能。尽管 DPL 看起来是一个全新的语言类型,但是它也遵守了一般化的编程规范,语法和协议上也尽可能的兼顾了服务端开发人员与前端开发人员的理解习惯,因此很容易上手熟悉。
DuerOS 在去年就推出了DPL1.0,现在推出的DPL2.0 和 DPL 1.0 有什么区别呢?
DPL1.0 BOT JSON格式 和 终端上渲染的格式差异较大,对解释器的效率有影响。DPL 1.0 的数据表达格式稍显扁平,样式、属性、事件、类型都在一层平铺,导致在组件定义属性时,容易出现命名冲突,且不易检查。另外,DPL 1.0更多是由业务驱动的功能升级,存在一些不合理的设计。
而DPL 2.0 有效地解决了DPL1.0的不足:
DPL 2.0 的整体功能框架如下图:
核心是两个功能指令:RenderDocument 和 ExecuteCommands。
RenderDocument 用于渲染 DPL 模板指令,通过下发该指令在设备端上渲染一个 DPL 页面。
{
"type":"DPL.RenderDocument",
"token": "{{STRING}}",
"document": {{DPL_DOCUMENT}}
}
DPL document 是一个基于 bot 协议的描述、有着完整结构定义的 JSON 对象数据,它定义了协议中对应于在页面中需要渲染展现的动态渲染模板部分(包括在页面上渲染展示的所有组件、数据内容和布局结构)以及设定页面模板的默认配置和交互能力(无交互超时退出时间、预设事件绑定指令执行机制等)。
ExecuteCommands在一个 DPL 页面模板渲染完成后,基于构造 RenderDocument 协议时的事件绑定交互逻辑和用户与服务端的语音交互逻辑,可以在当前页面上接收、执行指令,完成页面内的交互功能,比如可以在页面渲染完之后,对列表进行翻页、滚屏等功能。
{
"type":"DPL.ExecuteCommands",
"token": "{{STRING}}",
"commands": [
{
"type": "{{ENUM}}",
"componentId": "{{STRING}}"
}
]
}
可以执行的指令目前已经扩充到了6类:
其中信息交互指令非常重要,是终端上页面与DuerOS技能服务通信的重要手段。当组件绑定 SendEvent 类型的 Command 时,点击会触发组件上报 UserEvent 事件, 用户可以自定义参数。技能服务可以通过监听该事件类型,基于获取的组件id、事件类型、事件参数与绑定参数,执行下一步预期进行的交互逻辑。UserEvent 的消息样例如下:
{
"type": "UserEvent",
"requestId": "{{STRING}}",
"timestamp": "{{STRING}}",
"token": "{{STRING}}",
"payload": {
"componentId": "{{STRING}}",
"source": {
"type": "{{ENUM}}",
"handler": "{{ENUM}}",
"value": "{{STRING}}",
"selfArguments": ["{{STRING}}", ...]
}
}
}
所有组建都支持CSS盒模型的通用样式。需要注意的是,盒模型的 box-sizing 默认为 border-box,即盒子的宽高包含内容、内边距和边框的宽度,不包含外边距的宽度。设备端样式属性支持布局模型基于 CSS Flexbox,以便所有页面元素的排版能够一致可预测,同时页面布局能适应各种设备或者屏幕尺寸(强烈推荐使用 flex 而非使用绝对定位的方式来进行页面结构的布局)。
设备端样式属性支持 position 定位,用法与 CSS position 类似。为元素设置 position 后,可通过 top、right、bottom、left 四个属性设置元素坐标,支持百分比。
可以按照以下步骤来规划页面的样式:
DPL模板中使用的 dp 单位和 px 单位是1:1的对应关系。模板中应用的默认自适应方式是以设定 viewport中宽为 960dp的方式,并以在document文档协议描述中,基于使用具体样式宽高属性数值对各个组件使用和布局使用,来实现各个设备上以宽度铺满情况下的默认渲染展现。
viewport的示例代码如下:
// dpl 中使用 viewport 的方式
{
"type":"DPL",
"version":"2.0",
"duration": {{INTEGER}},
"renderConfig": {
"viewport": {
"width": 960,
"height": 562
}
}
...
}
端上渲染DPL 模板时,会通过判断是否存在viewport后,通过调用 defineWindow, 去设置当前的屏幕展示宽高(windowWidth = viewport.width || windowheight = viewport.height),注意,在viewport中,当同时存在 width 和 height 属性时,width 属性取值优先(端设定只能基于这两个属性中的其中一个来确定实际视图大小)。
由于使用 DuerOS 且能支持 DPL 模板使用的设备和类型(不同宽高占比)越来越多,通过一套设计无法完美的兼容各类宽高比例的设备,因此,需要 stylesheet 通过使用 mediaquery 的方式,来配置在不同宽高范围内的样式属性,以达到基于不同设备端,进行不同样式渲染展现,更好呈现内容的目的。
DPL2.0 中提供了大量的组件及其示例代码,我们可以轻松地参考范例实现各种的多模态交互,具体的组件列表如下:
其中多个组件在DPL 1.0 的时候就已经提供了有效的支持,这里主要介绍一下DPL 2.0 中的新特性。
语音输入有其特有的便利性,但是在某些场景,触屏输入能够更多地提高精确性。DPL2.0中提高了form表单进行数据提交、状态切换的控制能力, 目前支持接入表单作为可操控表的组件有Input 组件。
示例代码如下:
{
"type": "dpl",
"version": "2.0",
"duration": 66000,
"stylesheet": [],
"dataSource": {},
"events": [],
"abilities": [
{
// 能力类型:表单
"type": "Form",
// 对应能力的id,在组件中若需要接入该表单,需要明确声明:formId:该表单Id
"componentId": "form_validate_code",
"events": {
// 从存在表单项未完成验证到所有表单项均完成验证时触发
"onReady": [{{COMMANDS}}, ...],
// 从所有表单项均完成验证到存在表单项未完成验证时触发
"onPrepare": [{{COMMANDS}}, ...],
// 通过调用 submit 方法,验证通过时触发
"onSubmit": [{{COMMANDS}}, ...],
// 通过调用 submit 方法,验证失败时触发
"onValidateFail": [{{COMMANDS}}, ...],
}
},
...
],
"mainTemplate": {
"items":[
...,
{
"type":"Input",
"componentId": "demo_input1",
"styles": {
...
},
"props": {
// 声明该可用作表单项的组件注册到 componentId 为 form_validate_code 的表单上
"formId": "form_validate_code",
"type": "number",
"theme": "dark",
"maxLength":1,
"placeholder": "",
"value": "",
// 数据验证规则,组件或表单的submit方法提交表单项输入值时会先通过该规则进行验证(正则匹配), 不填默认不验证提交
"rule": {
"pattern": "^\\d{1}$"
},
// 输入组件内部样式
"inputStyle": {
...
},
...
}
]
},
...
}
在语音技能中支持手势交互是一件很酷的事,DPL 2.0可以对当前页面监听识别手势动作,并基于手势动作可触发相应的事件(onPalm, onOk等), 基于事件触发执行绑定指令。
示例代码如下:{ "type": "Gesture", "componentId": "demo-gesture", "props": { "enable": true }, "events": { "onOk": [ { "type": "SendEvent", "componentId": "demo-gesture-ok" }, { "type": "SetProps", "componentId": "demo-gesture-text", "delay": 111, "props": { "text": "识别到有人比划出一个OK手势: ?" } } ], "onPalm": [ { "type": "SendEvent", "componentId": "demo-gesture-palm" }, { "type": "SetProps", "componentId": "demo-gesture-text", "delay": 111, "props": { "text": "识别到有人比划出一个手掌手势: ?" } } ] } }
DPL2.0 提供了一种在端上进行注册语音话术来提供基础页面语音交互能力的规范和基础框架,通过更易使用的API,让用户可以快速在组件上赋予更丰富的语音交互能力。
相对于DBP云端意图语音识别能力,在DPL2.0中可以使用的终端侧语音识别支持具有如下特点:
当注册内容不包含 url的时候, 如果用户的语音请求匹配到了注册的内容,则服务端下发指令到设备端,设备端执行对应的行为(如执行点击(Container)、滚动(List、ScrollView)、翻页(Pager)等)。
当注册内容包含 url(注:url 是以 dueros:// 开头的链接地址)的时候, 如果用户的语音请求匹配到了注册的内容,则云端会根据此意图转换query请求为转发给对应技能的 LinkClicked 事件。
举个例子,如果展示给用户的页面有一个名为“苹果”的按钮, 我们希望用户说“苹果”来执行点击操作,那它的示例代码如下:
{
"type":"Container",
"componentId":"container-box",
"props": {
"enableVoice": true,
"voiceAction": "click",
"voiceConfig": "click.name: 苹果;"
},
"events": {
"onClick": [{
"type":"SendEvent",
"componentId":"container-box"
}]
}
}
关于DPL2.0 中更多有趣的用法,可以在技能开发过程中逐渐体会到的。
作为DuerOS 多模态交互的一种特定领域语言,DPL 以简洁明快的方式提供了高效开发和高效运行的能力。DPL 2.0 仅仅是DuerOS 多模态交互的另一个起点而已,在DPL 中进一步使用本地引擎执行计算的能力已经在路上了。