DuerOS是對話式AI系統(tǒng)滑臊,既然都已經(jīng)是人工智能了贿条,為什么還要在DuerOS上開發(fā)技能服務(wù)呢?
溫故知新船惨,我們還是要重新審視一下AI堪澎,具體可以參見《老碼農(nóng)眼中的簡明AI》擂错。從編程的角度看,面向人工智能的應(yīng)用大約由三部分組成:領(lǐng)域知識樱蛤、數(shù)學(xué)算法和計算方式钮呀。數(shù)學(xué)算法和計算已經(jīng)由類似DuerOS這樣的AI系統(tǒng)提供了,但是領(lǐng)域知識涉及到具體的行業(yè)知識刹悴,業(yè)務(wù)邏輯行楞,專業(yè)術(shù)語等等,尤其是業(yè)務(wù)邏輯土匀,AI系統(tǒng)也難以做到面面俱到子房。做一個不太恰當(dāng)?shù)念惐龋ヂ?lián)網(wǎng)擁有了web技術(shù)就轧,但是具體的Web應(yīng)用服務(wù)還是需求開發(fā)的证杭,面向?qū)υ捠紸I系統(tǒng)的技能服務(wù)也是類似。
面向人的交互
《面向協(xié)議的DuerOS技能開發(fā)》一文中談到妒御,技能開發(fā)基本上可以理解為Web服務(wù)開發(fā)解愤,只是用戶交互的方式發(fā)生了改變。
電腦/手機(jī)等原來使用鍵盤/鼠標(biāo)/觸摸屏完成輸入乎莉,用顯示屏幕完成輸出送讲,現(xiàn)在基于DuerOS的小度系列產(chǎn)品使用語音對話完成輸入,使用揚(yáng)聲器完成音頻輸出惋啃,當(dāng)然哼鬓,有屏設(shè)備同時支持了原有的功能。
簡單地說边灭,傳統(tǒng)的交互方式是面向機(jī)器的异希,DuerOS的交互方式是面向人的。
DBP的SDK
為了方便開發(fā)者高效地完成技能服務(wù)的開發(fā)绒瘦,DuerOS Bot Platform (DBP)提供了多種語言的SDK:Java称簿,JavaScript,PHP惰帽,Go以及Python憨降。對程序員而言,實(shí)現(xiàn)一個基于HTTP的協(xié)議善茎,并不是一個很輕松的事券册,而使用DBP的SDK,則可以極大的提升開發(fā)效率。
我們可以在https://github.com/dueros看到關(guān)于DuerOS的各種語言SDK烁焙,而Java SDK 就成為了Java程序員的福音航邢。
DBP的Bot-SDK-Java提供了一下主要功能:
- 封裝了DuerOS的request和response
- 提供了session的簡化接口
- 提供了nlu簡化接口
- 提供了多輪對話開發(fā)接口
- 提供了事件監(jiān)聽接口
需要注意的是,DBP的Java版SDK 需要在Java 8 及以上版本運(yùn)行骄蝇,采用Maven作為工程管理工具膳殷,同時DBP Java SDK的升級、維護(hù)也都通過Maven進(jìn)行發(fā)布九火,在pom.xml中添加最新版本依賴的示例如下:
<dependency>
<groupId>com.baidu.dueros</groupId>
<artifactId>bot-sdk</artifactId>
<version>1.1.8</version>
</dependency>
DBP Java SDK 淺析
從DBP Java SDK 的POM 文件中可以看到其中的依賴:
Javax.serverlet 3.0.1
Jackson 2.9.7
commons-codec 1.6
commons-io 2.4
commons-digester 2.1
百度的APM SDK 1.1.0
這與一般的Java 工程差別不大赚窃,唯一有點(diǎn)特殊的,可能算是APM的SDK了岔激。 對于微服務(wù)而言勒极,APM SDK 提供了類似sidecar之類的能力。
DBP Java SDK 工程結(jié)構(gòu)主體包括:
bot:技能服務(wù)的基類
certificate:資源證書的實(shí)現(xiàn)
data:DBP協(xié)議的實(shí)體封裝
model:對請求和響應(yīng)的封裝
nlu:對槽位和意圖的實(shí)現(xiàn)
samples: 示例代碼
從命名來看虑鼎,除了data稍顯歧義辱匿,其他都基本上可以見字知意。
DBP協(xié)議的主體——請求和響應(yīng)
Model目錄的request.java 和 response.java 實(shí)現(xiàn)了DBP協(xié)議中請求和響應(yīng)的封裝炫彩。
從面向?qū)ο蟮慕嵌瓤碦equest的組成大體如下:
而Response的響應(yīng)大體是這樣的:
各成員變量的意義可以參見《在面向協(xié)議的DuerOS技能開發(fā)》匾七。DBP協(xié)議中的實(shí)體分別位于Data目錄中的Request 和 Response Package。下面逐一看一下代碼中實(shí)現(xiàn)的協(xié)議實(shí)體江兢。
DBP協(xié)議Request中的實(shí)體
Request中的數(shù)據(jù)實(shí)體可以用戶相關(guān)昨忆,設(shè)備相關(guān),對話相關(guān)以及消息事件杉允。
用戶相關(guān)
Account類承載了百度賬號的信息邑贴,也就是說整個DBP平臺的用戶體系是基于百度賬號的,技能平臺可以基于百度賬號進(jìn)行用戶的綁定以及用戶系統(tǒng)的貫通叔磷。
CoordinatesInfo類表達(dá)了位置的經(jīng)緯度痢缎,而GeoLocation類承載了不同坐標(biāo)系的用戶位置信息,是LocationInfo 的成員變量世澜。
應(yīng)用服務(wù)相關(guān)
AccessToken類用于實(shí)現(xiàn)DuerOS設(shè)備與DuerOS技能之間的鑒權(quán)。
Application類表明了技能服務(wù)也就是bot的標(biāo)識署穗,開發(fā)者通過DBP管理平臺生成的bot-id就是這里的ApplicationID寥裂,而 Appinfo類描述了終端伴侶app的軟件信息,成員變量包括包括技能名稱案疲,應(yīng)用包名封恰,版本號及版本名稱。
Skillinfo 用于在有屏終端和App 伴侶上的技能呈現(xiàn)褐啡,包括了圖標(biāo)Icon類和技能的名稱诺舔。
終端設(shè)備的屬性特征
Device類和DeviceInfo類描述了基于DuerOS的終端終端信息,成員變量包括終端的ID以及終端的硬件能力即supportedInterface:
public class SupportedInterfaces {
private TextInput textInput;
private VoiceInput voiceInput;
private VoiceOutput voiceOutput;
private PlayController playController;
private AudioPlayer audioPlayer;
private Alerts alerts;
private com.baidu.dueros.data.request.supportedInterfaces.Screen screen;
private SpeakerController speakerController;
private com.baidu.dueros.data.request.supportedInterfaces.System system;
private ScreenExtendedCard screenExtendedCard;
private VideoPlayer videoPlayer;
private Display display;
....
Context是一個比較重要的類,成員變量包括:
- System類:系統(tǒng)信息包括Application低飒,User许昨,Device以及apiAccessToken和apiEndPoint
- 音/視頻播放器狀態(tài)
- 顯示屏幕的信息。
對話請求相關(guān)
RequestBody類封裝了http post中的body褥赊,是個基礎(chǔ)類糕档,繼承關(guān)系如下:
LaunchRequest是技能服務(wù)生命周期的開始,SessionEndedRequest是技能服務(wù)生命周期的終止拌喉,IntentRequest 則對應(yīng)著技能服務(wù)處理的消息循環(huán)速那。
IntentRequest 中的成員Query是DuerOS設(shè)備語音識別后的結(jié)果,成員DialogState代表對話的狀態(tài)尿背,對應(yīng)的意圖以列表方式表達(dá)端仰。
人機(jī)交互的對話是Dialog,而會話是session田藐。這里的Session是另一個重要的類荔烧,Session ID 是本次多輪對話的唯一標(biāo)識,更重要的是Session類中屬性列表:
private Map<String, String> attributes = new HashMap<String, String>();
該屬性列表實(shí)現(xiàn)了類似瀏覽器cookie的功能坞淮。
事件
事件是DBP協(xié)議中從DuerOS設(shè)備端發(fā)往技能服務(wù)的消息茴晋,目前包括顯示,音/視頻播放器回窘,支付和用戶授權(quán)事件诺擅。顯示相關(guān)的繼承關(guān)系如下:
LinkAccountSucceededEvent類對應(yīng)與協(xié)議中的Connections.Response事件,當(dāng)用戶授權(quán)完成之后啡直,技能服務(wù)會收到此事件烁涌。
ButtonClickedEvent和 RadioButtonClickedEvent了實(shí)現(xiàn)了Form.ButtonClicked和Form.RadioButtonClicked事件,當(dāng)用戶在有屏設(shè)備的表單上點(diǎn)擊按鈕/單選按鈕酒觅,技能服務(wù)會收到此事件撮执。
ElementSelectedEvent類實(shí)現(xiàn)了Display.ElementSelected事件,當(dāng)用戶在有屏設(shè)備的列表模版上選擇了某一項時舷丹,技能服務(wù)會收到此事件抒钱。
LinkClickedEvent類實(shí)現(xiàn)了Screen.LinkClicked事件,如果在卡片或者卡片列表配置了URL地址颜凯,當(dāng)用戶點(diǎn)擊卡片或者卡片列表時谋币,技能服務(wù)會收到此事件。
在Audioplayer 的事件中症概,AudioPlayerEvent繼承自httpbody蕾额,而其他事件類均繼承自AudioPlayerEvent。
事件名 對應(yīng)的類
AudioPlayer.PlaybackFailed PlaybackFailedEvent
AudioPlayer.PlaybackFinished PlaybackFinishedEvent
AudioPlayer.PlaybackNearlyFinished PlaybackNearlyFinishedEvent
AudioPlayer.PlaybackPaused PlaybackPausedEvent
AudioPlayer.PlaybackResumed PlaybackResumedEvent
AudioPlayer.PlaybackStarted PlaybackStartedEvent
AudioPlayer.PlaybackStopped PlaybackStoppedEvent
AudioPlayer.PlaybackStutterFinished PlaybackStutterFinishedEvent
AudioPlayer.PlaybackStutterStarted PlaybackStutterStartedEvent
AudioPlayer.ProgressReportDelayElapsed ProgressReportDelayElapsedEvent
AudioPlayer.ProgressReportIntervalElapsed ProgressReportIntervalElapsedEvent
類似的彼城,在videoplayer 的事件中诅蝶,VideoPlayerEvent繼承自httpbody退个,而其他事件類均繼承自VideoPlayerEvent。
事件名 對應(yīng)的類
VideoPlayer.PlaybackFinished PlaybackFinishedEvent
VideoPlayer.PlaybackNearlyFinished PlaybackNearlyFinishedEvent
VideoPlayer.PlaybackPaused PlaybackPausedEvent
VideoPlayer.PlaybackQueueCleared PlaybackQueueClearedEvent
VideoPlayer.PlaybackResumed PlaybackResumedEvent
VideoPlayer.PlaybackStarted PlaybackStartedEvent
VideoPlayer.PlaybackStopped PlaybackStoppedEvent
VideoPlayer.PlaybackStutterFinished PlaybackStutterFinishedEvent
VideoPlayer.PlaybackStutterStarted PlaybackStutterStartedEvent
VedioPlayer.ProgressReportDelayElapsed ProgressReportDelayElapsedEvent
VideoPlayer.ProgressReportIntervalElapsed ProgressReportIntervalElapsedEvent
支付及應(yīng)用內(nèi)付費(fèi)涉及到具體的商業(yè)模式调炬,ChargeEvent.java 是主要的支付事件语盈,繼承自RequestBody類,包括了支付指令的token和詳細(xì)的支付信息筐眷。
凡是涉及到用戶隱私的數(shù)據(jù)或者操作均需用戶授權(quán)黎烈,與用戶授權(quán)相關(guān)的事件有:
PermissionGrantFailedEvent.java
PermissionGrantedEvent.java
PermissionRejectedEvent.java
PermissionRequiredEvent.java
DBP協(xié)議Response中的實(shí)體
ResponseBody類是響應(yīng)的核心:
public class ResponseBody {
// 如果DuerOS仍然會選用當(dāng)前Bot結(jié)果,應(yīng)該再次下發(fā)請求匀谣,并將request.determined字段設(shè)置為true
private boolean needDetermine;
// 表示本次返回的結(jié)果是否為兜底結(jié)果
private boolean fallBack;
// 表示本次返回結(jié)果中需要播報的語音信息
private OutputSpeech outputSpeech;
// 在需要用戶輸入時照棋,如果用戶沒有輸入或用戶輸入內(nèi)容系統(tǒng)不理解,則播報reprompt內(nèi)容
private Reprompt reprompt;
// Bot輸出的Resource內(nèi)容
private Resource resource;
// Bot輸出的Card內(nèi)容武翎,用于在有屏場景下展示
private Card card;
// Bot輸出的指令烈炭,分為: 對DuerOS指令主要是對話指令;其他都是對端的指令
private List<Directive> directives = new ArrayList<>();
// 是否需要結(jié)束本次會話,DuerOS用于判斷是否需要關(guān)閉某個打開的Bot宝恶,端用于關(guān)閉麥克風(fēng)
private boolean shouldEndSession;
// 麥克風(fēng)是否開啟
private boolean expectSpeech;
......
輸出相關(guān)
Card類主要應(yīng)用于有屏設(shè)備的展示符隙,是個抽象類,派生出多種卡片展示:
ImageCard.java
LinkAccountCard.java
ListCard.java
StandardCard.java
TextCard.java
語音的輸出主要是通過OutputSpeech類來實(shí)現(xiàn)垫毙,Reprompt的應(yīng)用場景不同霹疫,但在實(shí)現(xiàn)上封裝了OutputSpeech類。
對于具體的播放資源實(shí)體综芥,通過Resource類實(shí)現(xiàn)丽蝎,可以使用各種Entity的各種數(shù)據(jù)對象,尤其要注意Entity中的token膀藐,錯誤的token設(shè)置可能會導(dǎo)致資源無法播放屠阻。
會話相關(guān)
Session類與Request package中的Session類類似,主要是HashMap的session attribute 列表额各。
技能服務(wù)的指令
和Event 對應(yīng)国觉, 技能服務(wù)返回一系列指令(directive)使DuerOS的設(shè)備端完成對應(yīng)的操作。Directive 是一個抽象類虾啦,其他的各種directive大都繼承于此麻诀。
音視頻播放器都保護(hù)play.java和stop.java 指令,但是視頻播放還有一個ClearQueue.java 的指令傲醉。同時针饥,二者都有對媒體內(nèi)容的封裝,例如Stream需频,AudioItem,VedioItem等筷凤。
與對話相關(guān)的指令都繼承自DialogDirective類昭殉,指令對應(yīng)的類文件如下所示:
事件名 對應(yīng)的類
Dialog.ElicitSlot ElicitSlot.java
Dialog.Delegate Delegate.java
Dialog.ConfirmIntent ConfirmIntent.java
Dialog.ConfirmSlot ConfirmSlot
需要注意的是苞七,Delegate代理的是處理邏輯,話術(shù)及意圖需要通過DBP 平臺進(jìn)行配置挪丢。
用戶授權(quán)的指令包括permission類和AskForPermissionsConsent類蹂风。 目前,DBP平臺提高了4種類型的用戶授權(quán):
public static final String READ_USER_PROFILE = "READ::USER:PROFILE";
public static final String READ_DEVICE_LOCATION = "READ::DEVICE:LOCATION";
public static final String WRITE_SMARTHOME_PRINTER = "WRITE::SMARTHOME:PRINTER";
public static final String RECORD_SPEECH = "RECORD::SPEECH";
當(dāng)然乾蓬,這些授權(quán)可以組合傳遞惠啄。
DBP 提供了輕量級的支付體系,類ChargeBaiduPay實(shí)現(xiàn)了主要的支付功能任内,SellerOrderAttributes 是訂單的信息撵渡。也就是說, DBP 是通過百度的聚合收銀臺實(shí)現(xiàn)了支付特性死嗦,用戶可以通過支付寶趋距、微信和百度錢包完成支付。
對DuerOS的有屏設(shè)備如小度在家而言越除,DBP的SDK還有完成觸摸屏上的人機(jī)交互节腐,主要指令只有一個Hint。但是摘盆,為了使開發(fā)更為高效翼雀,在Display.templates中提供了大量的模版可以使用。
NLU相關(guān)
NLU的模塊主要提供了Intent類和Slot類孩擂, 用于描述意圖和槽位狼渊。
public class Intent {
// 意圖名稱
private final String name;
// 意圖確認(rèn)狀態(tài)
private final ConfirmationStatus confirmationStatus;
// 意圖中的槽位
private final Map<String, Slot> slots;
// 意圖置信度
private final int score;
......
關(guān)于意圖和槽位的具體含義,可以參見《感知自然語言理解(NLU)》一文肋殴。
技能服務(wù)的載體——Bot
Bot 是技能服務(wù)的載體囤锉,BaseBot是所有Bot的基類,使用Bot-SDK開發(fā)的Bot都需要繼承這個類护锤。
public class BaseBot {
// Base子類構(gòu)造的Response
protected Response response;
// Bot收到的Request請求
private final Request request;
// 會話信息
private Session session = new Session();
// 是否需要結(jié)束本次會話
private boolean shouldEndSession = false;
// 如果DuerOS仍然會選用當(dāng)前Bot結(jié)果官地,應(yīng)該再次下發(fā)請求,并將request.determined字段設(shè)置為true
private boolean needDetermine = false;
// 麥克風(fēng)開關(guān)是否打開
private boolean expectSpeech = false;
// 返回的指令
private List<Directive> directives = new ArrayList<Directive>();
// 通過NLU解析出來的Intent
private Intent intent;
// 是否打開參數(shù)驗(yàn)證烙懦,默認(rèn)為false
private boolean enableCertificate = false;
// afterSearchScore
private float afterSearchScore = 1.0f;
private List<ExpectResponse> expectResponses = new ArrayList<>();
// 認(rèn)證簽名
private Certificate certificate;
// 緩存認(rèn)證相關(guān)信息
private static ConcurrentHashMap<String, PublicKey> cache = new ConcurrentHashMap<>();
// 數(shù)據(jù)統(tǒng)計信息
public BotMonitor botMonitor;
......
BaseBot通過一系列方法完成了對Request/Response的讀取和設(shè)置驱入,可以通過Serverlet的request來構(gòu)造BaseBot:
protected BaseBot(HttpServletRequest request) throws IOException, JsonMappingException {
certificate = new Certificate(request);
String message = certificate.getMessage();
ObjectMapper mapper = new ObjectMapper();
this.request = mapper.readValue(message, Request.class);
this.botMonitor = new BotMonitor(message);
this.session.getAttributes().putAll(this.request.getSession().getAttributes());
}
BaseBot 還完成了事件的分發(fā),證書驗(yàn)證等功能氯析,是主要的邏輯處理引擎亏较。
AudioPlayer和VideoPlayer繼承自BaseBot,主要對DuerOS設(shè)備端上傳的事件進(jìn)行具體處理掩缓。
DBP Java SDK 的應(yīng)用
建議采用Maven 來構(gòu)建工程雪情,這樣可以避免庫版本的不一致性帶來的困擾。
通過DBP Java SDK 構(gòu)建一個DuerOS技能和開發(fā)一個web服務(wù)沒什么區(qū)別你辣,只需要實(shí)現(xiàn)對應(yīng)的Action 和自己的技能服務(wù)邏輯即可巡通。
以sample中的個稅計算器為例尘执,只有TaxAction.java和TaxBot.java兩個文件,但可以看到實(shí)現(xiàn)的要點(diǎn)宴凉。
Action的實(shí)現(xiàn)
TaxAction 繼承自HttpServlet誊锭,但只實(shí)現(xiàn)了http中的3個方法:Head,GET和POST弥锄。
在POST方法中實(shí)現(xiàn)了技能服務(wù)的入口:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根據(jù)request創(chuàng)建Bot
TaxBot bot;
try {
bot = new TaxBot(request);
// 線下調(diào)試時丧靡,可以關(guān)閉簽名驗(yàn)證
// bot.enableVerify();
bot.disableVerify();
// 調(diào)用bot的run方法
String responseJson = bot.run();
// 設(shè)置response的編碼UTF-8
response.setCharacterEncoding("UTF-8");
// 返回response
response.getWriter().append(responseJson);
// 打開簽名驗(yàn)證
// bot.enableVerify();
} catch (Exception e) {
e.printStackTrace();
response.getWriter().append(e.toString());
}
}
其中,在測試環(huán)境中籽暇,可以關(guān)閉簽名的驗(yàn)證温治。
TaxBot的實(shí)現(xiàn)
TaxBot繼承自BaseBot,onLaunch()方法是啟動對話的入口:
protected Response onLaunch(LaunchRequest launchRequest) {
// 新建文本卡片
TextCard textCard = new TextCard("所得稅為您服務(wù)");
// 設(shè)置鏈接地址
textCard.setUrl("www:....");
// 設(shè)置鏈接內(nèi)容
textCard.setAnchorText("setAnchorText");
// 添加引導(dǎo)話術(shù)
textCard.addCueWord("歡迎進(jìn)入");
// 新建返回的語音內(nèi)容
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "所得稅為您服務(wù)");
// 構(gòu)造返回的Response
Response response = new Response(outputSpeech, textCard);
return response;
}
onInent()方法實(shí)現(xiàn)了DuerOS處理后的語音意圖:
@Override
protected Response onInent(IntentRequest intentRequest) {
// 判斷NLU解析的意圖名稱是否匹配 inquiry
if ("inquiry".equals(intentRequest.getIntentName())) {
// 判斷NLU解析解析后是否存在這個槽位
if (getSlot("monthlysalary") == null) {
// 詢問月薪槽位monthlysalary
ask("monthlysalary");
return askSalary();
} else if (getSlot("location") == null) {
// 詢問城市槽位location
ask("location");
return askLocation();
} else if (getSlot("compute_type") == null) {
// 詢問查詢種類槽位compute_type
ask("compute_type");
return askComputeType();
} else {
// 具體計算方法
return compute();
}
}
return null;
}
onSessionEnded()方法用于結(jié)束會話:
@Override
protected Response onSessionEnded(SessionEndedRequest sessionEndedRequest) {
// 構(gòu)造TextCard
TextCard textCard = new TextCard("感謝使用所得稅服務(wù)");
textCard.setAnchorText("setAnchorText");
textCard.addCueWord("歡迎再次使用");
// 構(gòu)造OutputSpeech
OutputSpeech outputSpeech = new OutputSpeech(SpeechType.PlainText, "歡迎再次使用所得稅服務(wù)");
ListCard listCard = new ListCard();
StandardCardInfo item1 = new StandardCardInfo("title1", "content1");
StandardCardInfo item2 = new StandardCardInfo("title2", "content2");
listCard.addStandardCardInfo(item1);
listCard.addStandardCardInfo(item2);
// 構(gòu)造Response
Response response = new Response(outputSpeech, textCard);
return response;
}
具體的图仓,askLocation()和askComputeType()完成槽位的填充罐盔,compute()完成具體的個稅計算。
當(dāng)然救崔,開發(fā)技能服務(wù)需要從開發(fā)者注冊開始惶看,還需要在DBP的管理平臺對意圖技能配置單很簡單,從注冊到配置技能在10分鐘內(nèi)可以完成六孵,詳情參考官網(wǎng):dueros.baidu.com/dbp
小結(jié)
為了方便DBP協(xié)議實(shí)現(xiàn)纬黎,可以使用DBP SDK 提高開發(fā)的效率,理解SDK的實(shí)現(xiàn)方式和原理對于SDK的使用有著很大的幫助劫窒,從而本今,開發(fā)者可以方便且高效地發(fā)布自己的技能服務(wù)。對于嘗鮮的用戶而言主巍,可以嘗試小技能的開發(fā)冠息。無需編程,幾分鐘就可能實(shí)現(xiàn)一個自己的小技能孕索。
當(dāng)然逛艰,各種編程語言的DBP SDK還在持續(xù)演進(jìn)中,值得期待搞旭。
參考:
https://dueros.baidu.com/dbp
https://github.com/dueros/bot-sdk-java