一种在智能对话中实现上下文功能的方法
来源:互联网 发布:蜻蜓飞行原理知乎 编辑:程序博客网 时间:2024/06/01 10:04
智能对话中有一个经典的场景:
Q:上海的天气A:上海的天气是……Q:那北京的呢A:北京的天气是……
第二个问句是一个特殊的问句,它的语义和前一句关联,但单独说它没有明确的意图。目前的olami开放平台提供的IDS模块(应用管理->配置模块->对话系统模块)自身可支持上下文,但对平台用户自己开发的NLI模块却没有提供直接的支持。不过我们可以通过一些办法实现这种功能。下面就介绍一下解决方案。
分析
这种上下文的语句有两种类型,一种是对前文的语义进行一部分的修改,另一种是补充前文语义缺失的部分。修改前文语义的语句一般都是问更多的信息,所以这里把处理这种语句的grammar称为more grammar;补充前文语义,一般都是补充osl语言描述的“slot”信息,所以处理它的grammar在本文叫做slot grammar。前面的例子就是修改前文语义,下例是补充前文语义的情况:
Q:查天气A:你要查哪里的天气Q:上海A:上海的天气是……
大多数时候more grammar可以很好的解决问题,但有些情况就不适用。比如:
Q:买机票A:请问你要从哪里出发Q:上海 ⑴A:请问你要去哪里Q:北京 ⑵A:上海到北京的机票
⑴和⑵两句的类型完全相同,但slot名称不同,只能用同一句grammar来抓取。如果用more grammar来处理,拿到语义后还要纠正slot的名称,这和一般的more grammar有一定的区别。所以将其设定为特殊的more grammar —— slot grammar,在这当中进行设置slot的操作。
利用前面定好的规则,我们希望实现如下场景:
Q:查天气A:你要查哪里的天气Q:上海A:上海的天气是……Q:那北京的呢?A:北京的天气是……Q:南京的呢A:南京的天气是……
另外,如果有多个应用,不应该出现一个模块的more处理了另一个模块上文的情况:
Q:导航去南京A:到南京的路线……Q:那北京的呢? // 这是天气的moreA:北京的天气是…… // 不应该出现这种情况
最后,两个应用模块共同的more语法要能被正确的模块处理:
Q:查天气A:你要查哪里的天气Q:上海A:上海的天气是……Q:导航A:你要去哪里Q:上海Q:到上海的路线是……
总结起来,我们的目标有:
模块的more可以继承上文的部分语义组合成一个完整的语义;
一个模块的more不应该继承另一个模块的上文;
相同的句式(例如整句是一个地点)能被正确的模块处理。
方案
计划用两个功能模块和一个公共模块来演示上下文的功能。两个功能模块一个是天气,功能是查指定城市的天气;另一个是导航,考虑到简化流程,只接收目的地的设置,出发地理解为当前地点。公共模块用来处理一些两个模块都要支持的more语句,例如“北京”这种。
实现
语法平台的操作
首先在平台上建三个新的模块,分别是weather(天气),navi(导航)和common(公共模块)。
然后,由于三个模块都需要一个地点的slot,所以每个模块都添加上:
高级设置中设置验证类别,之后应用时系统会自动验证抓到的内容是不是这个类型。同时引用类型设置为地点,以后可以用”那里“来引用上文的地点:
接下来依次添加需要的grammar,注意要覆盖上文需要实现场景中的所有情况。普通的grammar和平常一样写,more的grammar将modifier设置为more:
weather
不带location:[我要|帮我|我想]查[[一]下]天气<{@=query}>带location的完整grammar:(<location>|(那里|那儿)<{location@=last}>)的天气[怎[么]样]<{@=query}>more grammar:([那]<location>的[呢|怎么样|如何]|查<location>[的]|<location>(呢|怎么样|如何))<{@=more}>
navi
(帮我|我要)导航<{@=navi}>导航(去|到)(<location>|(那里|那儿)<{location@=last}>)<{@=navi}>([那](到|去)<location>[的]呢|[我要|我想]去<location>)<{@=more}>
common
抓整句作为location slot:(<location>|(那儿|那里)<{location@=last}>)<{@=slot}>
客户端的内容
本文用swt来实现客户端。导入olami java client sdk之后就能使用其提供的nli接口了。由于olami服务器目前只能提供一句话的语义解析,而不能指定上下文,所以客户端需要保存一些状态。这些状态包括:
处理上一回合语义的模块。用于判断本回合的more语义是否要进行处理。
上一回合模块是否处于等待slot输入的状态,以及需要的slot名称。这样同类型不同名称的slot不会被混淆。
上一回合的语义。目的是和本回合的more或slot语义进行合并,得到完整的语义。
和nli接口的交互封装在NliService类中。另外我们定义的每一个应用模块都有一个相应的app类处理其语义。这些类由NliService统一管理。NliService收到服务器返回语义后,会做一些简单的处理,如果是公共模块的语义就交给正确的上文app,其他的语义按照它的app名称进行分配。下面是NliService的代码:
package moredemo;import java.io.IOException;import java.security.NoSuchAlgorithmException;import java.util.HashMap;import ai.olami.cloudService.APIConfiguration;import ai.olami.cloudService.APIResponse;import ai.olami.cloudService.APIResponseData;import ai.olami.cloudService.TextRecognizer;import ai.olami.nli.NLIResult;import ai.olami.nli.Semantic;import app.App;import app.NaviApp;import app.WeatherApp;public class NliService { private static final String appkey = "85d85d62d2b3450c97c2f547c7d8de48"; private static final String appSercert = "57467df93d4c4f65a176ab06cca660ee"; private HashMap<String, App> appService = new HashMap<>(); private static String lastapp = null; private TextRecognizer recognizer = null; public void init() { APIConfiguration config = new APIConfiguration(appkey, appSercert, APIConfiguration.LOCALIZE_OPTION_SIMPLIFIED_CHINESE); recognizer = new TextRecognizer(config); // 初始化app服务,key与nli系统中的app名称对应,方便使用。 appService.put("weather", new WeatherApp()); appService.put("navi", new NaviApp()); } // 通过用户输入得到处理结果 public String process(String input) { String result = null; if (recognizer != null) { try { APIResponse response = recognizer.requestNLI(input); result = handleResponse(response); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } return result; } private String handleResponse(APIResponse response) { if (response.hasData()) { APIResponseData data = response.getData(); if (data.hasNLIResults()) { NLIResult nliResult = data.getNLIResults()[0]; String content = nliResult.getDescObject().getReplyAnswer(); if (nliResult.getDescObject().getStatus() == 0 && nliResult.hasSemantics()) { Semantic sem = nliResult.getSemantics()[0]; String appname = sem.getAppModule(); if (appname.equals("common")) { // 处理公共语义,交给lastapp处理 if (lastapp != null) { App app = appService.get(lastapp); if ("slot".equals(app.status())) { return app.getResult(sem); } else { return "抱歉,你说的我还不懂"; } } } else { App app = appService.get(appname); if (app != null) { String result = app.getResult(sem); lastapp = appname; return result; } else { return "错误:未知服务类型"; } } } else { return content; } } } return "抱歉,你说的我还不懂"; } public static String getLastApp () { return lastapp; }}
模块app中保存了上一回合的语义,以及一个回合结束之后app的状态。通过语义中的特殊标记(“more”或”slot)和普通的modifier,与slot信息一起得到结果。由于没有数据源,所以结果都只用一句话来代替。为了清晰的展示返回结果是由哪个app处理的,代码中在每个回答前加上了【app名称】的前缀。下面是weatherapp的代码:
package app;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import ai.olami.nli.Semantic;import ai.olami.nli.slot.Slot;import moredemo.NliService;public class WeatherApp implements App { private List<String> modifiers = new ArrayList<>(); private List<String> lastmods = new ArrayList<>(); private Map<String, Slot> slots = new HashMap<>(); // 保存当前的app状态,目前有"slot"一种可能,表示拿到的语义缺少slot信息,需要用户继续输入。 // 此时slotgrammar才会生效 private String status = null; private String op = null; @Override public String getResult(Semantic sem) { // 保存前一个回合的modifier,便于处理more和slot的情况 List<String> temp = lastmods; lastmods = modifiers; modifiers = temp; modifiers.clear(); // 设置新的modifier for (String mod : sem.getGlobalModifiers()) { modifiers.add(mod); } // 目前的功能只需要,并且语法中的语句都只有一个modifier,扩展的话需另作处理 if (modifiers.size() == 1) { String modifier = modifiers.get(0); if (modifier.equals("more")) { // 通过nliservice中的lastapp是否是这个app判断是否要解析此more语义 if (!"weather".equals(NliService.getLastApp())) { return "【weather】抱歉,我不明白你的意思"; } else { setSlots(sem.getSlots()); return result(); } } else if (modifier.equals("slot")) { // lastapp是此app且此app处于“slot”状态时才处理此slot语义 if (!"weather".equals(NliService.getLastApp())) { return "【weather】你给我的信息太少了"; } else if (!"slot".equals(status)) { return "【weather】我不明白你跟我说的是什么"; } else { setSlots(sem.getSlots()); return result(); } } else { // app的一般流程,这里表示modifier是“query”的情况 reset(); op = modifier; setSlots(sem.getSlots()); return result(); } } else { return "【weather】我的神经发生了混乱"; } } // 通过保存的op和slots的组合确定当前执行哪种操作,并设置对应的app状态 private String result() { if (op.equals("query")) { if (slots.containsKey("location")) { String location = slots.get("location").getValue(); status = null; return "【显示" + location + "的天气】"; } else { status = "slot"; return "【weather】你要查哪里的天气呢?"; } } else { status = null; return "【weather】我好像出了点问题?"; } } // 把参数slots中的slot信息保存起来 private void setSlots(Slot[] slots) { for (Slot slot : slots) { this.slots.put(slot.getName(), slot); } } private void reset() { slots.clear(); op = null; } @Override public String status() { return status; }}
navi的代码和它大同小异。
运行结果
加上一个简单的界面,就可以测试输出结果。
- 可以进行追问:
- 天气的more grammar不会处理导航的上文:
- 相同的句式不会混淆:
这样前文期望的目标基本都实现了。
总结与展望
通过给语法做标记,再在客户端做一些工作,我们可以实现一些基本的上下文处理。这已经可以满足相当一部分应用对上下文理解的需求。当然由于不是在语法匹配上支持的上下文,这里的办法有一些局限性,当应用数量和复杂度提高时很可能会出现问题。希望olami平台能尽快在服务端增加上下文的功能,这样使用起来就可以更加简便,应用的处理逻辑也可以更加简明和清晰。
附
源码地址:https://gitee.com/stdioh_cn/moredemo.git
- 一种在智能对话中实现上下文功能的方法
- 一种在智能对话中实现上下文功能的方法
- (OK) patent-2 (专利-2) 一种在智能终端设备中实现MPTCP协议的方法及装置
- 在 DB2 9.5 中实现新的安全功能,第 2 部分:理解可信上下文
- 在C++中实现库动态初始化的一种方法
- 在SOC 中实现Nand Flash 控制器的一种方法
- 在iphone程序中实现截屏的一种方法
- 一种在SQLServer中实现Sequence的高效方法
- 一种在BIOS中嵌入应用程序的方法及实现
- 智能指针的一种实现
- 智能指针的一种实现
- 点评了一下学生的简历。其实对话也是一种伟大的传播。 孔子在论语中对话,《理想国》里面的对话。
- 点评了一下学生的简历。其实对话也是一种伟大的传播。 孔子在论语中对话,《理想国》里面的对话。<另一博客搬家>
- 在网页中发起QQ临时对话的方法
- 在PB中实现热键功能的方法
- 关于在PLSQL中实现DEBUG调试功能的方法
- 在struts中分页的一种实现
- 在struts中分页的一种实现
- 通过像素数据生成VisionPro的CogImage8Grey图像
- 欢迎使用CSDN-markdown编辑器
- Android应用开发—重载fragment构造函数导致的lint errors
- Android Binder机制(超级详尽)
- 基于阿里移动端积木框架Tangram自定义首页卡片
- 一种在智能对话中实现上下文功能的方法
- 数组排序,交换法,选择法
- Trie树的C++简单实现
- HDU
- 队列在多线程中使用
- Unix-Linux编程实践教程——第三章
- centos7.3安装Apache
- 阿里巴巴Java开发规约插件全球首发
- 1067. 试密码(20)