作者介绍
本文由彩豚智能科技(北京)有限公司的自然语言技能开发专家编写,如有问题可以通过文末联系方式联系到我们。
彩豚智能是一家专业的自然语言技能应用研发公司,公司开发的语音应用上线了小爱同学、小度平台、天猫精灵开放平台、京东开放平台等10个主流平台,支持智能有屏/无屏音箱,智能手机,智能车载,智能家居以及生活IoT设备,涉及品牌营销、幼儿教育、游戏、电子商务等领域。欢迎有语音智能化需求的客户与我们合作。如有需求,你可以通过文末的联系方式联系到我们。
最终简单成品 Demo 演示
视频地址:https://v.youku.com/v_show/id_XNTg2MDgyOTExNg==.html
什么是技能?
天猫精灵技能应用平台提供了从语音交互模型定义、语义解析理解能力,到技能开发、测试、部署的一整套开发工具和便捷的可视化操作工作台,帮助开发者高效地将各类技能应用快速接入到天猫精灵音箱以及精灵生态硬件终端。
目前平台支持以下几种应用类型的接入:
• 语音技能
• 灵活定义语音交互方式,开发业务逻辑代码、创建屏显页面来响应任意的用户语音请求。
• 小程序
• 支持将支付宝小程序快速迁移到天猫精灵设备,同时也可利用内容模板小程序快速完成内容变现。
• 安卓Android APP应用
• 支持快速接入安卓生态应用,与天猫精灵带屏端设备无缝打通,同时支持语音交互能力。
• H5网页小游戏
• 无需开发,支持现成H5网页小游戏的快速接入,同时支持语音唤起。
搭建本地开发环境
PHP/Laravel 环境搭建
参考下面链接文档,根据自己的对应系统搭建 Laravel 的开发环境。
⚠️注意:天猫精灵模拟调试和真机调试,需要通过公网访问你的开发环境,如果无公网IP可以使用内网穿透的方式来进行。推荐几个内网穿透服务如下:
- https://github.com/open-dingtalk/dingtalk-pierced-client(推荐,本文使用)
- https://ngrok.com/
- https://natapp.cn/
- https://hsk.oray.com/
- https://expose.dev/
- https://github.com/fatedier/frp 自建
- https://github.com/ehang-io/nps 自建
创建Laravel 程序项目
我们使用 laravel 命令来创建项目 (laravel 命令可以用 composer global require laravel/installer 命令安装),参考上面👆环境搭建部分。
laravel new skills . . . # 创建成功之后可以启动服务器php artisan serve #Starting Laravel development server: http://127.0.0.1:8000#[Wed Apr 13 17:47:16 2022] PHP 8.1.4 Development Server (http://127.0.0.1:8000) started
可以根据上面的命令输出看到,Laravel 框架服务启动之后监听了127.0.0.1 这个本地IP上的8000端口。由于我们的服务器是在本地,天猫精灵服务器无法直连,所以需要准备一下内网穿透。通过内网穿透服务,实现公网80端口的数据请求到本地8000端口,从而为后续要开发的技能提供服务。
Web服务内网穿透
访问此网址 https://github.com/open-dingtalk/dingtalk-pierced-client 参考文档操作
具体指令参考如下:
git clone https://github.com/open-dingtalk/dingtalk-pierced-client.git cd dingtalk-pierced-client/mac/ ./ding -proto=http -config=./ding.cfg -subdomain=`openssl rand -hex 6`8000
执行上面的命令之后,我们将会看到如下输出:
ngrok (Ctrl+C to quit) Tunnel Status online Version 1.7/1.7 Forwarding http://936521bc.vaiwan.com -> 127.0.0.1:8000 Web Interface 127.0.0.1:4040 # Conn 0 Avg Conn Time 0.00ms
看到上面的输入说明本地服务器接口可以通过公网访问了,访问地址是: http://936521bc.vaiwan.com, 记住此域名,后面需要填写到天猫精灵技能开放平台的技能配置中。
创建天猫精灵技能
技能创建
首先需要完成账号的注册和认证,通过淘宝/支付宝账号注册、认证还是很快的。访问地址:https://iap.aligenie.com/home 。注册登录之后,可以根据以下步骤创建技能。
第一步:创建新技能
点击按钮【创建新技能】(下图右下角),进入技能创建页面。
第二步:输入技能信息
选择自定义技能,输入技能名称等信息,如下:
你可以选择一个自己想要的技能名称。注意选择自建Web Service,这样就可以在本地开发了,技能发布上线需要有自己的服务器和可以公网访问的域名,开发阶段可以先不考虑服务器和域名。
第三步:配置技能交互模型实体
顶部Tab菜单选择语音交互模型,然后左侧菜单选择实体,再点击右侧的引用公共实体按钮。为了方便开发我们引入两个公共实体:任意长度数字与泛匹配实体,后面配置技能交互意图会用到。
第四步:创建意图并增加话术和参数
顶部Tab菜单选择语音交互模型,然后左侧菜单选择意图,再点击中间的创建意图按钮。
输入意图名称:默认意图,输入意图标识:Default,并打开设为默认意图的开关。
接下来,增加一些用户表达话术内容,意图页面往下翻可以看到单轮对话表达模块,在这里输入话术,支持例句和模版两种形式。模版支持参数,参数通过@{param_name}的形式引用。
如下图:
上图中的表达话术我们有用到参数:@{order} 和 @{any},接下来我们将创建这两个参数,当前意图编辑页面往下翻,可以看到参数模块如下:
分别创建两个参数,添加第一个参数,输入参数名称: any,选择关联实体: sys.other;然后,添加第二个参数,输入参数名称: order,选择关联实体: sys.anyNumber。
⚠️注意:不要勾选 必选,也不需要填写前置意图,系统默认实体值,不要编辑追问和回复。
第五步:提交并训练语音模型
填写完技能信息、对话话术以及话术参数之后,点击右下角的提交保存意图信息。保存之后可以再编辑意图。编辑意图的页面点击【语音交互模型训练】按钮,可以上传进行训练。训练之后可以提高语义理解准确度。
第六步:申请技能的能力权限
点击顶部TAB菜单的后端服务,选择左侧菜单的能力权限管理,点击页面中的添加能力按钮,根据需要我们选择2个能力,分别是:事件触发上报,设备有无屏特性。
第七步:完善服务部署信息
点击顶部TAB菜单的后端服务,选择左侧菜单的服务部署,点击选择页面中的"自建Web Service" 与 "所有意图绑定同一个服务"。然后输入服务器部署地址:http://936521bc.vaiwan.com/api/teach/tmall/animate-master,此地址的中的 http://936521bc.vaiwan.com 就是我们通过文章前面的Web服务内网穿透申请到的域名地址。服务器部署地址URL的 PATH(/api/teach/tmall/animate-master)是技能接口地址,可以在程序开发部分完成,此时还不可以访问。
由于还未验证域名所有权,暂时无法保存,所以暂时不要关闭页面,接下来验证服务器权限。
第八步:验证服务器权限
- 点击【下载认证文件】按钮,下载文件。下载的文件是 .txt 后缀结尾的纯文本文件。我电脑上默认下载到了 Downloads 目录。
root:Downloads root$ ls-ahl 14c27feb915d73d39a09f471c9ae6e7e.txt -rw-r--r--@ 1 root staff 66B Apr 1320:32 14c27feb915d73d39a09f471c9ae6e7e.txt root:Downloads root$ cat 14c27feb915d73d39a09f471c9ae6e7e.txt Jfc4Z4Ur15JwUBuvUQD5wg7Nu8+l+HscqYlfofbyJdbmbWWP7x22Kqc+uWisuLHG
- 保存文件到可访问目录
在此前创建的 Laravel 项目程序根目录执行以下命令,把刚下载的.txt文件拷贝至项目的公开目录。
root:chatbot root$ mkdir public/aligenie root:chatbot root$ mv ~/Downloads/14c27feb915d73d39a09f471c9ae6e7e.txt public/aligenie/ root:chatbot root$ ls-ahl public/aligenie/ total 8drwxr-xr-x 3 root staff 96B Apr 1320:34 . drwxr-xr-x 7 root staff 224B Apr 1320:34 .. -rw-r--r--@ 1 root staff 66B Apr 1320:32 14c27feb915d73d39a09f471c9ae6e7e.txt
- 测试访问验证
浏览器打开此地址测试是否可以访问:http://936521bc.vaiwan.com/aligenie/14c27feb915d73d39a09f471c9ae6e7e.txt。注意此地址的域名需要换成你自己的内网穿透域名。
- 保存服务部署页面
如果第三步测试通过,能在浏览器中看见.txt文件的内容,则可以在服务部署页面,点击保存按钮。
到此技能的基础信息就创建完成了。🎉🎉🎉
技能测试
技能测试开发调试很重要的一个步骤,需要注意的是天猫精灵技能开放平台的在线测试与真机测试会有一些区别,在线测试只能做一些简单的验证。在技能请求上会有字段上的区别。
在线测试
进入对应技能的测试页面,选择在线测试,输入“打开动画小天才”,然后点击发送即可发送请求到刚刚设置的接口地址。由于我们还没有开发好技能,所以会提示技能错误。
发送的请求参考如下:
{ "sessionId": "f635d950-d016-4201-9a28-efd5c5f861e9", "utterance": "打开动画小天才", "requestData": [], "botId": 1213681, "domainId": 673423, "skillId": 887424, "skillName": "动画小天才", "intentId": 135359, "intentName": "Default", "slotEntities": [], "requestId": "20220413181126890-1274025511", "device": [], "skillSession": { "skillSessionId": "2093462e-d370-4bb5-bf7a-d0a10b98d157", "newSession": true }, "context": { "system": { "apiAccessToken": null } } }
真机测试
第一步:真机开启技能测试模式
第二步:手动添加测试设备
第三步:打开技能
对天猫精灵说:天猫精灵,打开动画小天才
天猫精灵会向配置的接口地址发送请求,请求内容参考如下:
{ "sessionId": "20073a3c-541b-4332-914c-7cd26f63b451", "utterance": "打开动画小天才", "requestData": { "screenStatus": "online", "userOpenId": "RrZphL2H0DxabHsGSofaiV+E4Kwbd4J3vpr8CREY2F+2BJ8M=", "deviceOpenId": "yd0KVpz4zaxb9TodqArZmxF9aoLZllmY0MRlT4ZQc8/P5jO+InHuTXodLA==" }, "botId": 592310, "domainId": 627343, "skillId": 883744, "skillName": "动画小天才", "intentId": 1352259, "intentName": "Default", "slotEntities": [], "requestId": "20220413184019578-1620153183", "device": { "deviceOpenId": "yd0KVpz4zaxb9TodqArZmxF9aoLZllmY0MRlT4ZQc8/P5jO+InHuTXodLA==" }, "skillSession": { "skillSessionId": "6467726c-4c8b-46ca-9d9b-7341a6371195", "newSession": true }, "coinContext": { "nluDimConf": "{\"ProductForField\":\"general\",\"ProductScreen\":\"screen\",\"ProductUsageScene\":\"home\",\"autoAgeGender\":false,\"ProductForAge\":\"adult\"}" }, "context": { "system": { "apiAccessToken": "d4b659d1accb4a02b7bda16fa76536sf" } } }
到此,我们创建好了技能,也尝试了一下技能测试,但是由于我们并完成开发,所以技能测试无法通过。接下来我们将一步一步来完善这个技能。
技能开发-Laravel 框架
你好世界
创建控制器
先创建一个控制器来接受技能请求
php artisan make:controller AnimeMasterController
然后打开 AnimeMasterController 写入下面的内容:
namespaceApp\Http\Controllers; useIlluminate\Http\Request; classAnimeMasterControllerextendsController{ publicfunctionskill(Request$request) { return$this->response('你好,世界!'); } publicfunctionresponse(string$text, bool$openMic=false, bool$end=false): array { return [ 'returnCode'=>0, 'returnErrorSolution'=>'', 'returnMessage'=>'success', 'returnValue'=> [ 'resultType'=>$end?'RESULT' : 'ASK_INF', 'askedInfos'=> [], 'gwCommands'=> [ [ 'commandDomain'=>'AliGenie.Speaker', 'commandName'=>'Speak', 'payload'=> [ 'type'=>'text', 'text'=>$text, 'expectSpeech'=>$openMic, 'wakeupType'=>$openMic?'continuity' : '' ] ], [ 'commandDomain'=>'AliGenie.System.Control', 'commandName'=>'Suspend' ] ] ], 'sessionEntries'=> [] ]; } }
配置路由地址
打开 api.php 文件,增加下面两行内容:
use App\Http\Controllers\AnimeMasterController;
Route::post('teach/tmall/animate-master', [AnimeMasterController::class, 'skill']);
增加之后的 api.php 如下:
useIlluminate\Http\Request; useIlluminate\Support\Facades\Route; useApp\Http\Controllers\AnimeMasterController; /*|--------------------------------------------------------------------------| API Routes|--------------------------------------------------------------------------|| Here is where you can register API routes for your application. These| routes are loaded by the RouteServiceProvider within a group which| is assigned the "api" middleware group. Enjoy building your API!|*/Route::middleware('auth:sanctum')->get('/user', function (Request$request) { return$request->user(); }); Route::post('teach/tmall/animate-master', [AnimeMasterController::class, 'skill']);
在线测试看看
在技能的测试页面,选择在线测试,然后输入:打开动画小天才,即可收到回复:你好,世界!
到此我们最简单的一个技能就开发完成了,如果你有天猫精灵音箱设备的话,可以真机测试看看效果。接下来我们再对技能程序做一些改动。
技能欢迎语
修改技能返回
我们修改一下 AnimeMasterController 中 skill 方法的返回,让他输出我们一个正常技能的返回内容,而不是简单的:你好,世界!
欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:第一个,试一试。
上面这句话就是我们要改成的返回,改完之后 AnimeMasterController 的 skill 方法如下。
public function skill(Request $request) { return $this->response('欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:第一个,试一试。'); }
再次测试看看
在技能的测试页面,选择在线测试,然后输入:欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:第一个,试一试。
显示在天猫精灵屏幕上
权限申请
点击技能开发页面顶部Tab 菜单的屏显页面申请权限,如果已经具备了此权限可以忽略此步骤。
前端开发环境
使用天猫精灵TPL 2.0的显示技术,我们可以在页面上开发丰富的视觉效果。相关文档链接:https://www.yuque.com/waft/docs。
执行以下命令安装开发环境:(依赖Nodejs 开发环境)
npm i waft-cli -gwaft init
根据提示我们输入前端项目名称以及模版选择(1)
? What's project name? waft
? Which template package to from? waft-hello-world
创建好之后前端项目之后
cd waft subl .
进入 waft 前端项目目录。然后用自己熟悉的编辑器打开文件夹查看,我用 Sublime Text 打开文件夹之后显示如下:
修改显示内容
我们修改一下 src/pages/index/index.axml 文件中的 "HELLO WORLD",换成: 你好,世界。
index.axml 内容如下:
<divclass="wrapper"><imageclass="img"src="http://gw.alicdn.com/imgextra/i4/O1CN01KoLIjy1xrDXBCZMUK_!!6000000006496-2-tps-424-424.png"/><divclass="bottom"><x-buttononTap="onMinus"text="-"/><divclass="text"><textclass="hello-font">你好,世界 ... {{percent}}%</text><x-textvalue="{{desc}}"/></div><x-buttononTap="onPlus"text="+"/></div></div>
然后编译一下前端文件,并输出成为.aot文件:
npx waft build --aot=true
如果你电脑有安装tree命令,则可以通过tree命令查看编译之后的结果。
tree build
build
├── _waft_index.ts
├── app.aot
├── app.wasm
├── app.wasm.map
├── app.wat
├── cards
│ └── cards-index-index
│ ├── _waft_index.ts
│ ├── cards-index-index.wasm
│ ├── cards-index-index.wasm.map
│ └── cards-index-index.wat
└── index.html
上传发布页面
在技能的编辑页面点击屏显页面,点击【点击上传代码包】按钮,上传上面编译的app.aot文件,操作截图如下:
以后修改了前端代码和显示效果,可以点击 x 图标删除已经上传的文件,替换为新的编译结果。
修改技能响应显示页面
修改 AnimeMasterController 中的 response 方法如下:
//.//.//.publicfunctionresponse(string$text, array$data= [], bool$openMic=false, bool$end=false): array { if (!$data) { $data= ['key'=>'value']; } $view= [ 'commandDomain'=>'AliGenie.Screen', 'commandName'=>'Render', 'payload'=> [ 'pageType'=>'TPL.RenderDocument', 'data'=> [ 'pageTitle'=>'动画小天才', 'dataSource'=>$data, 'config'=> [ 'header'=> [ 'enabled'=>false, 'height'=>'100rpx', 'foregroundColor'=>'#FF7383A2', 'backgroundColor'=>'#FFFFFFFF', ], 'body'=> [ 'backgroundColor'=>'linear-gradient(180deg, #5BD7CD 0%, #3DBAD3 100%)', ], ], ], ], ]; $speak= [ 'commandDomain'=>'AliGenie.Speaker', 'commandName'=>'Speak', 'payload'=> [ 'type'=>'text', 'text'=>$text, 'expectSpeech'=>$openMic, ] ]; if ($openMic) { $speak['payload']['wakeupType'] ='continuity'; } return [ 'returnCode'=>0, 'returnErrorSolution'=>'', 'returnMessage'=>'success', 'returnValue'=> [ 'resultType'=>$end?'RESULT' : 'ASK_INF', 'askedInfos'=> [], 'gwCommands'=> [ $view, $speak, [ 'commandDomain'=>'AliGenie.System.Control', 'commandName'=>'Suspend' ] ] ], 'sessionEntries'=> [] ]; } //.//.//.
真机测试技能显示页面
在添加了真机测试设备之后,就可以对天猫精灵说:打开动画小天才,即可查看到如下页面:
显示动画
天猫精灵的 TPL 2.0 支持显示 lottie 动画,我们可以在 https://lottiefiles.com/ 找到动画素材。
前端页面改造
修改显示页面index.axml结构,增加 Lottie 元素,代码如下:
<divclass="wrapper"><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path}}"repeatCount="{{repeatCount}}"></lottie><divclass="bottom"><x-buttononTap="onMinus"text="-"/><divclass="text"><textclass="hello-font">你好,世界 ... {{percent}}%</text><x-textvalue="{{desc}}"/></div><x-buttononTap="onPlus"text="+"/></div></div>
调整一下样式文件: index.acss,修改成如下内容:
.wrapper { background-color:rgb(50, 51, 56); width:100%; height:100%; display:flex; flex-direction: column; justify-content: center; align-items: center; } .hello-font { color:#FFF; font-size: 50px; } .img{ width: 400rpx; height: 400rpx; } .bottom{ display:flex; flex-direction: row; justify-content: space-around; align-items: center; width: 100%; } .text{ display:flex; flex-direction: column; justify-content: center; align-items: center; } .lottie{ width: 80vw; height: 150rpx; margin-left: 10vw; border: 1rpxsolidred; }
lottie 元素的四个参数:lottie,autoPlay, path, repeatCount 四个参数我们通过后端技能请求响应传给前端。需要注意:lottie 默认不会自动播放。
页面改造好之后,重新执行命令编译一遍:
npx waft build --aot=true
然后将编译之后的build 目录下的 app.aot 上传至技能屏显页面:
技能响应携带动画素材
页面准备好之后,我们再改造技能,输出相关的动画显示参数到前端。我们修改 AnimeMasterController 的 skill 方法,改成下面的内容:
publicfunctionskill(Request$request) { return$this->response( '欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:第一个,试一试。', [ 'lottie'=>true, 'path'=>'https://ailabs-iot.aligenie.com/waft-assets/call_anim_1.json', 'repeatCount'=>10, 'autoPlay'=>true, ] ); }
修改好之后,我们就可以在天猫精灵上看到效果了。
查看动画效果
我们对天猫精灵说:打开动画小天才,来看看效果。正常情况下,会出现如下动画效果:
虽然能看到动画了,但是页面显示的还不好看,接下来我们把技能做的好看好玩一点。
美化页面
现在的页面还比较难看,接下来一步一步做的更好看,更加好玩。
第一步:简化页面
去掉无用的元素,在页面上只显示动画内容,同时把页面背景修改成白色。
修改后的index.acss 如下:
.wrapper { background-color:rgb(255, 255, 255); width:100%; height:100%; display:flex; flex-direction: column; justify-content: center; align-items: center; } .lottie{ width: 80vw; height: 150rpx; }
修改后的index.axml 如下:
<divclass="wrapper"><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path}}"repeatCount="{{repeatCount}}"></lottie></div>
然后编译上传,看看效果
npx waft build --aot=true
效果:
第二步:显示更多好玩的动画
我们把屏幕分成左右两个部分,分别放两个动画。
修改页面index.axml结构如下:
<divclass="wrapper"><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path}}"repeatCount="{{repeatCount}}"></lottie><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path1}}"repeatCount="{{repeatCount}}"></lottie></div>
修改页面的样式index.acss如下:
.wrapper { background-color:rgb(255, 255, 255); width:100%; height:100%; display:flex; flex-direction: row; justify-content: center; align-items: center; } .lottie{ width: 48vw; height: 100vh; }
再去 https://lottiefiles.com/ 网站下载一些好玩的动画,保存到项目的public/lotties目录下,这样技能就可以访问到了。
然后修改技能的响应代码,修改AnimeMasterController.php的skill方法如下:
publicfunctionskill(Request$request) { $group=collect([ 'http://936521bc.vaiwan.com/lotties/13950-perchick-at-tgsticker-sticker-30.json', 'http://936521bc.vaiwan.com/lotties/46472-lurking-cat.json', 'http://936521bc.vaiwan.com/lotties/82726-cute-cat.json', 'http://936521bc.vaiwan.com/lotties/77317-cute-cat.json', ]); $group1=collect([ 'http://936521bc.vaiwan.com/lotties/36413-cat-loading.json', 'http://936521bc.vaiwan.com/lotties/78069-dog-tail-wag.json', 'http://936521bc.vaiwan.com/lotties/101336-toucan.json', 'http://936521bc.vaiwan.com/lotties/lf20_scrpsgm1.json', 'http://936521bc.vaiwan.com/lotties/52886-cow-eating-grass.json', ]); return$this->response( '欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:第一个,试一试。', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); }
然后打开技能就可以查看到屏幕上左右出现两个不同的动画了。截图如下:
现在能同时显示两个好玩的动画了,下一步我们加一点语音控制指令,语音切换动画。
第三步:语音控制切换动画
修改前端index.ts程序,方便接受语音指令下发的数据消息,在index.ts文件中增加以下代码:
onMessage(e: MessageEvent): void{ if (e.data.has("dataSource")) { constdata=e.data.getObject("dataSource"); this.setState(data); } }
加了这个代码之后,页面的动画就能动态切换了。下一步我们需要在天猫精灵技能开发后台增加一个意图,支持“换一个”指令话术。具体操作截图如下:
- 语音交互模型页面的意图菜单页面,点击创建意图:
- 设置意图的基础信息
- 增加意图话术
- 保存并训练
保存以上配置并训练模型,然后在天猫精灵上打开“动画小天才”之后说:下一个,换一个,技能就能收到对应的请求。
- 修改技能响应程序如下:
修改技能的skill方法,然后分别处理 Default 与 Next 意图。
publicfunctionskill(Request$request) { $group=collect([ 'http://936521bc.vaiwan.com/lotties/13950-perchick-at-tgsticker-sticker-30.json', 'http://936521bc.vaiwan.com/lotties/46472-lurking-cat.json', 'http://936521bc.vaiwan.com/lotties/82726-cute-cat.json', 'http://936521bc.vaiwan.com/lotties/77317-cute-cat.json', ]); $group1=collect([ 'http://936521bc.vaiwan.com/lotties/36413-cat-loading.json', 'http://936521bc.vaiwan.com/lotties/78069-dog-tail-wag.json', 'http://936521bc.vaiwan.com/lotties/101336-toucan.json', 'http://936521bc.vaiwan.com/lotties/lf20_scrpsgm1.json', 'http://936521bc.vaiwan.com/lotties/52886-cow-eating-grass.json', ]); if ($request->input('intentName') =='Next') { return$this->response( '好的,马上更换!', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } elseif ($request->input('intentName') =='Default') { return$this->response( '欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:换一个。', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } }
- 真机测试查看效果
打开技能:天猫精灵,打开动画小天才;然后就可以看见屏幕上出现两个动画并TTS播报这句话:
欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:换一个。
然后,我们对天猫精灵说:天猫精灵,换一个。屏幕上就会更好动画并播报下面这句话:
好的,马上更换!
到此整个技能的主要玩法就开发完成了,但是技能开发还没有结束。还需要支持技能的友好退出。
第四步:技能退出
为了支持用户语音退出技能,我们需要增加退出意图。操作步骤如下:
- 填写基础信息
- 增加语料
- 保存并训练模型
- 修改技能后端接口逻辑
修改AnimeMasterController.php文件中的skill方法如下:
publicfunctionskill(Request$request) { $group=collect([ 'http://936521bc.vaiwan.com/lotties/13950-perchick-at-tgsticker-sticker-30.json', 'http://936521bc.vaiwan.com/lotties/46472-lurking-cat.json', 'http://936521bc.vaiwan.com/lotties/82726-cute-cat.json', 'http://936521bc.vaiwan.com/lotties/77317-cute-cat.json', ]); $group1=collect([ 'http://936521bc.vaiwan.com/lotties/36413-cat-loading.json', 'http://936521bc.vaiwan.com/lotties/78069-dog-tail-wag.json', 'http://936521bc.vaiwan.com/lotties/101336-toucan.json', 'http://936521bc.vaiwan.com/lotties/lf20_scrpsgm1.json', 'http://936521bc.vaiwan.com/lotties/52886-cow-eating-grass.json', ]); if ($request->input('intentName') =='Next') { return$this->response( '好的,马上更换!', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } elseif ($request->input('intentName') =='Quit') { return$this->response( '好的,再见!下次要玩,记得对我说打开动画小天才', [], false, true ); } elseif ($request->input('intentName') =='Default') { return$this->response( '欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:换一个。', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } }
- 查看真机测试效果
对天猫精灵说:天猫精灵,退出。就会退出技能,回到天猫精灵桌面了。
第五步:发布技能
最后技能开发完成之后,你就可以在天猫精灵技能开发平台上发布你的技能。本文主要是演示如何开发技能,文中演示的技能还是比较简单的,所以就不真的发布上线。
关键程序源码文件共享
技能后端程序PHP
AnimeMasterController.php
namespaceApp\Http\Controllers; useIlluminate\Http\Request; classAnimeMasterControllerextendsController{ publicfunctionskill(Request$request) { $group=collect([ 'http://aligenie.vaiwan.com/lotties/13950-perchick-at-tgsticker-sticker-30.json', 'http://aligenie.vaiwan.com/lotties/46472-lurking-cat.json', 'http://aligenie.vaiwan.com/lotties/82726-cute-cat.json', 'http://aligenie.vaiwan.com/lotties/77317-cute-cat.json', ]); $group1=collect([ 'http://aligenie.vaiwan.com/lotties/36413-cat-loading.json', 'http://aligenie.vaiwan.com/lotties/78069-dog-tail-wag.json', 'http://aligenie.vaiwan.com/lotties/101336-toucan.json', 'http://aligenie.vaiwan.com/lotties/lf20_scrpsgm1.json', 'http://aligenie.vaiwan.com/lotties/52886-cow-eating-grass.json', ]); if ($request->input('intentName') =='Next') { return$this->response( '好的,马上更换!', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } elseif ($request->input('intentName') =='Quit') { return$this->response( '好的,再见!下次要玩,记得对我说打开动画小天才', [], false, true ); } elseif ($request->input('intentName') =='Default') { return$this->response( '欢迎来到动画小天才,你可以用动画片段来组装一个大型动画,现在对我说:换一个。', [ 'lottie'=>true, 'path'=>$group->random(), 'path1'=>$group1->random(), 'repeatCount'=>-1, 'autoPlay'=>true, ] ); } } publicfunctionresponse(string$text, array$data= [], bool$openMic=false, bool$end=false): array { if (!$data) { $data= ['key'=>'value']; } $view= [ 'commandDomain'=>'AliGenie.Screen', 'commandName'=>'Render', 'payload'=> [ 'pageType'=>'TPL.RenderDocument', 'data'=> [ 'pageTitle'=>'动画小天才', 'dataSource'=>$data, 'config'=> [ 'header'=> [ 'enabled'=>false, 'height'=>'100rpx', 'foregroundColor'=>'#FF7383A2', 'backgroundColor'=>'#FFFFFFFF', ], 'body'=> [ 'backgroundColor'=>'linear-gradient(180deg, #5BD7CD 0%, #3DBAD3 100%)', ], ], ], ], ]; $speak= [ 'commandDomain'=>'AliGenie.Speaker', 'commandName'=>'Speak', 'payload'=> [ 'type'=>'text', 'text'=>$text, 'expectSpeech'=>$openMic, ] ]; if ($openMic) { $speak['payload']['wakeupType'] ='continuity'; } $control= [ 'commandDomain'=>'AliGenie.System.Control', 'commandName'=>'Suspend' ]; if ($end) { $view=$control= [ 'commandDomain'=>'AliGenie.System.Control', 'commandName'=>'Exit', ]; } return [ 'returnCode'=>0, 'returnErrorSolution'=>'', 'returnMessage'=>'success', 'returnValue'=> [ 'resultType'=>$end?'RESULT' : 'ASK_INF', 'askedInfos'=> [], 'gwCommands'=> [ $view, $speak, $control ] ], 'sessionEntries'=> [] ]; } }
技能前端页面结构axml
index.axml
<divclass="wrapper"><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path}}"repeatCount="{{repeatCount}}"></lottie><lottiea:if="{{lottie}}"class="lottie"id="myLottie"autoPlay="{{autoPlay}}"path="{{path1}}"repeatCount="{{repeatCount}}"></lottie></div>
技能前端样式文件acss
index.acss
.wrapper { background-color:rgb(255, 255, 255); width:100%; height:100%; display:flex; flex-direction: row; justify-content: center; align-items: center; } .lottie{ width: 48vw; height: 100vh; }
技能前端控制逻辑TypeScript
index.ts
import { JSON, JSONObject } from"waft-json"; import { console, getDataSource, Page, Props, Event, MessageEvent, setTimeout,window } from"waft"; exportclassIndexextendsPage { constructor(props: Props){ super(props); // 获取queryconstquery=this.query; // 获取dataSourceconstdataSource=this.dataSource; // 设置到state的示例this.setState(dataSource); } onMessage(e: MessageEvent): void{ if (e.data.has("dataSource")) { constdata=e.data.getObject("dataSource"); this.setState(data); } } onPlus(e: Event): void{ constpercent=this.state.getInteger("percent"); this.setState(JSON.parseObject(`{"percent": ${(percent+1).toString()}}`)); } onMinus(e: Event): void{ constpercent=this.state.getInteger("percent"); this.setState(JSON.parseObject(`{"percent": ${(percent-1).toString()}}`)); } onShow(): void{ // 页面显示console.log('page onShow'); } onLoad(query: JSONObject): void{ // 页面加载后console.log('page onLoad:'+JSON.stringify(query)); } // 语音事件onVoice(data: JSONObject): void{} // 界面更新事件onUpdate(data: JSONObject): void{ console.log("onUpdate:"+data.toString()); } // 音频事件onAudio(data: JSONObject): void{} }
分享结束
附完整项目源码:https://github.com/caitunai/aligenie-skill-demo
如果你阅读到此了,相信你是真的想开发天猫精灵语音技能。我也希望能和读者你有更多交流和分享,你可以通过钉钉或者邮箱(help@caitun.com)联系到我。