C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

微信语义理解接口提供从用户自然语言输入到结构化解析的技术实现,使用先进的自然语言处理技术给开发者提供一站式的语义解析方案。该平台覆盖多个垂直领域的语义场景,部分领域还可以支持取得最终的展示结果。开发者无需掌握语义理解及相关技术,只需根据自己的产品特点,选择相应的服务即可搭建一套智能语义服务。结合语音识别接口,通过微信语音识别得到用户的语音信息之后,经过语义分析理解,得到用户需求,及时回复用户。本文介绍如何实现对微信语义接口的封装处理,以及一些常用场景的调用。

1、微信语义理解接口

这个东西也就是把我们日常的话语(称之为自然语言)解析为对应的信息结构体,方便我们提取里面的相关信息进行搜索查询,并精确回应给对应的请求者的一个桥梁,其主要的功能就是解析我们所说的内容。
微信开放平台语义理解接口调用(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" 
}

参数说明

注:单类别意图比较明确,识别的覆盖率比较大,所以如果只要使用特定某个类别,建议将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” 

}

返回参数说明


上面就是微信官方给出的代码案例,以及一个《语义理解接口协议文档》,里面介绍了各个场景的语义结构信息,虽然这个文档好像好久都没怎么更新,不过总体内容还是稳定的,我们可以通过这个文档进行相关的类库设计工作。

2、语义理解接口的C#实现

根据《语义理解接口协议文档》文档,我们可以定义各种所需的语义结构类库,这些是我们开展语义接口的基础类。
例如我们定义基础的时间协议类,如下所示。

    /// <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"
}

这样就是我们把我们常规的语义,分析成了机器可以识别的准确的数据结构了,我们可以根据不同的语义场合对它进行分析,然后给用户进行不同的响应就可以了,结合微信语音识别为文本内容,我们可以把它做得很强大,有的类似机器智能的味道了。

微信语义理解是一个好东西,不过在微信官网上没有看到进一步的案例,如果能够有一些与实际结合的例子,估计更能帮助我们理解和应用了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容