SDK常用功能介绍
SDK提供了开发技能所需的各种API接口。SDK常用的功能有技能打开和退出、NLU交互功能、对话控制功能、展示卡片功能、事件监听功能、插件功能。
打开和关闭技能
打开技能
当用户使用唤醒名唤醒(打开)技能时,DuerOS向技能发送LanuchRequest请求,技能处理请求后,返回问候语。代码示例如下。
$this->addLaunchHandler(function(){
return [
'outputSpeech' => '你好,欢迎使用小白技能 '
];
});
this.addLaunchHandler(function(){
return {
outputSpeech: '你好,欢迎使用小白技能 '
};
});
@Override
protected Response onLaunch(LaunchRequest launchRequest) {
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "你好,欢迎使用小白技能");
Response response = new Response(outputSpeech, textCard);
return response;
}
关闭技能
当用户退出技能时,DuerOS向技能发送SessionEndedRequest请求,技处理请求后,返回结束语。代码示例如下。
$this->addSessionEndedHandler(function(){
clear status
return [
'outputSpeech' => '谢谢使用小白技能,下次再见'
];
});
this.addSessionEndedHandler(function(){
clear status
return {
outputSpeech: '谢谢使用小白技能,下次再见'
};
});
@Override
protected Response onSessionEnded(SessionEndedRequest sessionEndedRequest) {
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "谢谢使用小白技能,下次再见");
Response response = new Response(outputSpeech);
return response;
}
NLU交互功能
NLU交互功能是DuerOS理解和处理用户请求的重要部分。DuerOS通过NLU解析,理解用户说话的意图及槽位信息,并将这些信息传递给技能处理。重点介绍NLU中常用的概念和常用的指令。
常用概念
- 意图
指用户说话的目的,用户通过这句话想要表达什么,想做什么。如用户提问“我想订一张火车票”,用户的意图是订火车票。 在一次对话中,用户可能包含多个意图,这些意图按照概率大小进行排序。 - 槽位
在用户表述意图过程中,用来准确的描述用户意图的关键词。如用户说“我要预定一张10月1日从北京到青岛的火车票”,用户的意图是订火车票,其中“一张”、“10月1日”、“北京”、“青岛”都是用户表达准确表达订火车票这个意图的关键信息,称之为槽位。 - 词典
系统槽位指SDK中预先定义的槽位信息。如“我要预定10月1日从北京到青岛的火车票”这个例子中,“北京”、“青岛”两个槽位类型是地点-城市,“10月1日”的槽位类型是时间。SDK中将这些通用常识领域的信息,如地点-国家、地点-城市、时间等预先定义成槽位,方便开发者直接使用。当系统槽位不能满足您技能信息时,开发者可以自定义槽位类型,这类槽位称为普通槽位。
常用指令
ask指令
当用户的请求信息不完整时,技能无法处理用户的请求,此时技能向DuerOS发送ask指令,询问用户缺失的关键信息。例如如用户说“我想订火车票”,这是一个订火车票的意图,在该意图中缺少的目的地信息,您的技能向DuerOS发送ask指令,询问目的地信息。代码示例如下。
$this->addIntentHandler('rent_car.book', function(){
$endPoint = $this->getSlot('destination');
if(!$this->endPoint) {
//询问slot: destination
$this->nlu->ask('destination');
$card = new TextCard('你想去哪里');
return [
'card' => $card,
'outputSpeech' => '你想去哪里'
];
}
});
this.addIntentHandler('rent_car.book', () => {
let endPoint = this.getSlot('destination');
if(!endPoint) {
//询问slot: destination
this.nlu.ask('destination');
let card = new TextCard('你想去哪里');
return {
card: 'card',
outputSpeech: '你想去哪里'
};
}
});
if ("rent_car.book".equals(intentRequest.getIntentName())) {
//询问slot: destination
if (getSlot("destination") == null) {
TextCard textCard = new TextCard("你想去哪里");
ask("destination");
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "你想去哪里");
Response response = new Response(outputSpeech, textCard);
return response;
}
}
confirm slot指令
当技能需要对槽位信息进行确认时,会向DuerOS发送confirm slot指令,同时返回询问的outputSpeach。如话费充值技能发现一位用户想要给手机充值大于10000元,这个金额超出了正常的充值范围,此时技能会向用户进行确认。代码示例如下。
$this->addIntentHandler('phone', function(){
if($this->getSlot('money') > 10000) {
$this->setConfirmSlot('money');
return [
'outputSpeech' => "请确认话费金额是$money元吗",
];
}
});
this.addIntentHandler('phone', () => {
if(this.getSlot('money') > 10000) {
this.nlu.setConfirmSlot('money');
return {
outputSpeech: '请确认话费金额是'+money+'元吗'
};
}
});
if ("phone".equals(intentRequest.getIntentName())) {
float money = getSlot("money");
if (money > 10000) {
setConfirmSlot("money");
}
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "请确认话费金额是" + money + "吗");
Response response = new Response(outputSpeech, textCard);
return response;
}
注意事项:一般情况下,在一次指令中完成所有槽位信息的确认。
confirm intent指令
当技能需要对意图信息进行确认时,会向DuerOS发送confirm intent指令,同时返回询问的outputSpeach。如订餐技能在下单前,需要用户确认订餐信息。代码示例如下。
$this->addIntentHandler('delivery', function(){
$money = $this->getSlot('money');
$phone = $this->getSlot('phone');
if($money && $phone) {
$this->setConfirmIntent();
return [
'outputSpeech' => "您此次订单消费:$money,手机号是:$phone"
];
}
});
this.addIntentHandler('delivery', () => {
let order = this.getSlot('order');
let money = this.getSlot('money');
let phone = this.getSlot('phone');
if(money && phone) {
this.nlu.setConfirmIntent();
return {
outputSpeech : '您此次订单消费'+money',手机号是:'+phone
};
}
...
});
if ("delivery".equals(intentRequest.getIntentName())) {
float money = getSlot("money");
float phone = getSlot("phone");
if ( money != null && phone != null) {
setConfirmIntent();
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "您此次订单消费:"+money+",手机号是:"+phone);
Response response = new Response(outputSpeech, textCard);
return response;
}
}
delegate指令
当您的技能将处理权交给DuerOS时,应该向DuerOS发送delegate指令。此时,DuerOS按您的技能配置的顺序,如对缺失槽位的询问,槽位值的确认、整个意图的确认等,对对话进行处理。代码示例如下。
$this->addIntentHandler('phone', function(){
if(!$this->request->isDialogStateCompleted()) {
return $this->setDelegate();
}
});
this.addIntentHandler('phone', () => {
if(!this.request.isDialogStateCompleted()) {
return this.nlu.setDelegate();
}
...
});
if ("phone".equals(intentRequest.getIntentName())) {
setDelegate();
}
注意事项:如果使用delegate指令,请不要使用setConfirmSlot、setConfirmIntent等指令,因为前面指令返回的结果会被后面指令返回的结果覆盖。
对话管理
reprompt信息
当用户在一段时间内不再输入信息时,DuerOS会使用技能之前返回的reprompt信息,提示用户进行输入。代码示例如下。
return [
'reprompt' => '你好,请问你想去哪里'
];
return {
reprompt: '你好,请问你想去哪里'
};
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "你好,请问你想去哪里");
Response response = new Response(outputSpeech);
return response;
展示卡片
在有屏设备端,当您的技能在回复用户请求时,可以使用展现卡片。展现卡片分文本卡片、标准卡片、列表卡片、图片卡片。
文本卡片
在屏幕上面显示文本信息,URL链接信息及提示信息。代码示例如下:
$card = new TextCard('content');
$card->setContent('content');
$card->setAnchor('http://www.baidu.com', 'showtext');
$card->addCueWords(['hint1', 'hint2']);
let card = new TextCard('content');
card.setContent('Content');
card.setAnchor('http://www.baidu.com', 'showtext');
card.addCueWords(['hint1', 'hint2']);
TextCard textCard = new TextCard("content");
textCard.setUrl("www:......");
textCard.setAnchorText("http://www.baidu.com");
textCard.addCueWord(['hint1', 'hint2']);
标准卡片
在屏幕上面显示图片信息,图片文本描述等信息。代码示例如下。
$card = new StandardCard();
$card->setTitle('title');
$card->setContent('content');
$card->setImage('http://www...');
$card->setAnchor('http://www.baidu.com');
const BaseBot = require('bot-sdk');
const StandardCard = BaseBot.Card.StandardCard;
let card = new StandardCard();
card.setTitle('title');
card.setContent('content');
card.setImage('http://www...');
card.setAnchor('http://www.baidu.com');
StandardCard standardCard = new StandardCard("title", "content");
standardCard.setUrl("www:......");
standardCard.setAnchorText("http://www.baidu.com");
standardCard.setImage("http://www...");
列表卡片
列表卡片是多张标准卡片的集合,在屏幕上面展现多张标准卡片。代码示例如下。
$card = new ListCard();
$item = new ListCardItem();
$item->setTitle('title');
$item->setContent('content');
$item->setUrl('http://www');
$item->setImage('http://www.png');
$card->addItem($item);
const BaseBot = require('bot-sdk');
const StandardCard = BaseBot.Card.StandardCard;
const ListCardItem = BaseBot.Card.Item;
let card = new StandardCard();
let item = new ListCardItem();
item.setTitle('title')
item.setContent('content')
item.setUrl('http://www')
item.setImage('http://www.png');
card.addItem(item);
return {
card: card
};
ListCard listCard = new ListCard();
StandardCardInfo item1 = new StandardCardInfo("title1", "content1");
StandardCardInfo item2 = new StandardCardInfo("title2", "content2");
listCard.addStandardCardInfo(item1);
listCard.addStandardCardInfo(item2);
图片卡片
在屏幕上展现一系列相关图片。代码示例如下。
$card = new ImageCard();
$card->addItem('http://src.image', 'http://thumbnail.image');
const BaseBot = require('bot-sdk');
const ImageCard = BaseBot.Card.ImageCard;
let card = new ImageCard();
card.addItem('http://src.image', 'http://thumbnail.image');
ImageCard imageCard = new ImageCard();
imageCard.addImageCardInfo("http://src.image", "http://thumbnail.image");
音频播放
音乐播放指令
当技能接收到用户播放音乐的意图时,技能应该返回执行音乐播放指令,同时返回语音“正在为你播放歌曲”。代码示例如下。
use \Baidu\Duer\Botsdk\Directive\AudioPlayer\Play;
$directive = new Play('http://www.music', Play::REPLACE_ALL);
return [
'directives' => [$directive],
'outputSpeech' => '正在为你播放歌曲'
];
const BaseBot = require('bot-sdk');
const Play = BaseBot.Directive.AudioPlayer.Play
let directive = new Play('http://www.music', Play.REPLACE_ALL);
return {
directives: [directive],
outputSpeech: '正在为你播放歌曲'
};
import com.baidu.dueros.data.response.directive.audioplayer.Play;
Play play = new Play("http://www.music");
play.setPlayBehavior(PlayBehaviorType.REPLACE_ALL);
play.setOffsetInMilliSeconds(1000);
this.addDirective(play);
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "正在为你播放歌曲");
Response response = new Response(outputSpeech);
return response;
停止播放音频
当技能接收到用户停止播放的意图时,技能应该返回执行停止播放指令,同时返回语音“已经停止播放"。代码示例如下。
use \Baidu\Duer\Botsdk\Directive\AudioPlayer\Stop;
$directive = new Stop();
return [
'directives' => [$directive],
'outputSpeech' => '已经停止播放'
];
const BaseBot = require('bot-sdk');
const Stop = BaseBot.Directive.AudioPlayer.Stop;
let directive = new Stop();
return {
directives: [directive],
outputSpeech: '已经停止播放'
};
import com.baidu.dueros.data.response.directive.audioplayer.Stop;
Stop stop = new Stop();
this.addDirective(stop);
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "已经停止播放");
Response response = new Response(outputSpeech);
return response;
监听
插件
插件又叫拦截器,用来干预对话流程、返回结果信息等。当用户唤醒您的技能时,您的技能发现用户没有登录,会提示用户先登录。上述拦截功能可以通过LoginIntercept实现,代码示例如下。
public function __construct($postData = []) {
parent::__construct($postData);
$this->addIntercept(new Baidu\Duer\Botsdk\Plugins\LoginIntercept());
}
SDK中提供了很多拦截器,也支持开发者自己开发拦截器。开发拦截器步骤如下:
- 继承\Baidu\Duer\Botsdk\Intercept。
- 重载preprocess,实例化对象。
- 通过重载postprocess能够对回调函数的返回值进行统一的处理。
class YourIntercept extends \Baidu\Duer\Botsdk\Intercept{
public function preprocess($bot) {
//$Bot: Bot实例化对象
}
public function postprocess($bot, $result) {
//maybe format $result
return $result;
}
}
说明:
- 拦截器可以定义多个,执行顺序由加入拦截器队列的顺序决定。
- Node.js SDK和Java SDK暂时不支持该功能。