本文继续围绕工业级业务对话平台和框架Rasa 的Knowledge Base案例解析、工作机制及自定义方式进行解析。Rasa提供的Knowledge Base框架可以实现对象列表和属性之类的查询,你也可以基于这个框架来二次开发以满足你自己的业务需求。
一、Rasa Knowledge Base案例解析、工作机制及自定义详解
- ActionQueryKnowledgeBase分析及案例解析
ActionQueryKnowledgeBase是用来从知识库(Knowledge Base)中查询给定的一个对象所包含的对象列表和属性列表。为了开发自定义的知识库查询action,需要继承ActionQueryKnowledgeBase,并设置需要查询的知识库信息。
下面是一个继承了ActionQueryKnowledgeBase的自定义action样例,从json文件存储的知识库数据中查询数据并调用lambda函数进行相应的处理:
下面是Rasa SDK文档提供的一个关于演示在对话中如何使用知识库查询的样例:
结合这个例子来看,在对话机器人和用户的对话过程中,如上面所示当对话机器人给出了通过知识库查询获得的餐馆列表信息并发送给用户后,用户会用”the first one”来指代列表中的第一个餐馆而不是使用名称来提问,这就是在对话AI里的一个常见的问题。所以需要跟踪已有的信息来解析用户消息中的指代(mentions)并映射到一个正确的对象。
另外如果用户想获得更多的关于对话中一个对象的信息,就需要相应业务领域的知识库,如上面例子里提到的关于餐馆方面的知识,所以需要进行知识库的查询。在Rasa提供的知识库查询框架中,你可以创建一个自定义action来继承这个框架类ActionQueryKnowledgeBase,因为这个类里包含预定义的知识库查询的逻辑。
使用ActionQueryKnowledgeBase的具体步骤如下:
-创建一个知识库
用来回答用户问题的数据会存放在各种形式的知识库中,知识库可以存储复杂的数据结构。首先你可以从简单的InMemoryKnowledgeBase开始,如果需要处理大量数据时,可以使用自定义的知识库。为了初始化一个InMemoryKnowledgeBase,需要提供一个json格式的数据文件。如下面的样例所示,在数据结构里可以看到每个object type的key,如"restaurant","hotel",在每个object type下面都会包含一个object list:
然后使用这个数据文件创建一个InMemoryKnowledgeBase类型的知识库,样例如下:
按照Rasa规范,为了使用InMemoryKnowledgeBase的默认实现,在知识库中的每个object至少需要包含字段name和id,否则你只能定制自己的InMemoryKnowledgeBase。
-为查询请求对象创建NLU训练数据
这里使用了一个新的intent:query_knowledge_base来让对话机器人理解用户想从知识库中查询的信息。我们可以把ActionQueryKnowledgeBase能够处理的用户请求分为两类:
-用户想获取一个特定object type的object list
-用户想知道一个特定object的一个具体的属性
Intent “query_knowledge_base”应该包含很多上面这些请求的情况。如以下样例所示:
在这个样例里,你需要为知识库中存在的每个object type添加样例数据。为了针对每个查询的object type添加各种训练数据,你需要在数据里指定和标注下列entities:
-object_type:当一个训练样例引用了知识库中的一个object type,这个object type就应该被标记为一个entity。你可以使用同义词来map知识库中的key,即object type,例如map restaurants为restaurant。
-mention:如果用户使用指代来引用一个object,如”the first one”,”that one”或者”it”,你应该把这些词组或词汇标记为mention。你可以使用同义词来把一些mentions map为符号。
-attribute:在知识库中被定义的所有属性名应该在NLU训练数据中被标识为attribute。同样可以使用同义词来map NLU数据里的attribute为知识库中使用的名称。
-需要添加这些entities和slots到domain文件里:
-创建一个action来查询知识库:
你的自定义action需要继承自ActionQueryKnowledgeBase,然后把知识库传入到ActionQueryKnowledgeBase的构造方法里:
这个知识库可以是InMemoryKnowledgeBase或者你自己实现的知识库。你只能使用一个知识库,目前还不支持使用多个知识库。你的action的名称是action_query_knowledge_base,需要添加到domain里:
注:如果你不用上面提到的action name,那么需要手动添加下列slots到你的domain文件里,因为这些slots需要被ActionQueryKnowledgeBase所使用:
-knowledge_base_objects
-knowledge_base_last_object
-knowledge_base_last_object_type
你需要添加一个story到你的stories文件里,这个story包含intent “query_knowledge_base”和action “action_query_knowledge_base”,样例如下:
最后需要在domain文件里定义一个response “utter_ask_rephrase”,如果action不知道如何处理用户请求,这个action就会要求用户澄清他们的意图:
完成以上步骤后,你的自定义action就可以用来查询知识库了。
2. Knowledge Base Actions工作机制解密
知识库查询机制:
ActionQueryKnowledgeBase会根据从当前用户请求中提取的entities和之前对话中设置的slots的值来判断需要查询的内容。
查询知识库中的objects:
为了能查询知识库中的任何object,用户请求需要包括object type。
如用户消息为:Can you please name some restaurants?
这个消息包含了” restaurants”,那么对话机器人需要提取这个entity来构建出一个查询,否则action无法知道用户对什么object感兴趣。
再譬如用户问:What Italian restaurant options in Berlin do I have?
从用户意图看想获取以下信息:
-需要能提供” Italian cuisine”的餐馆
-这个餐馆需要位于Berlin
如果NER能够检测到用户请求中的这些attributes,action就可以用来过滤从知识库中查询到的餐馆列表。为了让对话机器人能够检测到这些attributes,你需要在NLU数据里标记Italian和Berlin为entities:
intents:
- intent: query_knowledge_base
examples: |
- What [Italian](cuisine) [restaurant](object_type) options in [Berlin](city) do I have?.
这里的attributes “cuisine”和”city”应该和知识库中使用的名称一致。你需要把这些作为
entities和slots添加到domain文件里。
查询知识库中的一个object的一个attribute:
如果用户想获取一个object的特定信息,那么用户请求中应该包含这个object和感兴趣的attribute。
譬如用户问:What is the cuisine of Berlin Burrito Company?
表明用户想获取object type为“restaurant”的object “Berlin Burrito Company”的attribute “cuisine”的信息。那么这些attribute和object应该在NLU训练数据里被标记为entities:
intents:
- intent: query_knowledge_base
examples: |
- What is the [cuisine](attribute) of [Berlin Burrito Company](restaurant)?
你需要确保添加object type “restaurant”到domain文件里作为entity和slot。
指代解析(Resolve Mentions):
如果用户不是通过名称来发起请求,譬如:
What is the cuisine of the second restaurant you mentioned?
那么action需要执行以下两种类型的指代解析才能映射到知识库中的实际object:
-按照顺序来解析指代关系,如”the first one”指代object list中的第一个
-使用”it”或者”that one”的解析
当用户通过list的位置来引用一个object的样例如下:
User: What restaurants in Berlin do you know?
Bot: Found the following objects of type 'restaurant': 1: I due forni 2: PastaBar 3: Berlin Burrito Company
User: Does the first one have outside seating?
这种方式通常用于给用户返回的是一个object list,为了解析这种指代关系,在KnowledgeBase里设置了这样一个mapping:
lambda l: l[0]表示index为0的object。你可以使用entity同义词的方式来把NLU数据里的”the first one”映射为”1”。
NER会检测”first one”作为一个mention entity,然后设置mention slot的值为1,然后action可以使用这个slot结合上面的mapping来把”first one”解析为实际对应的一个object。你可以调用set_ordinal_mention_mapping()函数来覆写mentiond mapping。
关于其它类型的指代解析:
如下面这个样例所示:
User: What is the cuisine of PastaBar?
Bot: PastaBar has an Italian cuisine.
User: Does it have wifi?
Bot: Yes.
User: Can you give me an address?
用户使用了it来指代PastaBar,所以NER需要把”it”作为mention entity,这样action才能知道用户想要获取一个具体attribute的值。如果NER检测不到mention或者object,那么action会假定用户正在提到最近使用的object。你可以在初始化action时使用use_last_object_mention为False来禁用这种行为。
3. Knowledge Base Actions自定义详解
关于自定义ActionQueryKnowledgeBase:
你可以覆写以下两个方法来实现自定义逻辑:
utter_objects:当用户请求object list时使用,一旦对话机器人从知识库中查询到结果,会通过一条消息返回给用户,如上面对话样例所示。
utter_attribute_value:这个方法用于当用户请求一个特定attribute时对话机器人要返回什么信息
如果你想处理更复杂的情况,可以写自定义的action,这时可以借助Rasa提供的工具包:rasa_sdk.knowledge_base.utils。建议使用Rasa提供的KnowledgeBase 接口来结合使用ActionQueryKnowledgeBase和你自定义的action。
关于创建你的知识库:
你可以创建自己的知识库来应对复杂的数据结构,以及解析不同objects之间的关系。你只需要继承接口KnowledgeBase和实现下列方法:
- get_objects
- get_object
- get_attributes_of_object
-添加这些必需的slots到domain配置里:'object_type', 'attribute', 'mention'
-创建一个story包含这个action
-在domain文件里添加intent和action