微信語義理解接口提供從用戶自然語言輸入到結構化解析的技術實現,使用先進的自然語言處理技術給開發者提供一站式的語義解析方案。該平台覆蓋多個垂直領域的語義場景,部分領域還可以支持取得最終的展示結果。開發者無需掌握語義理解及相關技術,只需根據自己的產品特點,選擇相應的服務即可搭建一套智能語義服務。結合語音識別接口,通過微信語音識別得到用戶的語音信息之後,經過語義分析理解,得到用戶需求,及時回復用戶。本文介紹如何實現對微信語義接口的封裝處理,以及一些常用場景的調用。
這個東西也就是把我們日常的話語(稱之為自然語言)解析為對應的信息結構體,方便我們提取裡面的相關信息進行搜索查詢,並精確回應給對應的請求者的一個橋梁,其主要的功能就是解析我們所說的內容。
微信開放平台語義理解接口調用(http請求)簡單方便,用戶無需掌握語義理解及相關技術,只需根據自己的產品特點,選擇相應的服務即可搭建一套智能語義服務。我們來看看微信語義理解接口的定義內容。
http請求方式: POST(請使用https協議)
https://api.weixin.qq.com/semantic/semproxy/search?access_token=YOUR_ACCESS_TOKEN
POST數據格式:JSON,POST數據例子:
{
"query":"查一下明天從北京到上海的南航機票",
"city":"北京",
"category": "flight,hotel",
"appid":"wxaaaaaaaaaaaaaaaa",
"uid":"123456"
}
參數說明
參數 是否必須 參數類型 說明 access_token 是 String 根據appid和appsecret獲取到的token query 是 String 輸入文本串 category 是 String 需要使用的服務類型,多個用“,”隔開,不能為空 latitude 見接口協議文檔 Float 緯度坐標,與經度同時傳入;與城市二選一傳入 longitude 見接口協議文檔 Float 經度坐標,與緯度同時傳入;與城市二選一傳入 city 見接口協議文檔 String 城市名稱,與經緯度二選一傳入 region 見接口協議文檔 String 區域名稱,在城市存在的情況下可省;與經緯度二選一傳入 appid 是 String 公眾號唯一標識,用於區分公眾號開發者 uid 否 String 用戶唯一id(非開發者id),用戶區分公眾號下的不同用戶(建議填入用戶openid),如果為空,則無法使用上下文理解功能。appid和uid同時存在的情況下,才可以使用上下文理解功能。注:單類別意圖比較明確,識別的覆蓋率比較大,所以如果只要使用特定某個類別,建議將category只設置為該類別。
返回說明 正常情況下,微信會返回下述JSON數據包:
{
“errcode”:0,
“query”:”查一下明天從北京到上海的南航機票”,
“type”:”flight”,
“semantic”:{
“details”:{
“start_loc”:{
“type”:”LOC_CITY”,
“city”:”北京市”,
“city_simple”:”北京”,
“loc_ori”:”北京”
},
“end_loc”: {
“type”:”LOC_CITY”,
“city”:”上海市”,
“city_simple”:”上海”,
“loc_ori”:”上海”
},
“start_date”: {
“type”:”DT_ORI”,
“date”:”2014-03-05”,
“date_ori”:”明天”
},
“airline”:”中國南方航空公司”
},
“intent”:”SEARCH”
}
返回參數說明
參數 是否必須 參數類型 說明 errcode 是 Int 表示請求後的狀態 query 是 String 用戶的輸入字符串 type 是 String 服務的全局類型id,詳見協議文檔中垂直服務協議定義 semantic 是 Object 語義理解後的結構化標識,各服務不同 result 否 Array 部分類別的結果 answer 否 String 部分類別的結果html5展示,目前不支持 text 否 String 特殊回復說明上面就是微信官方給出的代碼案例,以及一個《語義理解接口協議文檔》,裡面介紹了各個場景的語義結構信息,雖然這個文檔好像好久都沒怎麼更新,不過總體內容還是穩定的,我們可以通過這個文檔進行相關的類庫設計工作。

根據《語義理解接口協議文檔》文檔,我們可以定義各種所需的語義結構類庫,這些是我們開展語義接口的基礎類。
例如我們定義基礎的時間協議類,如下所示。
/// <summary>
/// 時間相關協議datetime
/// </summary>
public class Semantic_DateTime
{
/// <summary>
/// 單時間的描述協議類型:“DT_SINGLE”。DT_SINGLE又細分為兩個類別:DT_ORI和DT_INFER。DT_ORI是字面時間,比如:“上午九點”;
/// DT_INFER是推理時間,比如:“提前5分鐘”。 時間段的描述協議類型:“DT_INTERVAL”
/// 重復時間的描述協議類型:“DT_REPEAT” DT_ REPEAT又細分為兩個類別:DT_RORI和DT_RINFER。DT_RORI是字面時間,比如:“每天上午九點”;DT_RINFER是推理時間,比如:“工作日除外”
/// </summary>
public string type { get; set; }
/// <summary>
/// 24小時制,格式:HH:MM:SS,默認為00:00:00
/// </summary>
public string time { get; set; }
/// <summary>
/// Time的原始字符串
/// </summary>
public string time_ori { get; set; }
}
/// <summary>
/// 單時間的描述協議datetime
/// </summary>
public class Semantic_SingleDateTime : Semantic_DateTime
{
/// <summary>
/// 格式:YYYY-MM-DD,默認是當天時間
/// </summary>
public string date { get; set; }
/// <summary>
/// 格式:YYYY-MM-DD 農歷
/// </summary>
public string date_lunar { get; set; }
/// <summary>
/// date的原始字符串
/// </summary>
public string date_ori { get; set; }
}
當然時間還有很多類型的定義,都基本上按照文檔所列的字段進行處理,上面的代碼只是定義了常用的單時間的描述協議類型:“DT_SINGLE”。
除了時間協議,還有數字,地點位置等相關協議,如數字協議如下所示。
public class Semantic_Number
{
/// <summary>
/// 大類型:“NUMBER” NUMBER又細分為如下類別: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>
/// 結束
/// </summary>
public string end { get; set; }
}
地點位置協議如下所示
/// <summary>
/// 地點相關協議
/// </summary>
public class Semantic_Location
{
/// <summary>
/// 大類型:“LOC” LOC又細分為如下類別: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數據格式是一個較為固定的格式內容,我們可以把它定義為一個類,方便數據處理。
POST數據格式:JSON,POST數據例子如下所示:
{
"query":"查一下明天從北京到上海的南航機票",
"city":"北京",
"category": "flight,hotel",
"appid":"wxaaaaaaaaaaaaaaaa",
"uid":"123456"
}
那麼我們可以定義它的類庫如下所示。
/// <summary>
/// 語義查詢條件
/// </summary>
public class SemanticQueryJson
{
/// <summary>
/// 輸入文本串
/// 必填
/// </summary>
public string query { get; set; }
/// <summary>
/// 需要使用的服務類別,多個用,隔開,不能為空
/// 必填
/// </summary>
public string category { get; set; }
/// <summary>
/// 城市名稱,與經緯度二選一傳入
/// 見說明,選填
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string city { get; set; }
/// <summary>
/// App id,開發者的唯一標識,用於區分開放者,如果為空,則沒法使用上下文理解功能。
/// 非必填
/// </summary>
public string appid { get; set; }
/// <summary>
/// 用戶唯一id(並非開發者id),用於區分該開發者下不同用戶,如果為空,則沒法使用上下文理解功能。appid和uid同時存在的情況下,才可以使用上下文理解功能。
/// 非必填
/// </summary>
public string uid { get; set; }
................
}
接著我們分析語義理解的接口返回值,它們基本上都是很有規律的內容,如下所示。

這樣我們也就可以定義一個通用的類庫對象,用來存儲不同的返回內容了,如下代碼所示。
/// <summary>
/// 微信語義結果
/// </summary>
public class SemanticResultJson<T> : ErrorJsonResult
{
/// <summary>
/// 用戶的輸入字符串
/// </summary>
public string query { get; set; }
/// <summary>
/// 服務的全局類別id
/// </summary>
public string type { get; set; }
/// <summary>
/// 語義理解後的結構化標識,各服務不同
/// </summary>
public T semantic { get; set; }
}
而其中的T semantic就是另外一個結構體裡面的內容,這個結構體總體也是固定的內容,我們繼續定義一個如下的類。
/// <summary>
/// 詳細信息裡面的對象
/// </summary>
/// <typeparam name="T"></typeparam>
public class SemanticDetail<T>
{
/// <summary>
/// 詳細信息
/// </summary>
public T details { get; set; }
/// <summary>
/// 查詢類型
/// </summary>
public string intent { get; set; }
}
有了這些類庫的支持,我們可以封裝語義理解接口的返回值了,這樣它的接口定義和封裝處理代碼如下所示。
/// <summary>
/// 語意理解接口
/// 微信開放平台語義理解接口調用(http請求)簡單方便,用戶無需掌握語義理解及相關技術,只需根據自己的產品特點,選擇相應的服務即可搭建一套智能語義服務。
/// </summary>
public class SemanticApi : ISemanticApi
{
/// <summary>
/// 發送語義理解請求
/// </summary>
/// <param name="accessToken">調用接口憑證</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);
}
由於微信語義結果是針對不同的服務協議,我們需要根據這些不同的服務協議,來定義屬於這些信息結構,如在文檔裡,我們可以看到有很多不同類型的服務協議。

根據文檔的詳細字段說明,我們可以定義不同服務的對應類庫。
例如對於旅游服務的語義理解,它們的協議類如下所示。
/// <summary>
/// 旅游服務(travel)
/// </summary>
public class Semantic_Details_Travel
{
/// <summary>
/// 旅游目的地
/// </summary>
public Semantic_Location location { get; set; }
/// <summary>
/// 景點名稱
/// </summary>
public string spot { get; set; }
/// <summary>
/// 旅游日期
/// </summary>
public Semantic_SingleDateTime datetime { get; set; }
/// <summary>
/// 旅游類型詞
/// </summary>
public string tag { get; set; }
/// <summary>
/// 0默認,1自由行,2跟團游
/// </summary>
public int category { get; set; }
}
那麼調用的旅游語義的案例代碼如下所示
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());
如果我們測試,上面的代碼跑起來會返回一個旅游的協議對象,包括了相關的數據信息。
{
"errcode" : 0,
"query" : "故宮門票多少錢",
"semantic" : {
"details" : {
"answer" : "",
"context_info" : {},
"hit_str" : "故宮 門票 多少 錢 ",
"spot" : "故宮"
},
"intent" : "PRICE"
},
"type" : "travel"
}
我們再來看一個例子,例如對於航班服務,我們定義它的語義理解協議如下所示。
/// <summary>
/// 航班服務(flight)
/// </summary>
public class Semantic_Details_Flight
{
/// <summary>
/// 航班號
/// </summary>
public string flight_no { get; set; }
/// <summary>
/// 出發地
/// </summary>
public Semantic_Location start_loc { get; set; }
/// <summary>
/// 目的地
/// </summary>
public Semantic_Location end_loc { get; set; }
/// <summary>
/// 出發日期
/// </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>
/// 座位級別(默認無限制):ECONOMY(經濟艙)BIZ(商務艙)FIRST(頭等艙)
/// </summary>
public string seat { get; set; }
/// <summary>
/// 排序類型:0排序無要求(默認),1價格升序,2價格降序,3時間升序,4時間降序
/// </summary>
public int sort { get; set; }
}
那麼調用獲取語義理解內容的代碼如下所示。
json = new SemanticQueryJson
{
appid = appId,
uid = openId,
category = SemanticCategory.flight.ToString(),
query = "查一下明天從廣州到上海的南航機票",
city = "廣州"
};
var flight = api.SearchSemantic<Semantic_Details_Flight>(token, json);
Console.WriteLine(flight.ToJson());
我們可以獲取到的JSON數據如下所示
{
"errcode" : 0,
"query" : "查一下明天從廣州到上海的南航機票",
"semantic" : {
"details" : {
"airline" : "中國南方航空公司",
"answer" : "已幫您預定2016-04-13,從廣州市出發,前往上海市的航班。",
"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" : "查 一下 明天 從 廣州 到 上海 南航 機票 ",
"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"
}
這樣就是我們把我們常規的語義,分析成了機器可以識別的准確的數據結構了,我們可以根據不同的語義場合對它進行分析,然後給用戶進行不同的響應就可以了,結合微信語音識別為文本內容,我們可以把它做得很強大,有的類似機器智能的味道了。
微信語義理解是一個好東西,不過在微信官網上沒有看到進一步的案例,如果能夠有一些與實際結合的例子,估計更能幫助我們理解和應用了。