各大厂商都在推自己的 Serverless 方案,今天通过一个天猫精灵技能开发来试水一下阿里云的云开发平台,整个流程走下来,不是很复杂,给我的感觉是通过阿里云开发平台开发天猫精灵技能,减轻了开发者部署后端和配置后端的环境,整个过程流畅,甚至不需要 IDE,通过浏览器就可以完成,极大降低了上手难度。
是不是很神奇,所以在开始之前,我们应该初步了解一下什么是 Serverless。

什么是 Serverless

Serverless 的全称为 Serverless computing,即无服务器运算,也被称为函数即服务(FaaS),是继 IaaS、PaaS、SaaS 后的一种云计算模型。它以 PaaS 为基础提供一个微型的架构,开发者不需要部署、配置、管理服务器和服务,只需要将代码放到 Serverless 空间中运行,即可提供服务,代码运行所需要的服务器资源均由云平台提供。
使用 Serverless,能真正实现免运维和按量(调用量)计费,降低开发难度,节约运营成本。

先立 flag

我要做什么功能呢,预期的功能如下:

  • 欢迎和帮助:简单的语音交互
  • 实况天气查询:可以查询某地(北京、北京朝阳)的天气情况
  • 生活指数查询:查生活指数、污染指数等
  • 吃什么推荐:选择困难症晚期用户刚需

准备

既然需要查询天气,肯定是需要接入一个天气服务 API,这里我选择的是和风天气 API,免费版的 API 刚刚好可以满足查天气和生活指数等内容,而且通过 GET 即可调用,非常方便。
当然既然是开发天猫精灵技能,天猫精灵少不了,这里我手头只有方糖,所以就是他了。
有了 API 和天猫精灵,剩下的就是账号的事情了,你需要提前到 AliGenie 开发者平台上使用淘宝账号登录,因为后期要真机调试,为了减少不必要的麻烦,最好使用和天猫精灵绑定的淘宝账号。登录 AliGenie 平台后记得进行开发者认证~否则你是不能创建技能的。既然要使用阿里云开发作为后端,还需要注册一个云开发平台,直接使用阿里云账号登录,第一次使用云开发平台需要创建一个团队,在完成团队创建和同意许可协议之后,即可登录到云开发平台。

20200508000927

20200508000944

后端会使用 Node.js 进行开发,如果你对某一种语言熟悉,Node.js 的语言差异并不会成为在实践中遇到的困难。Node.js 教程

开发

基于阿里云开发打造天猫精灵技能应用:个人助理(小助手)。对于天猫精灵技能开发,一般开发流程可以抽象成以下几个步骤。

AliGenie-skill

天猫,当然要吃 🐠🐠🐠 咯~

编写示例对话

根据文章开头我立的 flag 编写示例对话。

  • 欢迎和帮助

    问题:打开“小助手”
    回复:欢迎使用小助手,我现在可以帮你查天气、查生活指数、解决你中午吃什么的问题,其他技能正在修炼中。

  • 实况天气查询

    问题:北京海淀的天气怎么样/保定的天气怎么样/保定的天气如何/北京海淀的天气如何/帮我查查保定市的天气/帮我查查北京海淀的天气
    回复:今天{海淀}天气{晴},温度{26}摄氏度,体感温度{25}摄氏度,相对湿度{60},{东南风}{2}级。

  • 生活指数查询

    问题:查查杭州的生活指数/杭州生活指数查询帮我查查杭州的生活指数/北京海淀生活指数查询/北京生活指数查询/查查北京海淀的生活指数/查查北京的生活指数/帮我查查北京的生活指数/帮我查查北京海淀的生活指数
    回复:今天杭州{天气不错,适宜晾晒。赶紧把久未见阳光的衣物搬出来吸收一下太阳的味道吧!}

  • 吃什么推荐

    问题:帮我推荐今天晚上吃什么/帮我推荐今天中午吃什么/推荐吃什么给我/帮我推荐吃什么/帮我推荐今天吃什么
    回复:今天适合吃{肉夹馍}。

梳理交互流程

因为不涉及到多轮对话,整体的交互流程不是很复杂,如下图。

20200508092748

在 AliGenie 平台配置技能

创建技能

登录 AliGenie 智能应用平台,在应用列表中选择技能,进入技能列表。点击页面右上角创建语音技能,开始创建自定义技能。

20200508093620

20200508093545

这里需要注意的是调用词,用户是可以用“天猫精灵+调用词”来进入技能的,这个调用词可以不同于技能名称。

调用词最好是好记、上口、易识别的词语,因为测试了几次,如果是含有同音词的话,可能会被解析成其他词语,造成无法识别技能。

20200508093836

