1. 概念介绍
首先引出Rasa的设计理念: Learning from real conversations is more important than designing hypothetical ones,我觉得这是非常重要的事情,我们在构建自己的bot的时候 ,我们往往想绞尽脑汁来排列组合出各种意图的story,但是在实际应用中,我们发现不管我们的story库有多丰富,还是会有大量的unhappy path无法处理,所以我们应该想方设法从真实的对话中来抽象sotry。
1.1 Stories
sotry就是Rasa用来训练对话管理模型的数据集。格式如下:*开头的表示某一轮用户输入的intent和slot信息。-开头的表示分配给bot对应的action。
## greet + location/price + cuisine + num people <!-- name of the story - just for debugging -->
* greet
- action_ask_howcanhelp
* inform{"location": "rome", "price": "cheap"} <!-- user utterance, in format intent{entities} -->
- action_on_it
- action_ask_cuisine
* inform{"cuisine": "spanish"}
- action_ask_numpeople <!-- action that the bot should execute -->
* inform{"people": "six"}
- action_ack_dosearch
1.2 Domains
定义了整个系统需要操作的所有元素,包括:intent,slot,entity,template,actions。
1.3 Actions
定了了bot具体执行动作。在Rasa中定了四种action,包括:
- Utterance actions,直接返回template中定义的话术。
- Retrieval actions,闲聊(small talk)和FAQ(small questions)的方式。
- Custom actions,用户自定义。用户自定义的action,需要发布action server,然后通过HTTP请求来获取执行action的答案。
- Defailt actions,比如:action_listen, action_restart, action_default_fallback等
1.4 Policies
所谓policy就是bot在对话中的每一步决定要执行哪一个action。我们可以在config.yml中配置多个policies,Agent会根据policy的执行优先级来选择最终的action。 Max_history ,bot再决定action时会向前考虑的对话轮数。 Data Augmentation, 在训练模型是,Rasa会从stories.md中随机取出sotry拼接一起看作一个story,用来做数据扩充。
agent决定action的一般原则是选择这些policy给的得分最大的action,但是当两个policy给出了相同的最高得分,这时候就需要根据policy的priority来下决定采取哪一个policy的结果。在Rasa中推荐的设置是:
5. FormPolicy
4. FallbackPolicy and TwoStageFallbackPolicy
3. MemoizationPolicy and AugmentedMemoizationPolicy
2. MappingPolicy
1. EmbeddingPolicy, KerasPolicy, and SklearnPolicy
1.4.1 Embedding Policy (Transformer Embedding Dialogue Policy)
1.5 Forms
现看一个使用Form实现的对话情形: 所谓form就是slot filling,是我们在实现任务型对话的一般思路,及:首先定义一些必须槽位,每个槽位会构建一个反问话术,然后bot会顺序反问用户,收集信息,直到填满所有槽位。FormAction就是来实现这个功能。
名词解释:happy path,就是指你问用户什么信息,用户就配个回答给bot这个信息。
比如下面这个happy path:
## happy path
* greet
- utter_greet
* request_restaurant
- restaurant_form
- form{"name": "restaurant_form"}
- form{"name": null}
- utter_slots_values
* thankyou
- utter_noworries
当用户意图是request_restaurant,bot会接着执行restaurant_form action。form{"name": "restaurant_form"}会激活这个form,form{"name": null}会关闭这个form。对于unhappy path的情形,bot可以执行任意其他的action,只要这个form还是active的。
我们可以使用Rasa提供的sdk来定制自己的FormAction,需要实现下面三个方法:
- name(),aciton的名字
- required_slots(),列出来所有必须的槽位
- submit()方法,当所有槽位被填充,此action产生的结果或者执行的动作。
一旦form action被激活,FormPolicy会对应的被激活,FormPolicy非常简单,它预测的一下个动作永远还是form action。
定制化slot mapping 有时候我们填充槽位不一定只是用抽取到的slot value,也可以是多种形式的回答。比如当bot问:"您是要坐在外面吗?",用户可能的回答有:“是的”/"不是"/“我更喜欢坐在里面”。这几个回答都是不同的意图或者是相同实体中的其他value,但却都是正确的回答,所以,我们要将这些答案mapping到这一轮的槽位上。 FormAction可以支持将yes/no以或者自由文本映射到slot中,只需要实现slot_mappings()方法。
def slot_mappings(self):
# type: () -> Dict[Text: Union[Dict, List[Dict]]]
return { "outdoor_seating": [self.from_entity(entity="seating"),
self.from_intent(intent='affirm', value=True),
self.from_intent(intent='deny', value=False)]}
如上所示,我们将所有mapping策略函数封装在list中,赋值给目的槽位"outdoor_seating"。详细来讲:
- from_entity,将抽取出来的"seating"(seating表示实体)的value填充到outdoor_seating
- from_intent,如果识别的意图是affirm,True填充到outdoor_seating,反之,False填充到outdoor_seating。 还有其他的复制策略函数,from_trigger_intent,from_text。
slot value validation 有时候我们需要对用户输入的slot value进行个性化的确认,我们可以在FormAction中使用 validate_{slot-name} 方法来实现。当然,我们也可以在validate的过程中来终止form,即放validate方法返回self.deactivate(),或者重新反问。
处理unhappy path 在FormAction执行过程中,如果用户不配合,比如:问其他问题、闲聊或者改变了主意,这时候form会引起ActionExecutionRejection,所以,你需要采取措施不让这种情形产生。比如, 处理闲聊情形的story如下:
## chitchat
* request_restaurant
- restaurant_form
- form{"name": "restaurant_form"}
* chitchat
- utter_chitchat
- restaurant_form
- form{"name": null}
当用户改变注意,不在询问最初的需求,这时候bot就不能继续询问最初form的槽位。便可以使用action_deactivate_form来关闭此form:
## chitchat
* request_restaurant
- restaurant_form
- form{"name": "restaurant_form"}
* stop
- utter_ask_continue
* deny
- action_deactivate_form
- form{"name": null}
处理待条件的slot的逻辑 这个功能是为了让对话更加灵活和有个性化,比如某个人回答要吃希腊菜,bot可以反问他是不是需要露天的座位。可以在FormAction中通过required_slots()来实现:
@staticmethod
def required_slots(tracker) -> List[Text]:
"""A list of required slots that the form has to fill"""
if tracker.get_slot('cuisine') == 'greek':
return ["cuisine", "num_people", "outdoor_seating",
"preferences", "feedback"]
else:
return ["cuisine", "num_people",
"preferences", "feedback"]
1.6 检索动作(retrieval action)
这个是Rasa在实验的新功能,主要作用是为了在解决闲聊(small talk)和Faq(simple question)问题时,可以简化构建story,因为这些都是单轮的对话,可以尝试通过一个aciton解决掉。
比如,我们可以不在使用下面这些story:
## weather
* ask_weather
- utter_ask_weather
## introduction
* ask_name
- utter_introduce_myself
...
而是用一个chitchat
意图来将上面所有意图包装起来:
## chitchat
* chitchat
- respond_chitchat
然后retrival action使用NLU组件中的response selector模型来学习和预测结果。
训练数据
准备NLU训练数据:
## intent: chitchat/ask_name
- what's your name
- who are you?
- what are you called?
## intent: chitchat/ask_weather
- how's weather?
- is it sunny where you are?
首先所有上面的sample将被放在一起用来训练意图识别模型用来预测chitchat类别。然后通过一个'/'符号来分隔出来的后缀就是对应的答案,将作为训练模型的label,这其实就是一个问题-答案的qq模型。
然后,在另外一个response.md
文件中准备response语料:
## ask name
* chitchat/ask_name
- my name is Sara, Rasa's documentation bot!
## ask weather
* chitchat/ask_weather
- it's always sunny where I live
1.7 interactive actions
1.8 Fallback Action
当NLU得分低于某个阈值或者policy预测低于某个阈值,需要进行对话回退,也就是尝试让用户重新表达。有下面两种用法:
- 使用
FallbackPolicy
:
policies:
- name: "FallbackPolicy"
nlu_threshold: 0.4
core_threshold: 0.3
fallback_action_name: "action_default_fallback"
action_default_fallback
这个action会对应执行模板中定义的utter_default
来反问客户。
- 使用
TwoStageFallbackPolicy
:
policies:
- name: TwoStageFallbackPolicy
nlu_threshold: 0.3
core_threshold: 0.3
fallback_core_action_name: "action_default_fallback"
fallback_nlu_action_name: "action_default_fallback"
deny_suggestion_intent_name: "out_of_scope"
当用户输入低于nlu阈值,TwoStageFallbackPolicy
会采用多个阶段来处理:
- 如果用户输入低于nlu阈值,bot执行
action_default_ask_affirmation
让用户确认输入: 如果用户说yes,则把输入看作大于阈值的输入, sotry继续。如果用户否认,让用户重新输入。 - 执行
action_default_ask_rephrase
让用户重新输入:如果用户用户重新输入的句子大于阈值,则sotry继续。如果用户输入再一次低于阈值,再次让用户确定意图。 - 执行
action_default_ask_affirmation
让用户二次确认:如果用户确认,则story继续。如果用户否认,用户输入会被认定为'deny_suggestion_intent_name',就会执行最终的召回动作fallback_nlu_action_name
(比如转人工)。
2. 代码结构分析和源码学习
-
DialogueStateTracker 保存对话状态,包括: