根据OLAMI平台开发的日历Demo

来源:互联网 发布:学车电销数据 编辑:程序博客网 时间:2024/06/07 09:04

前言

在自然语言处理中,语义理解一直是个难题。
最近发现OLAMI语义平台提供了自然语言语义理解 API ,而且支持自定义语法,所以决定写一个日历的Demo来看一下效果。

生成语法

想要使用自然语言语义理解 API,首先我们需要提前创建自己的帐号、应用以及编写自己的语法。

创建应用

创建帐号的过程,在这里就不详述了,OLAMI的官网主页上就有注册入口。
创建完帐号之后,登录成功就可以看到如下界面:
创建新应用
点击创建新应用,跳转到如下界面:
创建新应用
在上面的界面分别填写上应用名称、应用类型和应用描述,点击提交就可以创建成功了。
经过测试,除了应用类型只能选择固定的内容,其他两项可以按照自己喜好填写任意字段,在这里,我主要是想开发一个日历程序,所以就以“日期时间”来命名。

编写语法

在登录成功之后看到的页面上,除了创建新应用,还有一个“进入NLI系统”的按钮。
进入NLI系统
点击之后,就可以进入NLI 自然语言语义互动系统编写语法了。
OLAMI平台对“NLI 自然语言语义互动系统”和“OSL 语法描述语言”都有详细的文档说明。我在这里就只简单的描述一下。
首先,我们可以点击新增,创建一个模块,用来写某一类相关联的语法。例如:可以创建“music”模块用来写音乐相关的语法、“weather”模块用来写天气相关的语法。这样的好处是,对于前面创建的不同应用,可以导入不同的模块语法。(应用导入模块的内容会在后面单独列出来)
我在这里就可以新建一个“date”模块。点击进入模块就可以开始编写语法了。
进入模块
当然,这里有一个更方便的方法——就是直接导入内置模块的语法。前提条件就是,语法平台提供的内置模块刚好和你需要开发的模块相一致或者相近。
刚好,内置模块里面提供了“date”,可以直接选择导入。
导入模块
到了这里,还不能算完成了,因为这时候的语法还不能使用,还需要在发布页面点击发布。当看到“发布成功”的提示之后,编写语法的工作才算完成了。

配置模块

在我的应用里面,可以对每一个创建的应用分别配置模块。
配置模块
我这边只有date模块,勾选上即可。
前面提到的分模块写语法的好处就体现在这里。如果有创建了多个应用,同时又多个模块的语法,就可以根据对应的应用需求去选择对应的语法,而不需要为每一个应用去单独写语法了。

日历Demo

完成了语法的工作之后,就可以开始来应用程序的开发了。

语义理解请求

OLAMI提供了几种不同的API接口,我们这里主要使用“语义理解请求”,接口在“OLAMI 文档中心”都有详细的描述。
在调用接口的时候,需要注意两个参数——appkey和sign。
API 请求参数中的 sign 为一个字符串,其值必须是由以下规则所组合成的字符串所生成的 MD5 值。
your_app_secret + api=api_parameter + appkey=your_app_key + timestamp=current_timestamp + your_app_secret
其中的your_app_key(同appkey)和your_app_secret是从前面创建的应用中获取的。
如下图所示(找到对应的应用之后,点击查看Key即可):
查看key

获取到了App Key和App Secret就可以调用接口了,以下是接口调用示例:

public static JSONObject process (String input) {    JSONObject NLIresult = new JSONObject();    List<NameValuePair> params = new ArrayList<NameValuePair>();    params.add(new BasicNameValuePair("appkey", Appkey)); //Appkey为上图中的App Key    params.add(new BasicNameValuePair("api", nli)); // 值必须是 ‘nli’    long timestamp = Calendar.getInstance().getTimeInMillis();    params.add(new BasicNameValuePair("timestamp", String.valueOf(timestamp)));    params.add(new BasicNameValuePair("sign", generateSign(timestamp))); // generateSign方法根据规则生成sign    JSONObject request = new JSONObject();    JSONObject data = new JSONObject();    try {        data.put("input_type", 1); // input_type具体含义可参照文档,由于这里不需要语音输入,默认填1即可        data.put("text", input); // input为需要解析的语句;例如“今天是几号”        request.put("data_type", "stt");        request.put("data", data);    } catch (JSONException e1) {        e1.printStackTrace();        return NLIresult;    }    params.add(new BasicNameValuePair("rq", request.toString()));    params.add(new BasicNameValuePair("cusid", cusid)); // cusid终端用户识别码;如果是手机应用,上传IMEI即可    CloseableHttpClient httpclient = HttpClients.createDefault();    HttpPost httppost = new HttpPost(url); // 接口url:https://cn.olami.ai/cloudservice/api    try {        httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));        CloseableHttpResponse response = httpclient.execute(httppost);        try {            HttpEntity entity = response.getEntity();            if (entity != null) {                String contnt = EntityUtils.toString(entity);                System.out.println("Response content: " + contnt);                NLIresult = new JSONObject(contnt);            }        } finally {            response.close();        }    } catch (Exception e) {        e.printStackTrace();        return NLIresult;    } finally {        try {            httpclient.close();        } catch (Exception e) {            e.printStackTrace();        }    }    return NLIresult;}

语义处理

如果输入的语句能够匹配上对应的语法的话,在调用接口之后,就能够根据对应语法的内容返回语义结构了。
语义结果的详细描述请参考NLI 输出结果。由于我们的模块主要是语义输出,所以主要关心“semantic”字段里面的内容就可以了。
以下是解析Semantic的示例代码:

private static Semantic parseSemantic(JSONObject semantic) {    Semantic s = new Semantic();    if (semantic == null || !"ok".equalsIgnoreCase(semantic.optString("status", "")))         return s;    JSONObject data = semantic.optJSONObject("data");    JSONArray s_list = data.optJSONArray("nli");    JSONObject s_first = s_list.optJSONObject(0);    JSONObject desc_obj = s_first.optJSONObject("desc_obj");    s.status = desc_obj.optInt("status", -1);    if (0 != s.status) {        s.answer = desc_obj.optString("result", "some error occured");        return s;    }    JSONObject intention = s_first.optJSONArray("semantic").optJSONObject(0);    JSONArray modifier = intention.optJSONArray("modifier");    for (int i = 0; i != modifier.length(); i++) {        s.modifiers.add(modifier.optString(i, ""));    }    JSONArray slots = intention.optJSONArray("slots");    for (int i = 0; i != slots.length(); i++) {        JSONObject record = slots.optJSONObject(i);        Slot slot = new Slot();        slot.name = record.optString("name", "");        slot.value = record.optString("value", "");        slot.datetime = record.optJSONObject("datetime");        s.slots.put(slot.name, slot);    }    return s;}class Semantic {    String app = "";    String answer = "";    Map<String, Slot> slots = new HashMap();    List<String> modifiers = new ArrayList<>();    // status -1表示未处理的错误;0表示正常输出;其他数值对应不同的系统错误。    int status = -1;}class Slot {    String name = "";    String value = "";    JSONObject datetime = new JSONObject();}

根据语义生成最终结果

当获取到了modifiers和slots之后,就可以根据不同modifier所对应的含义和slots的值去获取最终结果了。这个处理过程完全是由自己决定的,输出结果也是任意的。
例如:
输入“今天是几号”,通过调用语义理解的接口之后,在返回的Semantic中包含modifier(query_gregorian)和slot(今天)。
query_gregorian表示的是用户输入的语句意图为查询公历日期。
slot表示用户指定查询的时间为“今天”。
针对上面这个语义,我们当然可以直接回复“14号”,也可以回复“今天是6月14号”,甚至可以返回结果“我不想告诉你今天是几号”。
由于Demo程序处理的语义类别比较多,这里我就不给出具体的示例代码了。大家可以直接去github上查看或者下载源码。里面实现了包括农历计算、时区转换、节假日计算等各种功能。

最终效果展示

为了方便展示,我做了一个简单的展示界面。
界面展示
可以将需要处理的语句填入“文本输入框”,点击send按钮。
NLI 输出结果会展示在“语义输出框”中。
最终处理结果会展示在“结果输出”中。

原创粉丝点击