告訴你如何使用OLAMI自然語言理解開放平臺API製作自己的智能對話助手 我們經常在電影中看到機器和人對答如流,隨著越來越多自然語言開放平臺的出現,IT愛好者製作一個自己的APP或者小玩具等逐漸可以變為現實。 自然語言對話即你的APP或者你製作的工具、機器人等能夠對用戶輸入的語音或者文字做... ...
我們經常在電影中看到機器和人對答如流,隨著越來越多自然語言開放平臺的出現,IT愛好者製作一個自己的APP或者小玩具等逐漸可以變為現實。
自然語言對話即你的APP或者你製作的工具、機器人等能夠對用戶輸入的語音或者文字做出準確的回應。
比如,在微信公眾號中,經常要求用戶通過輸入1、2或者其他關鍵字來獲取相應的服務,而對於句子卻無法正確理解。比如,你輸入“中秋活動”,這個幾個字如果符合關鍵字的要求,那就會彈出相應的服務。但如果你輸入的是“我想參加今年的中秋活動”,“參加中秋活動”等可能就無法進入活動網頁了。
再比如,很多智能玩具,你只能跟它進行簡單的對話,因為它也是只能抓取簡單的關鍵字。
實際上,我們希望的是做到像許多語音助手一樣,能夠正確識別用戶說的話,並做出對應回覆,甚至希望能夠理解上下文。
比如,用戶問:今天上海的天氣
應用回答:今天上海的天氣為。。。。。
如果用戶接著問: 那北京呢
你的應用能直接回答:今天北京的天氣。。。。。
而不需要輸入完整的句子“今天北京的天氣”。
目前提供智能語音語義理解的API介面不少,使得自己製作語義語音小助手成為現實。各開放平臺的情況可以看我的另一篇博客:熱門自然語言理解和語音API開發平臺對比
我選擇了歐拉蜜人工智慧開放平臺來完成我的快遞小助手。
要使用OLAMI開放平臺首先要了平臺的提供的功能和我們需要做的事情
我使用微信小程式開發工具寫了一個工具類的小程式,可以參考我的另一篇博客 微信小程式+OLAMI自然語言API介面製作智能查詢工具--快遞、聊天、日曆等
代碼下載 CSDN代碼
語法文件下載CSDN語法文件下載
代碼測試和體驗
下圖簡單描述了OLAMI語言理解開放平臺的工作過程,我們要做的工作其實就是圖中紅色的標誌1和標誌2,即寫語法和寫自己的應用程式。而你的應用程式用戶只需要輸入想說的話,即自然語言句子,就可以得到你的應用程式處理的結果。
************
一.瞭解幾個關鍵詞的含義
- 語法,我的理解就是描述自然語言句子的一套規則,看官方文檔也確實是這樣。比如:“幫我查個快遞”,你用一堆符號描述出來,這個組合起來的符號就叫語法。
- 自然語言,也叫語料。這個不用多說,我們平時說的話是什麼,那就是什麼。
- 語料的有效信息,即輸入的句子中包含的關鍵信息,你的應用程式獲取到這些這些關鍵信息之後,可以做相應的處理。比如:‘請你幫我查個快遞’,其實這句話隱藏的含義是“查快遞”,其他的辭彙信息不需要關心。
- Answer---這個在OLAMI平臺中指你寫的語法的預設回覆,不需要應用程式做深度處理的,這個暫時可以忽略。
- 應用程式----這就是你自己的APP了。
- 結果----就是用戶輸入一句話,你的APP回覆結果。比如,用戶輸入’查快遞1234’,你應該回覆‘1234這個快遞的物流信息’。再比如“今天的天氣”,你應該列出今天的天氣狀況,而不是昨天的天氣,也不是今天的日期。
二 寫語法
調用API介面之前,首先要寫自己的語法,也就是你要支持哪些句子,當然你也可以直接使用OLAMI平臺提供的內置語法,如果它的語法符合你的要求的話。在這裡,我說說怎麼寫自己的語法。
- 首先在官網上註冊,然後進入開發系統中“我的應用”。
2.確定自己的模塊
在正式寫語法之前,首先得弄明白怎麼寫語法,寫什麼樣的語法。
第一步得選擇你的應用程式支持的模塊,比如是查天氣、播放音樂、還是智能家控制。
這個模塊,其實可以稱之為領域,因為你不可能把人類所有的語言都涵蓋,你的APP一般都是針對某個或者某幾個領域。我選擇的是快遞APP。所以我要支持的都是快遞的說法。
確定好模塊之後,你要思考一下你的應用程式希望從用戶的說法中得到哪些有用信息,以及用戶會有哪些操作。比如快遞查詢,我需要有“運單號”,“快遞公司名稱”這兩個有效信息,另外,我的應用程式僅提供快遞的查詢業務。
我大致知道如果用戶說“幫我查一下圓通快遞12344”的時候,包含的有效信息很全,我可以直接輸出物流信息給他。但如果用戶僅僅說“我想查快遞”,這時他僅表達了想查的意願,我的應用程式應該提示用戶輸入快遞單號。
在OALMI的語法中,使用”Slot”來抓取有效信息,它就像一個函數的參數,它的內容由用戶決定。比如運單號和快遞公司名稱,每個用戶的內容都是不同的。
因此進入OLAMI的NLI系統之後,我首先“新增”一個模塊,名字為”expressage”,然後進入這個模塊開始寫語法。
3.確定有效信息Slot
就如上面所述,我需要“運單號”和“快遞公司名稱”這兩個關鍵信息,因此,我定義了”expnumber”和”expname”這兩個Slot.
我選擇的類型均為’ext’,因為用戶有可能會直接輸入運單號,而運單號的格式無法確定,所以我選擇ext來抓取。
快遞公司的名稱也是有限的,其實選擇internal格式的就可以了,但是我選擇後面通過ext賦值的方式給slot賦值,這樣有利於語法維護。(很拗口是吧?可以暫時不管)
4.確認APP的功能
你的APP準備提供什麼功能呢?查詢?顯示?打開?關閉?OLAMI語法需要用Modifier來描述操作信息。其實也就是一個標誌來告訴應用程式這句話的意圖是什麼。
比如“打開燈”,意圖是打開,可以定義一個modifier “open”.
“打開空調”的意圖同樣是打開,你只要用沿用已經定義的“open”即可。
我的快遞APP很簡單,我就是提供查詢功能,因此我定義一個global modifier ‘query’.
5.瞭解Grammar,Rule,Template.
其實剛開始只要知道Grammar和Rule就可以了。
Rule即同義辭彙的集合,辭彙之間用’|’隔開,表示或的關係。
Grammar即描述你要匹配的句子的語法。
比如你希望匹配句子”查詢快遞”,可以將“查詢”的同義詞定義一個Rule, “快遞”同義辭彙定義一個Rule,我建議名稱能使用中文就使用中文,這樣看起來比較直觀。下表中是可以匹配”查詢快遞”這句話的Grammar相關定義。
***
名稱 | 類型 | 內容 |
---|---|---|
查詢_動詞 | Rule | 查詢\ |
快遞_名詞 | Rule | 快遞\ |
expname | Ext Slot | |
expnumber | Ext Slot | |
查快遞1 | Grammar | <查詢_動詞><快遞_名詞>{@=query} |
查快遞_名稱_運單號1 | Grammar | <查詢_動詞>< expname ><快遞_名詞>{@=query_name_num} |
你在寫Grammar之前要確保Grammar中需要的Rule,Slot,Template已經定義好,並且想好自己的操作modifier.
每寫好一個Grammar可以通過“例句測試”檢查你要支持的句子是不是被當前的Grammar匹配,這個Grammar希望支持的句子都包含進去了,你再提交,然後發佈。
發佈之後你才能通過API介面進行訪問。
三 開發自己的APP
- 你需要從語義理解API介面獲取什麼信息?
https的返回,比如status就不再介紹了。
說白了,開發平臺解析你的語法之後,就是會告訴你這句話中的Slot和modifier,以及你的模塊名稱。
Slot根據你選擇的類型不同,你獲取的內容不同。比如ext類型的,你可以拿到slot的名稱和slot的值。
Datetime類型,即你的Slot是時間,你還可以拿到時間的毫秒數,起始時間等。
Number類型,表示你只會抓取數字,會得到數字的計算值等。
Modifier就是你自己定義的要支持的操作,只要按照他們規定的格式命名就好。
2.創建應用
應用可以包含多個模塊,具體包含哪些模塊也是由你自己決定。OLAMI預設支持了“聊天”,“百科”,“查詢日期”三個模塊。如果你不需要可以去掉。內置模塊的說明見
點擊下圖中的配置模塊添加自己寫的模塊和你希望添加的內置模塊。比如對話系統模塊中的nonsense就是聊天用的。你可以點配置模塊右邊的”測試”,輸入要查詢的句子就可以看到結果。
如果測試結果能正確返回,就表明API介面也可以獲取同樣的結果。
比如我在輸入框中“查快遞”就可以看到JSON格式的輸出,如下圖顯示:
3.查看應用的key
這個key就是你訪問API 介面的鑰匙,在你的應用中點擊”查看key”就可以看到了。
4.訪問自然語言解析API介面
API介面是https協議,相關說明,我就不再贅述了。
我使用的是小程式訪問,相關代碼如下,你代碼下載包的input.js里可以看到:
function parseCorpus(corpus,object) {
var usekey = Appkey;
var usesecret = Appsecret;
if (object.data.dialogtype == chat_type){
usekey = ChatAppkey;
usesecret = ChatAppsecret;
}
//獲取sign的MD5值
object.setData({
text: '請稍後......'
})
var timestamp = new Date().getTime();
var originalSign = usesecret + "api=" + api + "appkey=" + usekey + "timestamp=" + timestamp + usesecret;
var sign = MD5.md5(originalSign);
var rqdata = { "data": { "input_type": 1, "text": corpus }, "data_type": "stt" };
console.log(JSON.stringify(rqdata))
console.log('\r\n')
wx.request({
url: requestUrl,
data: {
appkey: usekey,
api: api,
timestamp: timestamp,
sign: sign,
rq: JSON.stringify(rqdata),
cusid: userId,
changebuttoncolor: "#d0e0e3"
},
header: {
'content-type': 'application/x-www-form-urlencoded'
},
method: 'POST',
success: function (result) {
var data = result.data.data;
if (result.data != null && result.data.status!=null&&result.data.status=='ok'){
HandleOLAMIresponseData(data.nli[0], corpus, object);
console.log('歐拉蜜有效數據', result.data);
}else{
console.log('歐拉蜜返回失敗', result.data.status);
object.setData({
text: API_data_error
})
}
},
fail: function ({errMsg}) {
console.log('request fail', errMsg)
object.setData({
text: API_data_error
})
}
})
};
- 根據獲取到的Semantics內容提供服務
我的處理邏輯是根據不同的modifier進行相應的操作,不同的操作下又要檢查slot,代碼如下:
function HandleOLAMIresponseData(data, corpus, object)
{
var textData=''; //text文本框要show的內容
var semantics = data.semantic;
if (semantics == null || semantics.length==0){
if (data.desc_obj.result != null && data.desc_obj.result.length != 0 && data.desc_obj.status==0) {
if (data.type == 'joke' || data.type == 'cooking'){
textData = data.data_obj[0].content;
}else
textData = data.desc_obj.result;
}else
textData='抱歉,我還理解不了你說的話。';
object.setData({
expresshead: '',
text: textData
})
}else {
for (var i = 0; i < semantics.length; i++) {
var tempSem = data.semantic[0];
if (tempSem.app == expressAPPname) { //僅處理快遞模塊的語義
//處理modifier
var mods = tempSem.modifier;
if (mods.indexOf("query") > -1)
expAppinfo.OPT = OPT_QUERY;
else if (mods.indexOf("query_num") > -1)
expAppinfo.OPT = OPT_QUERY_NUM;
else if (mods.indexOf("query_name") > -1)
expAppinfo.OPT = OPT_QUERY_NAME;
else if (mods.indexOf("query_name_num") > -1)
expAppinfo.OPT = OPT_QUERY_NUM_NAME;
//獲取slots,即快遞公司名稱和運單號
var slots = tempSem.slots;
if (slots != null) {
for (var j = 0; j < slots.length; j++) {
var tempslot = slots[j];
if (tempslot.name == expNumSlotName) { //運單號
var numslot = new APPSlot();
numslot.name = expNumSlotName;
numslot.value = tempslot.value;
expAppinfo.numSlot = numslot;
} else if (tempslot.name == expNameSlotName) {//快遞名稱
var nameslot = new APPSlot();
nameslot.name = expNumSlotName;
nameslot.value = tempslot.value;
expAppinfo.nameSlot = nameslot;
}
}
}
//handle Operations
switch (expAppinfo.OPT) {
case OPT_QUERY:
textData = '請提供您的運單編號。';
object.setData({
expresshead: '',
text: textData
})
break;
case OPT_QUERY_NUM:
//檢測是否存在快遞名稱
if (expAppinfo.nameSlot != null && expAppinfo.numSlot != null){ //採用快遞編號+快遞公司方式查詢
//獲取快遞公司名稱
var expname = getExpCode(expAppinfo.nameSlot.value);
var expcode = expCodes[expname];
queryExpress.queryExpress(expname,expcode, expAppinfo.numSlot.value, object);
} else if (expAppinfo.numSlot != null){
//用運單編號查詢
queryExpress.queryEXPbyNum(expAppinfo.numSlot.value, object);
}
resetExpInfo(object);
break;
case OPT_QUERY_NAME:
textData = '請提供您的運單編號。';
object.setData({
expresshead: '',
text: textData
})
break;
case OPT_QUERY_NUM_NAME:
if (expAppinfo.nameSlot != null && expAppinfo.numSlot != null) { //採用快遞編號+快遞公司方式查詢
//獲取快遞公司名稱
var expname = getExpCode(expAppinfo.nameSlot.value);
var expcode = expCodes[expname];
queryExpress.queryExpress(expname,expcode, expAppinfo.numSlot.value, object);
}
resetExpInfo(object);
break;
}
break;
}
}
}
}
至此,OLAMI API 介面的基本調用工作已經完成,至於你要添加語言識別,語法完善,模塊添加等就看自己的需求了。
最後說一下語法文件.osl下載之後如何導入。你創建好模塊之後,直接選擇上傳OSL文件即可
優秀自然語言理解博客文章推薦:
微信小程式+OLAMI自然語言API介面製作智能查詢工具--快遞、聊天、日曆等
自然語言處理-實際開發:用語義開放平臺olami寫一個翻譯的應用
使用OLAMI SDK和訊飛語音合成製作一個語音回覆的簡訊小助手
推薦自然語言理解愛好者博客:
http://blog.csdn.net/huangmeimao
http://blog.csdn.net/u011211290
http://blog.csdn.net/u011827504
http://blog.csdn.net/xinfinityx