完成创建,进入技能基本信息页,我们可以看到刚刚创建技能的基本信息。

语音交互模型

在建立语音交互模型的过程中,你会发现下面几个词会一直伴随着你,所以我们应该对他们有一点初步的认识。

  • 语料

    语料可以理解成用户对音箱所说的话,比如“天猫精灵,我想查快递”,这就是一个语料。

  • 意图

    意图是对用户语音输入后想要得到的响应的一种抽象,比如说“天猫精灵,我想要查快递”,我的意图就是查询快递;“天猫精灵,今天天气怎么样”,我的意图应该就是要查天气,一种意图可能有多种表达方式(用户输入),我们应该合理设计用户意图的对话表达。

  • 实体

    一种规范的自然语言短语的集合,可以是常用的词或者一些领域的技术术语,例如“时间”、“地点(省)”、“地点(市)”都是实体。目前 AliGenie 平台提供了公共实体和自定义实体两种实体类型。

  • 参数

    包括实体的值、交互时产生的上下文信息等,在实现回复逻辑时,参数是必要的。

  • 回复逻辑

    用户语音输入后,音箱通过处理,分析出意图、对应实体解析出参数,通过程序处理后,返回给音箱。这就是一个回复逻辑。

在有了这些基础知识之后,我们开始正式创建语音交互模型。

实体

因为在配置意图时,需要在对话表达中标注实体,所以我们应该先配置实体。通过前期示例对话的设计,我们发现该技能应该有 3 个实体:城市、省份和生活指数。AliGenie 很友好的为一些常用的实体定义了公共实体,例如城市和省份,我们就可以直接引入公共实体。

20200508113256

生活指数实体,需要我们自己定义,根据查询和风天气 API 文档,生活指数可以从以下几个方面进行查询(红色标记是免费 API 可以查询的指数类型),所以生活指数实体应该包含这些值。

20200508113617

创建实体,配置实体名称和实体标识。

20200508113739

配置实体的值,我们可以这样理解,当用户表达中包含实体中的某个同义词时,参数会对应成实体值。并以实体标识+实体值的形式传递给后端。

20200508114138

意图

根据需求,该技能应该有 4 个意图,其中 1 个是默认意图,用于引导用户使用该技能。其他 3 个是自定义意图,大家可以按照下图创建意图。

20200508094924

默认意图的配置如下图,只需要填写意图信息,设置为默认意图即可。

20200508111425

以实况天气意图为例,自定义意图的配置入下图。

20200508111522

需要根据前面编写的示例对话,配置单轮对话表达,并将对话中的实体标注出来,AliGenie 会自动帮你配置出参数。

20200508111651

回复逻辑配置

AliGenie 平台给我们提供了三种回复逻辑,因为要使用云开发平台作为后端,这里我们选择 默认逻辑阿里云FaaS ,在操作中设置为默认集合,点击+号展开列表,点击云开发,进入云开发平台

在阿里云开发平台编码

创建应用

通过回复逻辑配置中的云开发入口进入云开发平台会自动创建一个应用。云开发依赖下面 4 个云服务,如果你没有开通这些云服务,可以点击云服务对应的链接进行开通。

20200508115758

一切就绪之后,点击开发,进入 CloudIDE。

20200508115907

20200508120119

开发应用

目前通过云开发平台创建的函数计算环境, Node.js 的运行时是 nodejs6。

云开发平台 demo 代码功能是做回显用的,就是说你说什么,天猫精灵就会回复你什么。我们可以参考一下 demo 代码。

整个后端应用的入口文件是 index.js,同时这个文件也负责分发和组织意图,记得把申请到的和风天气 API 写在这个文件里。

index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 意图
const intentHandlers = require("./intent");

// 和风天气API KEY
global.key = "XXXXXXXXXXXXXXXXXXXXXXXX";

//云函数处理入口
module.exports.handler = function (event, context, callback) {
try {
event = JSON.parse(event);
let strBody = event.isBase64Encoded
? new Buffer(event.body, "base64").toString()
: event.body;
let skillReqParams = JSON.parse(strBody);
let { intentName } = skillReqParams;
// 分发和组织意图
let intentHandler = intentHandlers[intentName];
// 意图处理结果
let result = intentHandler(skillReqParams);
// 构造返回
let response = {
isBase64Encoded: false,
statusCode: "200",
headers: {
"content-type": "application/json",
},
body: result,
};
callback(null, response);
} catch (err) {
callback(err);
}
};

我们需要创建一个文件夹叫 intent,用于存放各种意图相应的处理程序,这里的 index.js 文件定义了都有哪些意图。

