序言
以一周前的一条微博作为开始。一周前我讲:相对的,自然语言解析技术已经逐渐不再成为各家广义智能助理产品的核心竞争力,识别用户意图之后所提供的服务开始成为对话机器人差异化的核心。
对于一个对话系统而言,我微博中所指的『后续服务』,就是上图中的 DST(对话状态维护)以及 Policy(动作候选排序),或者统一的称其为 DM(Dialogue Mannagement,对话管理)。也即,当接收到 NLU 模块的输出、其他场景及用户特征信息之后,判断系统应该跳转到什么状态,以及执行什么样的动作。
从产品的角度,DM 是对话机器人
封闭域多轮对话
体验的核心,正是一次次 DST + Policy 形成了人机间的多轮对话体验。(我个人倾向于将识别用户意图之后,为了获取必要信息,与用户进行的有目的的多轮对话称为
封闭域多轮对话
,区别于识别用户意图之前,为了利用上文信息,所采用的『上下文替换』、『主体补全』等技术,aka
开放域多轮对话
。下文提到的『多轮对话』,均指
封闭域多轮对话
。)
既然多轮对话在对话机器人类产品体验中扮演着如此重要的角色,我便倾向于开始思考:
一个架构完备的多轮对话体系应该是什么样的
。也即多轮对话系统中需要至少包含哪些模块,才能为用户提供一种与人人对话相去不远的人机对话体验。
多轮对话定义
我有个习惯,就是在构造一个复杂系统之前,先从纷繁的细节之中跳出,尝试抽象的描述整个系统,及系统中的各个模块,也即为它们『下定义』。这能帮助你在多种可行方案中做出选择,也即帮你明确:
什么该做,什么不该做,什么该谁做
。
基于以上思想,我尝试先给出几个我个人对于多轮对话体系定义问题的回答:
基本定义:什么是多轮对话?
(封闭域)多轮对话是一种,在人机对话中,初步明确用户意图之后,
获取必要信息
以最终得到
明确用户指令
的方式。多轮对话与
一件事情
的处理相对应。
补充说明:所谓『必要信息』一定要通过与用户的对话获取吗?
不一定,即便是人与人之间的交流,对话本身所包含的信息也只占总传递信息量的一小部分,更多信息来源于说话人的身份、当前的时间/地点等一系列场景信息。所以多轮对话的信息获取方式也不应当只局限于用户所说的话。
补充说明:多轮对话一定在形式上表现为与用户的多次对话交互吗?
不一定,如果用户的话语中已经提供了充足的信息,或者其它来源的补充信息已经足够将用户的初步意图转化为一条明确的用户指令,那就不会存在与用户的多次对话交互。
以上是针对多轮对话整体定义问题的回答,每个模块的相关定义会在下文各个模块的讲解中尝试给出。
槽
基本定义:什么是槽?
槽是多轮对话过程中将
初步用户意图
转化为
明确用户指令
所需要补全的
信息
。一个槽与
一件事情
的处理中所需要获取的
一种信息
相对应。
补充说明:多轮对话中的所有的槽位都需要被填充完整吗?
不一定,以如下对话为例:
我:『去萧山机场多少钱』
出租车司机:『70』
对话中的『70』,应当被理解为70元人民币,而不必再去追问:『你说的是人民币、美元、日元还是港币?』。这类信息应当以默认值的形式存在,也即槽有
必填
与
非必填
之分,与上文所说的『信息未必需要通过与用户的对话获取』相对应。
词槽与接口槽
上文反复的提到,
对话内容并不是获取信息的唯一方式
,用户身份以及当前场景也包含着大量值得被利用的隐含信息。所以,与此相对的,一个完备的多轮对话体系应当同时具备从用户
话里
以及
话外
获取信息的能力。
我个人将利用用户话中关键词填写的槽叫做
词槽
,利用用户画像以及其他场景信息填写的槽叫做
接口槽
。
举个例子,我讲『我明天要坐火车去上海』。其中,分别将『明天』、『上海』填入名为『出发时间』、『目的地』的词槽中,而我当前所在的位置,则填入到了名为『出发地』的接口槽中。
槽组与槽位
我个人将利用用户话中关键词填写的槽叫做
词槽
,利用用户画像以及其他场景信息填写的槽叫做
接口槽
。
举个例子,我讲『我后天要坐火车去上海』。其中,分别将『后天』、『上海』填入名为『出发时间』、『目的地』的词槽中,而我当前所在的位置,则填入到了名为『出发地』的接口槽中。
不知道上文错的如此离谱的结论有没有引起你的注意 :)
仔细读一遍上面举的例子,就会发现一个很严重的矛盾点:难道『出发地』这个槽不能由用户指定?用户完全可以说『我后天要坐火车从北京去上海』,那它是词槽还是接口槽?而且更进一步的,难道只能用『我当前所在的位置』来填入『出发地』这个槽中?比如,如果能读到我的日程表,发现我明天会去杭州,那是不是就应该用『杭州』而不是『我现在所在的位置』来填『出发地』这个槽了?
从中我们能发现什么呢?同一个槽,可能会存在多种
填槽方式
。
我将可能包含多种填槽方式的
槽
称为
槽组
,槽组下面可能存在任意多个
槽位
,也即任意多种填槽方式,而每个槽位又都对应着『词槽』与『接口槽』两种
槽位类型
之一。
本质上来讲,槽组(也即上文中提到的『槽』),对应着
一种信息
,而几乎不会有哪种信息的
获取方式只有一种
。所以一个『槽』会同时对应多种填槽方式也就是自然而然的了。
依照上文,同一种信息会有多种获取方式,也即
同一个槽组会对应多种填槽方式(槽位)
。那不同填槽方式之间必然会存在
优先级
的概念。
就如同上文『订票』的例子,『出发地』槽包含三种填写方式,一种词槽、两种接口槽,自然的,词槽的优先级最高,『日程表中隐含的出发地』次之,『我当前所在的位置』再次。
如果将其与前文提到过的
必填/非必填
结合起来,其填槽过程应当遵循以下步骤:
-
尝试填写词槽
-
若失败,尝试填写第一接口槽『用户日程表中隐含的出发地』
-
若失败,尝试填写第二接口槽『用户当前所在位置』
-
若失败,判断是否该槽必填
-
若必填,反问用户,重填词槽
*若非必填,则针对该槽组的填槽过程结束
我们需要知道,
必填/非必填
在逻辑上与
槽组
而不是
槽位
平级,只有
信息
才会分为
必要/非必要
,填槽方式不做这种区分。而且是否必填实际上与接口槽无关,只取决于是否需要与用户进行交互。
澄清话术
与
槽组
也即与
一种信息
平级的概念还有一个,叫做
澄清话术
。
澄清话术是
对话机器人希望获取某种信息时所使用的问句
。比如『目的地』对应的澄清话术就是『您想从哪出发呢?』,『出发时间』对应的澄清话术就是『您想什么时间出发呢?』。
显而易见的,澄清话术与
槽组
而不是
槽位
平级。
槽的填写
上文讲到,一个
槽组
可能会有多个
槽位
,槽位存在
词槽
与
接口槽
之分。
先说词槽。
词槽信息的抽取其实还是有些麻烦的,不过这属于解析的问题,不在本文探讨的范围内,这里只是简单提一下,举两个例子:
-
用户表达『不』,可能会有『不行』、『不是』、『算了』、『没有』等一系列说法。
-
用户话中有
多个
符合条件的
关键词
,我们整套多轮对话中有
多个槽
,每个槽填一个还是多个值?哪个槽与哪个词对应?
同义词典、规则、双向LSTM+CRF,各有各的方法。
再说接口槽。
接口槽与词槽相比,额外存在一个问题,就是:
接口返回的结果就是用户需要的结果吗?
这里需要分成两种情况来讨论,一种是:
我们明确知道接口的返回值可以直接填入槽位(不是槽/槽组)中,不需要向用户确认
。
特别的,这里还要明确一点,即便是上述情况,也并不意味着当前槽/槽组只有该特定接口槽这一个槽位。有两种情况存在:一种是该槽组下只有这一个槽位,该接口的返回值直接填入槽位中,也相当于填入了槽/槽组中;或者该槽位下有多个槽位,接口槽的填入值并不一定最终作为槽/槽组的填入值。
另一种是:
我们知道接口的返回值只能作为参考,需要用户的协助才能进行槽位的填写
。
这种情况下,需要
提供选项
,让用户最终决定该槽位的填入值,与词槽一样,这里同样需要处理
单值/多值
的问题。
单值/多值
在逻辑上与
槽组
平级。