Rasa学习笔记2--Rasa Core

584 阅读6分钟

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,包括:

  1. Utterance actions,直接返回template中定义的话术。
  2. Retrieval actions,闲聊(small talk)和FAQ(small questions)的方式。
  3. Custom actions,用户自定义。用户自定义的action,需要发布action server,然后通过HTTP请求来获取执行action的答案。
  4. 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 所谓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"。详细来讲:

  1. from_entity,将抽取出来的"seating"(seating表示实体)的value填充到outdoor_seating
  2. 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 保存对话状态,包括: