C#開發(fā)微信門戶及應(yīng)用(31)--微信語義理解接口的實(shí)現(xiàn)和處理
微信語義理解接口提供從用戶自然語言輸入到結(jié)構(gòu)化解析的技術(shù)實(shí)現(xiàn),使用先進(jìn)的自然語言處理技術(shù)給開發(fā)者提供一站式的語義解析方案。該平臺(tái)覆蓋多個(gè)垂直領(lǐng)域的語義場景,部分領(lǐng)域還可以支持取得最終的展示結(jié)果。開發(fā)者無需掌握語義理解及相關(guān)技術(shù),只需根據(jù)自己的產(chǎn)品特點(diǎn),選擇相應(yīng)的服務(wù)即可搭建一套智能語義服務(wù)。結(jié)合語音識(shí)別接口,通過微信語音識(shí)別得到用戶的語音信息之后,經(jīng)過語義分析理解,得到用戶需求,及時(shí)回復(fù)用戶。本文介紹如何實(shí)現(xiàn)對(duì)微信語義接口的封裝處理,以及一些常用場景的調(diào)用。
1)微信語義理解接口
這個(gè)東西也就是把我們?nèi)粘5脑捳Z(稱之為自然語言)解析為對(duì)應(yīng)的信息結(jié)構(gòu)體,方便我們提取里面的相關(guān)信息進(jìn)行搜索查詢,并精確回應(yīng)給對(duì)應(yīng)的請(qǐng)求者的一個(gè)橋梁,其主要的功能就是解析我們所說的內(nèi)容。
微信開放平臺(tái)語義理解接口調(diào)用(http請(qǐng)求)簡單方便,用戶無需掌握語義理解及相關(guān)技術(shù),只需根據(jù)自己的產(chǎn)品特點(diǎn),選擇相應(yīng)的服務(wù)即可搭建一套智能語義服務(wù)。我們來看看微信語義理解接口的定義內(nèi)容。
http請(qǐng)求方式: POST(請(qǐng)使用https協(xié)議)
https://api.weixin.qq.com/semantic/semproxy/search?access_token=YOUR_ACCESS_TOKEN
POST數(shù)據(jù)格式:JSON,POST數(shù)據(jù)例子:
{ "query":"查一下明天從北京到上海的南航機(jī)票", "city":"北京", "category": "flight,hotel", "appid":"wxaaaaaaaaaaaaaaaa", "uid":"123456" }
參數(shù)說明
參數(shù) | 是否必須 | 參數(shù)類型 | 說明 |
access_token | 是 | String | 根據(jù)appid和appsecret獲取到的token |
query | 是 | String | 輸入文本串 |
category | 是 | String | 需要使用的服務(wù)類型,多個(gè)用“,”隔開,不能為空 |
latitude | 見接口協(xié)議文檔 | Float | 緯度坐標(biāo),與經(jīng)度同時(shí)傳入;與城市二選一傳入 |
longitude | 見接口協(xié)議文檔 | Float | 經(jīng)度坐標(biāo),與緯度同時(shí)傳入;與城市二選一傳入 |
city | 見接口協(xié)議文檔 | String | 城市名稱,與經(jīng)緯度二選一傳入 |
region | 見接口協(xié)議文檔 | String | 區(qū)域名稱,在城市存在的情況下可省;與經(jīng)緯度二選一傳入 |
appid | 是 | String | 公眾號(hào)唯一標(biāo)識(shí),用于區(qū)分公眾號(hào)開發(fā)者 |
uid | 否 | String | 用戶唯一id(非開發(fā)者id),用戶區(qū)分公眾號(hào)下的不同用戶(建議填入用戶openid),如果為空,則無法使用上下文理解功能。appid和uid同時(shí)存在的情況下,才可以使用上下文理解功能。 |
注:單類別意圖比較明確,識(shí)別的覆蓋率比較大,所以如果只要使用特定某個(gè)類別,建議將category只設(shè)置為該類別。
返回說明 正常情況下,微信會(huì)返回下述JSON數(shù)據(jù)包:
{ “errcode”:0, “query”:”查一下明天從北京到上海的南航機(jī)票”, “type”:”flight”, “semantic”:{ “details”:{ “start_loc”:{ “type”:”LOC_CITY”, “city”:”北京市”, “city_simple”:”北京”, “l(fā)oc_ori”:”北京” }, “end_loc”: { “type”:”LOC_CITY”, “city”:”上海市”, “city_simple”:”上海”, “l(fā)oc_ori”:”上?!? }, “start_date”: { “type”:”DT_ORI”, “date”:”2014-03-05”, “date_ori”:”明天” }, “airline”:”中國南方航空公司” }, “intent”:”SEARCH” }
?
返回參數(shù)說明
參數(shù) | 是否必須 | 參數(shù)類型 | 說明 |
errcode | 是 | Int | 表示請(qǐng)求后的狀態(tài) |
query | 是 | String | 用戶的輸入字符串 |
type | 是 | String | 服務(wù)的全局類型id,詳見協(xié)議文檔中垂直服務(wù)協(xié)議定義 |
semantic | 是 | Object | 語義理解后的結(jié)構(gòu)化標(biāo)識(shí),各服務(wù)不同 |
result | 否 | Array | 部分類別的結(jié)果 |
answer | 否 | String | 部分類別的結(jié)果html5展示,目前不支持 |
text | 否 | String | 特殊回復(fù)說明 |
上面就是微信官方給出的代碼案例,以及一個(gè)《語義理解接口協(xié)議文檔》,里面介紹了各個(gè)場景的語義結(jié)構(gòu)信息,雖然這個(gè)文檔好像好久都沒怎么更新,不過總體內(nèi)容還是穩(wěn)定的,我們可以通過這個(gè)文檔進(jìn)行相關(guān)的類庫設(shè)計(jì)工作。
?
2、語義理解接口的C#實(shí)現(xiàn)
根據(jù)《語義理解接口協(xié)議文檔》文檔,我們可以定義各種所需的語義結(jié)構(gòu)類庫,這些是我們開展語義接口的基礎(chǔ)類。
例如我們定義基礎(chǔ)的時(shí)間協(xié)議類,如下所示。
/// <summary> /// 時(shí)間相關(guān)協(xié)議datetime /// </summary> public class Semantic_DateTime { /// <summary> /// 單時(shí)間的描述協(xié)議類型:“DT_SINGLE”。DT_SINGLE又細(xì)分為兩個(gè)類別:DT_ORI和DT_INFER。DT_ORI是字面時(shí)間,比如:“上午九點(diǎn)”; /// DT_INFER是推理時(shí)間,比如:“提前5分鐘”。 時(shí)間段的描述協(xié)議類型:“DT_INTERVAL” /// 重復(fù)時(shí)間的描述協(xié)議類型:“DT_REPEAT” DT_ REPEAT又細(xì)分為兩個(gè)類別:DT_RORI和DT_RINFER。DT_RORI是字面時(shí)間,比如:“每天上午九點(diǎn)”;DT_RINFER是推理時(shí)間,比如:“工作日除外” /// </summary> public string type { get; set; } /// <summary> /// 24小時(shí)制,格式:HH:MM:SS,默認(rèn)為00:00:00 /// </summary> public string time { get; set; } /// <summary> /// Time的原始字符串 /// </summary> public string time_ori { get; set; } } /// <summary> /// 單時(shí)間的描述協(xié)議datetime /// </summary> public class Semantic_SingleDateTime : Semantic_DateTime { /// <summary> /// 格式:YYYY-MM-DD,默認(rèn)是當(dāng)天時(shí)間 /// </summary> public string date { get; set; } /// <summary> /// 格式:YYYY-MM-DD 農(nóng)歷 /// </summary> public string date_lunar { get; set; } /// <summary> /// date的原始字符串 /// </summary> public string date_ori { get; set; } }
當(dāng)然時(shí)間還有很多類型的定義,都基本上按照文檔所列的字段進(jìn)行處理,上面的代碼只是定義了常用的單時(shí)間的描述協(xié)議類型:“DT_SINGLE”。
除了時(shí)間協(xié)議,還有數(shù)字,地點(diǎn)位置等相關(guān)協(xié)議,如數(shù)字協(xié)議如下所示。
public class Semantic_Number { /// <summary> /// 大類型:“NUMBER” NUMBER又細(xì)分為如下類別:NUM_PRICE、NUM_PADIUS、NUM_DISCOUNT、NUM_SEASON、NUM_EPI、NUM_CHAPTER。 /// </summary> public string type { get; set; } /// <summary> /// 開始 /// </summary> public string begin { get; set; } /// <summary> /// 結(jié)束 /// </summary> public string end { get; set; } }
地點(diǎn)位置協(xié)議如下所示
/// <summary> /// 地點(diǎn)相關(guān)協(xié)議 /// </summary> public class Semantic_Location { /// <summary> /// 大類型:“LOC” LOC又細(xì)分為如下類別:LOC_COUNTRY、LOC_PROVINCE、LOC_CITY、LOC_TOWN、LOC_POI、NORMAL_POI。 /// </summary> public string type { get; set; } /// <summary> /// 國家 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string country { get; set; } /// <summary> /// 省全稱,例如:廣東省 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string province { get; set; } /// <summary> /// 省簡稱,例如:廣東|粵 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string province_simple { get; set; } /// <summary> /// 市全稱,例如:北京市 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string city { get; set; } /// <summary> /// 市簡稱,例如:北京 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string city_simple { get; set; } ..............
前面我們看到了,語音立即的POST數(shù)據(jù)格式是一個(gè)較為固定的格式內(nèi)容,我們可以把它定義為一個(gè)類,方便數(shù)據(jù)處理。
POST數(shù)據(jù)格式:JSON,POST數(shù)據(jù)例子如下所示:
{ "query":"查一下明天從北京到上海的南航機(jī)票", "city":"北京", "category": "flight,hotel", "appid":"wxaaaaaaaaaaaaaaaa", "uid":"123456" }
那么我們可以定義它的類庫如下所示。
/// <summary> /// 語義查詢條件 /// </summary> public class SemanticQueryJson { /// <summary> /// 輸入文本串 /// 必填 /// </summary> public string query { get; set; } /// <summary> /// 需要使用的服務(wù)類別,多個(gè)用,隔開,不能為空 /// 必填 /// </summary> public string category { get; set; } /// <summary> /// 城市名稱,與經(jīng)緯度二選一傳入 /// 見說明,選填 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string city { get; set; } /// <summary> /// App id,開發(fā)者的唯一標(biāo)識(shí),用于區(qū)分開放者,如果為空,則沒法使用上下文理解功能。 /// 非必填 /// </summary> public string appid { get; set; } /// <summary> /// 用戶唯一id(并非開發(fā)者id),用于區(qū)分該開發(fā)者下不同用戶,如果為空,則沒法使用上下文理解功能。appid和uid同時(shí)存在的情況下,才可以使用上下文理解功能。 /// 非必填 /// </summary> public string uid { get; set; } ................ }
接著我們分析語義理解的接口返回值,它們基本上都是很有規(guī)律的內(nèi)容,如下所示。
這樣我們也就可以定義一個(gè)通用的類庫對(duì)象,用來存儲(chǔ)不同的返回內(nèi)容了,如下代碼所示。
/// <summary> /// 微信語義結(jié)果 /// </summary> public class SemanticResultJson<T> : ErrorJsonResult { /// <summary> /// 用戶的輸入字符串 /// </summary> public string query { get; set; } /// <summary> /// 服務(wù)的全局類別id /// </summary> public string type { get; set; } /// <summary> /// 語義理解后的結(jié)構(gòu)化標(biāo)識(shí),各服務(wù)不同 /// </summary> public T semantic { get; set; } }
而其中的T semantic就是另外一個(gè)結(jié)構(gòu)體里面的內(nèi)容,這個(gè)結(jié)構(gòu)體總體也是固定的內(nèi)容,我們繼續(xù)定義一個(gè)如下的類。
/// <summary> /// 詳細(xì)信息里面的對(duì)象 /// </summary> /// <typeparam name="T"></typeparam> public class SemanticDetail<T> { /// <summary> /// 詳細(xì)信息 /// </summary> public T details { get; set; } /// <summary> /// 查詢類型 /// </summary> public string intent { get; set; } }
有了這些類庫的支持,我們可以封裝語義理解接口的返回值了,這樣它的接口定義和封裝處理代碼如下所示。
/// <summary> /// 語意理解接口 /// 微信開放平臺(tái)語義理解接口調(diào)用(http請(qǐng)求)簡單方便,用戶無需掌握語義理解及相關(guān)技術(shù),只需根據(jù)自己的產(chǎn)品特點(diǎn),選擇相應(yīng)的服務(wù)即可搭建一套智能語義服務(wù)。 /// </summary> public class SemanticApi : ISemanticApi { /// <summary> /// 發(fā)送語義理解請(qǐng)求 /// </summary> /// <param name="accessToken">調(diào)用接口憑證</param> /// <param name="data">查詢條件</param> public SemanticResultJson<SemanticDetail<T>> SearchSemantic<T>(string accessToken, SemanticQueryJson data) { var url = string.Format("https://api.weixin.qq.com/semantic/semproxy/search?access_token={0}", accessToken); string postData = data.ToJson(); return JsonHelper<SemanticResultJson<SemanticDetail<T>>>.ConvertJson(url, postData); }
由于微信語義結(jié)果是針對(duì)不同的服務(wù)協(xié)議,我們需要根據(jù)這些不同的服務(wù)協(xié)議,來定義屬于這些信息結(jié)構(gòu),如在文檔里,我們可以看到有很多不同類型的服務(wù)協(xié)議。
根據(jù)文檔的詳細(xì)字段說明,我們可以定義不同服務(wù)的對(duì)應(yīng)類庫。
例如對(duì)于旅游服務(wù)的語義理解,它們的協(xié)議類如下所示。
/// <summary> /// 旅游服務(wù)(travel) /// </summary> public class Semantic_Details_Travel { /// <summary> /// 旅游目的地 /// </summary> public Semantic_Location location { get; set; } /// <summary> /// 景點(diǎn)名稱 /// </summary> public string spot { get; set; } /// <summary> /// 旅游日期 /// </summary> public Semantic_SingleDateTime datetime { get; set; } /// <summary> /// 旅游類型詞 /// </summary> public string tag { get; set; } /// <summary> /// 0默認(rèn),1自由行,2跟團(tuán)游 /// </summary> public int category { get; set; } }
那么調(diào)用的旅游語義的案例代碼如下所示
var api = new SemanticApi(); var json = new SemanticQueryJson { appid = appId, uid = openId, category = SemanticCategory.travel.ToString(), query = "故宮門票多少錢", city = "北京市" }; var travel = api.SearchSemantic<Semantic_Details_Travel>(token, json); Console.WriteLine(travel.ToJson());
如果我們測試,上面的代碼跑起來會(huì)返回一個(gè)旅游的協(xié)議對(duì)象,包括了相關(guān)的數(shù)據(jù)信息。
{ "errcode" : 0, "query" : "故宮門票多少錢", "semantic" : { "details" : { "answer" : "", "context_info" : {}, "hit_str" : "故宮 門票 多少 錢 ", "spot" : "故宮" }, "intent" : "PRICE" }, "type" : "travel" }
我們?cè)賮砜匆粋€(gè)例子,例如對(duì)于航班服務(wù),我們定義它的語義理解協(xié)議如下所示。
/// <summary> /// 航班服務(wù)(flight) /// </summary> public class Semantic_Details_Flight { /// <summary> /// 航班號(hào) /// </summary> public string flight_no { get; set; } /// <summary> /// 出發(fā)地 /// </summary> public Semantic_Location start_loc { get; set; } /// <summary> /// 目的地 /// </summary> public Semantic_Location end_loc { get; set; } /// <summary> /// 出發(fā)日期 /// </summary> public Semantic_SingleDateTime start_date { get; set; } /// <summary> /// 返回日期 /// </summary> public Semantic_SingleDateTime end_date { get; set; } /// <summary> /// 航空公司 /// </summary> public string airline { get; set; } /// <summary> /// 座位級(jí)別(默認(rèn)無限制):ECONOMY(經(jīng)濟(jì)艙)BIZ(商務(wù)艙)FIRST(頭等艙) /// </summary> public string seat { get; set; } /// <summary> /// 排序類型:0排序無要求(默認(rèn)),1價(jià)格升序,2價(jià)格降序,3時(shí)間升序,4時(shí)間降序 /// </summary> public int sort { get; set; } }
那么調(diào)用獲取語義理解內(nèi)容的代碼如下所示。
json = new SemanticQueryJson { appid = appId, uid = openId, category = SemanticCategory.flight.ToString(), query = "查一下明天從廣州到上海的南航機(jī)票", city = "廣州" }; var flight = api.SearchSemantic<Semantic_Details_Flight>(token, json); Console.WriteLine(flight.ToJson());
我們可以獲取到的JSON數(shù)據(jù)如下所示
{ "errcode" : 0, "query" : "查一下明天從廣州到上海的南航機(jī)票", "semantic" : { "details" : { "airline" : "中國南方航空公司", "answer" : "已幫您預(yù)定2016-04-13,從廣州市出發(fā),前往上海市的航班。", "context_info" : { "isFinished" : "1", "null_times" : "0" }, "end_loc" : { "city" : "上海市", "city_simple" : "上海|滬|申", "loc_ori" : "上海", "modify_times" : "0", "slot_content_type" : "2", "type" : "LOC_CITY" }, "hit_str" : "查 一下 明天 從 廣州 到 上海 南航 機(jī)票 ", "sort" : "1", "start_date" : { "date" : "2016-04-13", "date_lunar" : "2016-03-07", "date_ori" : "明天", "modify_times" : "0", "slot_content_type" : "2", "type" : "DT_ORI", "week" : "3" }, "start_loc" : { "city" : "廣州市", "city_simple" : "廣州", "loc_ori" : "廣州", "modify_times" : "0", "province" : "廣東省", "province_simple" : "廣東|粵", "slot_content_type" : "2", "type" : "LOC_CITY" } }, "intent" : "SEARCH" }, "type" : "flight" }
這樣就是我們把我們常規(guī)的語義,分析成了機(jī)器可以識(shí)別的準(zhǔn)確的數(shù)據(jù)結(jié)構(gòu)了,我們可以根據(jù)不同的語義場合對(duì)它進(jìn)行分析,然后給用戶進(jìn)行不同的響應(yīng)就可以了,結(jié)合微信語音識(shí)別為文本內(nèi)容,我們可以把它做得很強(qiáng)大,有的類似機(jī)器智能的味道了。
微信語義理解是一個(gè)好東西,不過在微信官網(wǎng)上沒有看到進(jìn)一步的案例,如果能夠有一些與實(shí)際結(jié)合的例子,估計(jì)更能幫助我們理解和應(yīng)用了。
?
專注于Winform開發(fā)框架/混合式開發(fā)框架、Web開發(fā)框架、Bootstrap開發(fā)框架、微信門戶開發(fā)框架的研究及應(yīng)用。
??轉(zhuǎn)載請(qǐng)注明出處:
撰寫人:伍華聰?
本文摘自 :https://blog.51cto.com/w