SDK常用功能介绍  

SDK提供了开发技能所需的各种API接口。SDK常用的功能有技能打开和退出、NLU交互功能、对话控制功能、展示卡片功能、事件监听功能、插件功能。

打开和关闭技能

打开技能

当用户使用唤醒名唤醒(打开)技能时,DuerOS向技能发送LanuchRequest请求,技能处理请求后,返回问候语。代码示例如下。

PHP Node.js Java
$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请求,技处理请求后,返回结束语。代码示例如下。

PHP Node.js Java
$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指令,询问目的地信息。代码示例如下。

PHP Node.js Java
$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元,这个金额超出了正常的充值范围,此时技能会向用户进行确认。代码示例如下。

PHP Node.js Java
$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。如订餐技能在下单前,需要用户确认订餐信息。代码示例如下。

PHP Node.js Java
$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按您的技能配置的顺序,如对缺失槽位的询问,槽位值的确认、整个意图的确认等,对对话进行处理。代码示例如下。

PHP Node.js Java
$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信息,提示用户进行输入。代码示例如下。

PHP Node.js Java
return [
    'reprompt' => '你好,请问你想去哪里'
];
return {
    reprompt: '你好,请问你想去哪里'
};
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "你好,请问你想去哪里");
Response response = new Response(outputSpeech);
return  response;

展示卡片

在有屏设备端,当您的技能在回复用户请求时,可以使用展现卡片。展现卡片分文本卡片、标准卡片、列表卡片、图片卡片。

文本卡片

在屏幕上面显示文本信息,URL链接信息及提示信息。代码示例如下:

PHP Node.js Java
$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']);

标准卡片

在屏幕上面显示图片信息,图片文本描述等信息。代码示例如下。

PHP Node.js Java
$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...");

列表卡片

列表卡片是多张标准卡片的集合,在屏幕上面展现多张标准卡片。代码示例如下。

PHP Node.js Java
$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);

图片卡片

在屏幕上展现一系列相关图片。代码示例如下。

PHP Node.js Java
$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");

音频播放

音乐播放指令

当技能接收到用户播放音乐的意图时,技能应该返回执行音乐播放指令,同时返回语音“正在为你播放歌曲”。代码示例如下。

PHP Node.js Java
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;

停止播放音频

当技能接收到用户停止播放的意图时,技能应该返回执行停止播放指令,同时返回语音“已经停止播放"。代码示例如下。

PHP Node.js Java
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实现,代码示例如下。

PHP
public function __construct($postData = []) {    
    parent::__construct($postData);
    $this->addIntercept(new Baidu\Duer\Botsdk\Plugins\LoginIntercept());
}

SDK中提供了很多拦截器,也支持开发者自己开发拦截器。开发拦截器步骤如下:

  • 继承\Baidu\Duer\Botsdk\Intercept。
  • 重载preprocess,实例化对象。
  • 通过重载postprocess能够对回调函数的返回值进行统一的处理。
PHP
class YourIntercept extends \Baidu\Duer\Botsdk\Intercept{
  public function preprocess($bot) {
    //$Bot: Bot实例化对象
  }
  public function postprocess($bot, $result) {
    //maybe format $result
    return $result;
  }
}

说明

  1. 拦截器可以定义多个,执行顺序由加入拦截器队列的顺序决定。
  2. Node.js SDK和Java SDK暂时不支持该功能。