这些是否困扰你? 当你想到智能客服问答的时候,你是否第一反应它一定要与人工智能有关? 你是否只是想要一个开箱即用,简答上手,无需复杂的人工智能知识和机器学习规则的智能客服系统? 你是否想让你的智能问答系统不单单满足于问答场景,还希望它可以执行更多的动作? 那么,从今天开始,我们开发的 Segnavi 框架将满足以上所说的这些全部内容,它并不需要你了解人工智能、机器学习方面的知识,就像写脚本一样快速轻松的构建属于你的智能问答系统。
当你不再重复性的为各业务的同学解答相同问题,不再被重复的问题所打扰,一杯咖啡,静下心来,去专心思考和研究更深入的问题时,不得不说这的确是最理想的工作方式了。
从今天开始,只需一杯咖啡的时间,即可立刻构造属于你的智能问答系统。Segnavi 本身的体积十分小巧,无需依赖任何数据库即可运行。基于 python 开发,就像些脚本一样的轻松使用。我们将知识库从常见的数据库存储,转换为本地代码文件存储,所以严格意义上来讲 Segnavi 是一个机器人对话的框架。我们将框架尽可能设计的足够简单、轻量、灵活且功能强大。
我们将传统机器人对话中的意图理解和槽概念转换为 Python 在面向对象状态下的两种可继承类,通过这个继承类生成的对象可以取得全部的对话与槽特性。简化在任务型对话中的槽类型、槽字段等相关概念,取而代之是对象内的属性和方法。你可以像写脚本一样简单快速的使用到框架的全部功能。
在进一步的介绍之前,先简单了解一下任务型对话中槽的概念:在任务型对话中,我们的要完成某一个动作,需要各种的条件满足一个任务的触发,这种对话就叫任务型对话,触发的条件就是槽。当然你可以把槽理解为传递触发条件的参数,我们所做的就是简化机器人中的逻辑。
通俗的讲,类似于 http 请求中传递的参数。这种参数是通过多轮问答的形式来实现的。
下面我将用“40 行代码”来举例说明。
我们设计了两种任务型对话,以满足不同场景的多轮对话需求,一种为 Question 类,一种为 Operation 类。为了简化结构和方便理解,我们将两种不同的任务型对话转换成树形结构,我们管这棵树叫“问题树“。
Question 的每一个问题就相当于树的每一个分支,树的子节点可以继续是 Question,也可以是Operation类。而 Operation 类是负责任务型对话中槽位的填充,其为树的最终子节点。可以理解成红黑树中的黑树,且不再有任何子节点。
除了为了满足不同多轮对话场景需求而设计的 Question 类和 Operation 类,我们还设计了一个 Word 类,改类的目的为注册不同的词或语句绑定在一个 Question 或 Operation 的子类中。
举个例子
【代码】
QuestionTree1 类为 Question 类,它是作为问题树的结构体,方便用户做出进一步的选择,也是一类在多轮对话中槽位的填充。这其中 Word.registe()为注册的词,我们可以注册两个词"hello"和"WelcomeList"等任意个词,这其中 WelcomeList 为引导词,你也可以在配置文件里面随意设置你的引导词,当用户达到配置中连续未任何命中的时候,就会执行引导词。
Question.register() 用于注册任务型对话中负责询问的部分,在 Segnavi 展示中,为问题树的选项。为了方便使用,Segnavi 会将 Question.register()注册的内容,通过1,2,3,4…这类数字作为选项引导。对于超过 2 层的 Question(Question 的子节点还是 Question)Segnavi 会自动生成 “返回上一级” 的选项。q3 中的 handle='我是问题树1’代表当用户选择 3 这个选项,实际 Segnavi 会内部执行 “我是问题树1” 这个词。在 Segnavi 中,默认状态下,用户选择了哪个选项就相当于对系统说了哪个选项的 text 文本,当然这个也是可以改的。就像 q3 这样。
OperationHandle1、OperationHandle2分别代表两个最终子节点,Operation.register() 相当于进入该对话时槽位的填充,这里的槽位我们可以限制指定词的类型,也可以不限制词的类型,词的类型我们在稍后会介绍。当槽位被填满(满足所有条件时),Segnavi 会触发 Operation 子类中定义的 _ handle _ 方法 ,这个方法内你可以执行任何代码,或者只是返回一句简单的 hello world。
【运行后的结果】
超强的纠错能力
在传统的规则匹配型语义理解模块中,最难维护的是你词规则是否是唯一,且规则中是否存在互斥,死锁,循环等问题。在 Segnavi 中这些作为使用者无需考虑。因为我们在系统启动时会有全面的自检程序,防止此类情况的发生。当 Question.register() 中注册的 text 文本是一个不可被理解的词(其他类未注册的词),或者会被理解成两个不同的选项时(多个类同时注册一个词,或一个词指向了两个不同的类),系统此时是无法启动的。
比如我们将 q2 的 text 文本改成一个不存在的词时:
系统启动时的自检程序将会报错:
将机器对话的复杂度降低至小学水平的知识
在传统机器对话模型中,会通常设置自然语言处理和语义处理模块,来处理用户的提问,并起到语义理解,与槽位的识别功能。通常情况下,自然语言处理和语义模块是在一起的。
Segnavi 也有自己的语义理解模块,和自然语言处理系统。但我们为了简化逻辑,将自然语言处理和语义理解模块拆开。对话过程只保留语义理解中的分词功能。将自然语言处理的逻辑由用户输入时的触发变为逆向的先行触发。
我们会在系统启动时,会预先对所有 Word.register(),Question.register(),Operation.register() 内所有的text文本进行自然语言处理,和切词操作。并构造一个大型词典的结构体,这个结构体内包含了词的text文本、自然语言处理后的切词、词的类型、词的动作、词的权重等等内部的参数。
当一个词未被理解时,我们会根据在系统启动时构造的 NLP 词典进行反查,达到联想和猜测的功能。然而这些操作都是作为使用者无需关心的,你只需维护一个简单的 text 知识库即可。
上面我们刚刚提到了在 Operation.register() 内可以设置槽位的类型为指定词的类型,我们采用了基于规则理解方法来解决槽提取以及槽填充的问题。我们将 程序主目录/handle 作为所有词和知识库的存储。handle 目录下的 一级目录名 为词的类型,词的类型类似我们上小学时候学的动词、名词这种,它起到对词进行归类并打标识的作用。就好比我们说跑、跳、走这类词是动词,文具、铅笔、橡皮这类词是名词一样。在 Segnavi 中,槽填充和槽提取也会依赖对词类型的检测。
例如 num 目录下的所有文件,注册的词,它们的类型都是 num 类型。ip 目录下注册的所有词类型都是 ip。这个我们已经预先设置好,可以不用做任何的修改,开箱即用。
我们预先设置好的词:
Segnavi 可以应用在任何系统中,我们曾经接入过 Qtalk、微信、QQ,可以说只要是 IM 系统都可以接入 Segnavi 作为处理引擎调用方式仅为 http 的 post 请求而已。我们会在内部维护 session,无需调用方维护。
同时我们还会提供一个 webui 供测试使用。后台日志和分析功能,可以还原当时的会话场景,并按照指定日期进行词频、动作、理解程度等维度进行综合分析。还支持通过关键词在所有会话内进行查找,并支持导出至 excel。
经过我们测试,每 10,000 个 session,平均占用 100MB 内存。我们在高峰时可以同时处理超过 500 个对话,并且只用一台主机即可。因为是非 cpu 密集型程序,即使是 500 个对话,cpu 的使用水平依旧可以保持一个很低的水平。
目前 Segnavi 已经在公司内部开源使用。现在 OPS 值班热线、CM 热线、JIRA 热线都已接入。我们在设计 Segnavi 之初,跟大家一样也曾尝试过别人的机器人框架,当初想法很简单,只是想做一个运维相关的机器人而已,我们的需求也很简单同时能满足有多轮问答的场景,和满足直接使用命令行操作,并且当用户不知道选项时最好能提供一个菜单。但调研了一圈框架后发现,要不就是框架过于复杂,要不就是功能不完全。最后不得已只能自己造轮子,其实早在 18 年 11 月的时 OPS 内部就已经将机器人切换至 Segnavi 框架了。从目前的状态来看 Segnavi 运行的还是比较稳定。
由于篇幅有限,其实在文章中只能介绍 Segnavi 框架的最基础功能,我们还支持例如转人工、层级索引、模糊猜测、命令式的直接槽位提取等等。我们没有为 Segnavi 配置机器学习的模块,但已经预留出插件接口。其实作为运维工程师来讲,目前的 Segnavi,已经能够满足 OPS 的全部需求了。接下来如果有精力,我们期望能将 Segnavi 的对话和 session 改造成中间件式的可插拔插槽。
我们始终期望创造一个开箱即用,简单逻辑的机器人应用,减轻学习成本,方便快速使用。最后,如果你希望使用 Segnavi 欢迎与我取得联系。
作者介绍:
王欣宇,2018年加入去哪儿 OPS 团队,负责运维自动化相关的工作,保障了公司每日数十万的自动化任务执行,让海量服务器共舞。
本文转载自公众号Qunar技术沙龙(ID:QunarTL)。
领取专属 10元无门槛券
私享最新 技术干货