intent/index.js
1
2
3
4
5
6
7
8
9
10
11
const welcome = require("./welcome");
const weather_now = require("./weather_now");
const lifestyle = require("./lifestyle");
const eat = require("./eat");

module.exports = {
welcome,
weather_now,
lifestyle,
eat,
};

intent 文件夹中的意图处理程序的文件名应该和 AliGenie 平台侧配置的意图名称保持一致。

以处理实况天气这个意图为例进行代码编写。首先在 intent 文件夹中创建 weather_now.js 文件。

intent/weather_now.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const urlencode = require("urlencode");
const request = require("urllib-sync").request;

function getHeWeather(slotEntities) {
// 处理地理位置信息
let location = "";
let res = "";
for (let i = slotEntities.length - 1; i >= 0; i--) {
location +=
slotEntities && slotEntities[i] ? slotEntities[i].slotValue : "";
if (i != 0) {
location += ",";
}
}
// 查询天气,处理结果
let api =
"https://free-api.heweather.net/s6/weather/now?location=" +
urlencode(location) +
"&key=" +
key;

let weatherData = request(api);

if (weatherData.status == 200) {
weatherData = JSON.parse(weatherData.data);
weatherData = weatherData.HeWeather6[0];
res = `和风天气,专业的天气数据服务,今天${weatherData.basic.location}天气${weatherData.now.cond_txt},温度${weatherData.now.tmp}摄氏度,体感温度${weatherData.now.fl}摄氏度,相对湿度${weatherData.now.hum}${weatherData.now.wind_dir}${weatherData.now.wind_sc}级。`;
}

return res;
}

module.exports = function (request) {
let { skillName, intentName, slotEntities } = request;
let reply = "<NULL>";

try {
reply = getHeWeather(slotEntities);
} catch (err) {
reply = "获取当前天气状态时出现异常,建议您稍后再试。" + err;
}
return {
returnCode: "0",
returnErrorSolution: "",
returnMessage: "",
returnValue: {
reply: reply,
resultType: "RESULT",
executeCode: "SUCCESS",
msgInfo: "",
},
};
};

最后我们的工程结构应该是这个样子的。

20200508143105

完整代码我放到了 CODING 中了,可以自行下载:链接,密码:h9lh

部署与测试

完成编码,把代码推送至线上运行环境,即可完成与 AliGenie 平台的对接。

将代码部署到线上

阿里云开发平台为我们提供了 3 套运行环境,分别为日常环境、预发环境和线上环境,方便开发者进行蓝绿发布。我们直接将代码发到线上环境就好了。

20200508151238

点击部署,CloudIDE 将自动打包上传。

20200508151538

如图,即已经完成部署。

在线调试

部署完成,我们先进行一下在线调试。
回到 AliGenie 平台,进入技能,点击测试,选择在线测试。将示例对话作为测试用例进行测试,每次测试的返回都包括识别意图、参数、返回状态和返回内容四部分。

20200508152049

真机调试

在线调试已经没有问题了,我们马上进入真机测试。选择真机测试 TAB,开启真机测试。

20200509181014

在 AliGenie 开发文档中提到“真机调试需要绑定设备”,目前真机调试已经不需要进行设备绑定了, 只要是这个开发者淘宝账号配网的天猫精灵设备都可以测试这个技能。

对音箱说示例对话,观察音箱的回复是否为预期回复,如果是预期回复,恭喜你,你的天猫精灵技能修炼成功啦。同时欢迎你继续扩展天猫精灵的技能~~

20200508152729

演示

trouble shooting

我们应该具有一定的错能力,下面是 trouble shooting 部分。

如何抓 log

我们在进行在线测试时,有时候会遇到接口错误的问题,不返回任何信息,直接报 IDE Error,我们可以借助阿里云函数计算的日志服务进行排错。
打开阿里云控制台,选择函数计算产品,进入函数计算控制台,在服务/函数选项卡找到对应的函数,进入函数。

20200508160111

函数服务有地域属性,如果你找不到自动创建的云函数,请查看是否选择了正确的地域。

点击日志查询后选择高级查询,勾选要查询的时间段,例如一分钟内,或者一个自定义的时间段,点击查询分析按钮。

20200508160813

我刷蓝色字体的部分就是保存,可以参考进行排错。

为什么我的精灵不听话

使用同样的测试用例进行在线测试和真机测试,结果却截然不同。真机测试时,有时候精灵并不能有效响应我们的意图,这是为什么呢?
我们可以打开天猫精灵 APP,选择消息,打开设备对话查看用户输入的识别结果是否正确。
例如我有一个应用叫 和风天气 ,我对天猫精灵说 天猫精灵,打开和风天气 ,而精灵却识别成了 盒风天气 ,当然返回的结果就出错。

20200508154149

参考文献