(十二)RASA Domain
作者简介
作者:孟繁中(北京邮电大学 计算机科学与技术硕士)
原文:https://zhuanlan.zhihu.com/p/334500244
转载者:杨夕
面筋地址:https://github.com/km1994/NLP-Interview-Notes
个人笔记:https://github.com/km1994/nlp_paper_study
                          
Domain(领域)定义了助手可以操控的范围。它指定了机器需要了解的intents,entities,slots,actions,forms。可选的,它还包括机器能够说的responses模板。domain是一个或者多个YAML格式的文件。可以通过
rasa train --domain path_to_domain_directory指定搜索domain文件的路径,默认从机器人所在路径查找。
领域示例
典型Domain如下:
version: "2.0"
intents:
- switch_faq
- nlu_fallback
entities:
- some_slot
slots:
  some_slot:
    type: text
    influence_conversation: false
  detailed_faq:
    type: bool
actions:
- utter_explain_some_slot
- action_stop_q_form
forms:
  loop_q_form:
    some_slot:
    - type: from_entity
      entity: some_slot
session_config:
  session_expiration_time: 60  # value in minutes
  carry_over_slots_to_new_session: true
responses:
  utter_ask_some_slot:
    - text: "utter_ask_some_slot"
  utter_explain_some_slot:
    - text: "utter_explain_some_slot"
  utter_list_possibilities:
    - text: "utter_list_possibilities"Intents
域文件中的intents键列出了NLU数据和会话训练数据中使用的所有意图。并且可以通过use_entities和ignore_entities配置指定意图使用哪些实体或者忽略哪些实体。
例如,下面的配置段,greet意图忽略所有实体。
intents:
  - greet:
      use_entities: []再如,下面的代码段greet使用name、first_name实体,忽略first_name、age个实体。
intents:- greet:    use_entities:      - name      - first_name    ignore_entities:      - location      - age这些意图排除的实体将不进行特征化,因此不会影响下一步行动预测。当您的意图不关心提取的实体时,这很有用。如果您在没有这个参数的情况,则实体将按正常方式进行特征化。
Entities
entities部分列出了可以由NLU管道中的任何实体提取器提取的所有实体。这里可以给实体配置两个参数role和group,配置好后,实体提取器会给提取的实体打上您配置的role或者group。
entities:
   - PERSON           # entity extracted by SpacyEntityExtractor
   - time             # entity extracted by DucklingEntityExtractor
   - membership_type  # custom entity extracted by DIETClassifier
   - priority         # custom entity extracted by DIETClassifier
   - city:            # custom entity extracted by DIETClassifier
       roles:
       - from
       - to
   - topping:         # custom entity extracted by DIETClassifier
       groups:
       - 1
       - 2
   - size:            # custom entity extracted by DIETClassifier
       groups:
       - 1
       - 2例如,对于上文role这个配置,实体提取器提取的实体可能是这样的
{
  "text": "Book a flight from Berlin to SF",
  "intent": "book_flight",
  "entities": [
    {
      "start": 19,
      "end": 25,
      "value": "Berlin",
      "entity": "city",
      "role": "from",
      "extractor": "DIETClassifier",
    },
    {
      "start": 29,
      "end": 31,
      "value": "San Francisco",
      "entity": "city",
      "role": "to",
      "extractor": "DIETClassifier",
    }
  ]
}具体NLU的训练数据如何写,我们在下一章描述。这里我们需要知道当NLU的训练数据里面用到实体的role和group的是,domain里面要声明它。role和group是很有用的一个标签,他用于区别提取出的实体,比如一句话提取2个都是地名的实体,有了role的标识,我们就知道哪个是出发地,哪个是到达地。
Slots
插槽是机器人的内存。它们充当一个键值存储,可用于存储用户提供的信息(例如他们的家乡城市)以及收集的有关外部世界的信息(例如数据库查询的结果)。
slot在域的Slots部分定义,包括它们的名称、类型以及它们应该如何影响助手的行为。下面的示例定义了一个名为“slot_name”,类型为text的插槽。
slots:
  slot_name:
    type: text默认的,slot的值可以在story里面配置,影响下一步action的预测。在这里,我们可以通过influence_conversatio属性指定插槽是否影响会话。influence_conversatio:false的时候,slot将存储有关的信息,但不会影响会话的流程。这意味着助手每次预测下一个操作时都会忽略槽的值。
如果influence_conversatio设置为true,则该slot将影响下一个动作预测,除非它具有any类型。注意:不同类型的Slot影响会话的方式是不同的,对于文本Slot未设置和设置将走不同的流程,而至于文本是什么(例如是纽约还是香港)没有任何差别。
slots:
  # this slot will influence the conversation depending on
  # whether the slot is set or not
  home_city:
    type: text
    influence_conversation: true为什么要这么设计呢?主要考虑到这样一种情况,两个输入“天气怎么样?”“纽约的天气怎么样?”如果slot已经设置好,如“纽约”,机器人可以预测action_forecast。如果没有设置slot,它需要先获得城市的信息,然后才能预测天气。
Slot类型有:
Text Slot
存储文本类型的值,当influence_conversatio设置为true的时候,如上文所示,slot有值和没有值将走不同的会话流程,但是slot的值是什么不影响会话流程。
slots:
   cuisine:
      type: textBoolean Slot
存储true或false值.如果influence_conversation设置为true,则助手的行为将根据插槽是否为空、设置为true或设置为false而更改。请注意,bool槽为空和为false是不同的。
slots:
   is_authenticated:
      type: boolCategorical Slot
存储枚举值,如果influence_conversation设置为true,则助手的行为将根据插槽的具体值而改变。这意味着助手的行为会有所不同,这取决于上面示例中的插槽的值是low、medium还是high。默认值“__other__”将自动添加到用户定义的值中。遇到的所有未在插槽值中显式定义的值都映射到__other__。__other__作为系统保留字,用户不能自定义值为__other__。
slots:
  risk_level:
    type: categorical
    values:
      - low
      - medium
      - highFloat Slot
存储实数值,默认的max_value=1.0,min_value=0.0。如果influence_conversation设置为true,则助手的行为将根据插槽的值而改变。如果值介于最小值和最大值之间,则使用数字的特定值。低于最小值的所有值将被视为最小值,高于最大值的所有值将被视为最大值。因此,如果max_value设置为1,则插槽值2和3.5之间是没有差异的。
slots:
  temperature:
    type: float
    min_value: -100.0
    max_value:  100.0List Slot
存储列表值,如果“influence_conversation”设置为true,助手的行为将根据列表是否为空而改变。存储在插槽中的列表长度不影响对话。它只关心列表长度是零还是非零。
slots:
  shopping_items:
    type: listAny Slot
存储任意值(它们可以是任何类型,例如字典或列表)。任何类型的槽在对话过程中总是被忽略。对于此插槽类型,属性influence_conversation不能设置为true。如果要存储影响对话的自定义数据结构,请使用自定义槽类型。
slots:
  shopping_items:
    type: anyCustom Slot Types
也许你的餐厅预订系统最多只能处理6个人的预订。在这种情况下,您希望插槽的值影响下一个选定的操作(而不仅仅是是否指定了它)。您可以通过定义一个自定义slot类来实现这一点。下面的代码定义了一个名为NumberOfPeopleSlot的自定义插槽类。特征化定义了如何将这个槽的值转换成向量,这样Rasa开源机器学习模型就可以处理它。
from rasa.shared.core.slots import Slot
class NumberOfPeopleSlot(Slot):
    def feature_dimensionality(self):
        return 2
    def as_feature(self):
        r = [0.0] * self.feature_dimensionality()
        if self.value:
            if self.value <= 6:
                r[0] = 1.0
            else:
                r[1] = 1.0
        return rNumberOfPeopleSlot有三个可能的“值”,可以用长度为2的向量表示。
(0,0)	not yet set
(1,0)	between 1 and 6
(0,1)	more than 6Slot 自动填充
如果您的NLU模型选择一个实体,并且您的域包含一个同名的slot,那么slot将被自动设置。例如:
stories:
- story: entity slot-filling
  steps:
  - intent: greet
    entities:
    - name: Ali
  - slot_was_set:
    - name: Ali在这种情况下,您不必在故事中包含slot_was_set部分,因为它是自动拾取的。要对特定插槽禁用此行为,可以在域文件中将“自动填充”属性设置为False:
slots:
  name:
    type: text
    auto_fill: Falseslot初始值
slots:
  num_fallbacks:
    type: float
    initial_value: 0Actions
Actions是机器实际会处理的事情。举个例子,一个动作可以是:
对用户的回答
外部API的调用
数据库查找
可以是其他任何事情
自定义Actions和Slots
为了在你的domain中引用slots,你需要以模块路径进行引用。要引用自定义action,可以使用他们的名字。比如,如果你有一个模块叫做my_actions,这个模块里面有一个类叫做MyAwesomeAction,还有一个模块my_slots,含有一个类MyAwesomeSlot,那么在domain文件中,你需要添加下面的内容:
actions:
    - my_custom_action
    ...
    
slots:
    - my_slots.MyAwesomeSlot这个例子中MyAwesomeAction类的name函数必要返回my_custom_action。更加详细的介绍可以见:Custom Actions。
Forms
表单是一种特殊类型的操作,旨在帮助助手从用户那里收集信息。在域文件的forms键下定义表单。有关Forms以及如何定义Forms的详细信息,
Responses
响应是向用户发送消息而不运行任何自定义代码或返回事件的操作。这些响应可以直接在域文件中的responses键下定义,并且可以包含丰富的内容,如按钮和图片等。响应放在域文件中的Responses键下或单独的“Responses.yml“文件。每个响应名称都应该以utter_开头。例如,您可以在响应名称utter_greet和utter_bye下添加问候和道别的响应:
intents:
  - greet
responses:
  utter_greet:
  - text: "Hi there!"
  utter_bye:
  - text: "See you!"如果在助手中使用检索意图,还需要为助手对这些意图的答复添加答复:
intents:
  - chitchat
responses:
  utter_chitchat/ask_name:
  - text: Oh yeah, I am called the retrieval bot.
  utter_chitchat/ask_weather:
  - text: Oh, it does look sunny right now in Berlin.Images和Buttons
在domain定义文件中可以包含图像和按钮,如下:
tempaltes:
    utter_greete:
    - text: "Hey! How are you?"
      buttons:
      - title: "great"
        payload: "great"
      - title: "super sad"
        payload: "super sad"
    utter_cheer_up:
    - text: "Here is something to cheer you up:"
      image: "https://i.imgur.com/nGF1K8f.jpg"注意:这个功能取决于输出channel是否支持button的显示。如命令行模式,会通过以输出选项的方式,替换图像和按钮的显示。
自定义输出
你可以使用custom将任意的响应输出到输出通道。
比如,尽管日期选择器在template模板中并没有定义成参数,但是slack date picker可以通过下面的方式发送:
templates:
  utter_take_bet:
  - custom:
      blocks:
      - type: section
        text:
          text: "Make a bet on when the world will end:"
          type: mrkdwn
        accessory:
          type: datepicker
          initial_data: '2019-05-21'
          placeholder:
            type: plain_text
            text: Select a date特定频道的话语
如果你有某个话语只需要发送到特定频道,可以使用channel关键词进行限定。value需要和channel类(OutputChannel类)的name函数返回的名字相匹配。这个功能当你需要创建的自定义的输出只针对某个频道的时候就很有用。
templates:
  utter_ask_game:
  - text: "Which game would you like to play?"
    channel: "slack"
    custom:
      - # payload for Slack dropdown menu to choose a game
  - text: "Which game would you like to play?"
    buttons:
    - title: "Chess"
      payload: '/inform{"game": "chess"}'
    - title: "Checkers"
      payload: '/inform{"game": "checkers"}'
    - title: "Fortnite"
      payload: '/inform{"game": "fortnite"}'每一次机器查找言论的时候,首先会确认是模板中是否有针对特定频道的言论。如果有,只会选择该言论。如果没有找到,将从没有定义channel的言论中挑选。因此,针对每个言论都定义一个没有channel的情况是一个好的习惯。
变量
在模板中也可以使用变量来表示对话中收集的内容。你可以在python代码中添加或使用自动slot填充机制。如有下面的模板:
templates:
  utter_greet:
  - text: "Hey, {name}. How are you?"Rasa会自动的用叫做name的slot将变量进行填充。
在自定义代码中实现方式为:
class ActionCustom(Action):
    def name(self):
        return "action_custom"
    
    def run(self, dispatcher, tracker, domain):
        # send utter default template to user
        dispatcher.utter_template("utter_default", tracker)
        # .. other code
        return []如果模板包含{my_variable}的变量,你可以通过参数的形式传递给utter_template
dispatcher.utter_template("utter_default", tracker, my_variable="my text")可变性
如果你想要发送给用户的回复是随机的,那么可以将多个回复以列表的形式给出,Rasa会随机地进行选择,如:
templates:
  utter_greeting:
  - text: "Hey, {name}. How are you?"
  - text: "Hey, {name}. How is your day going?"Session configuration
会话表示助手和用户之间的对话。会话可以从三种方式开始:
用户开始与助手对话,
用户在一段可配置的非活动期后发送第一条消息,或者
通过/session_start intent消息触发手动会话启动。
在domain的session_config键下,可用参数包括:
session_expiration_time定义新会话开始之前的非活动时间(以分钟为单位)。
carry_over_slots_to_new_session新会话是否复用以前会话的槽位信息。
当当前会话不活动超过session_expiration_time这个时间,将开启一个新会话。下面是这几个参数的默认配置
session_config:
  session_expiration_time: 60  # value in minutes, 0 means infinitely long
  carry_over_slots_to_new_session: true  # set to false to forget slots between sessions这意味着,如果用户在60分钟不活动后发送第一条消息,则会触发一个新的会话会话,并且所有现有的slot信息都会被转移到新会话中。将session_expiration_time的值设置为0意味着会话将不会结束(请注意,action_session_start操作仍将在会话开始时触发)。
Config
域文件中的config键维护store_entities_as_slots参数。当NLU模型识别出实体并且实体名称与插槽名称匹配时,“store_entities_as_slots ”定义是否应将实体值放入该插槽。默认情况下,实体将自动填充同名的插槽。
config:
  store_entities_as_slots: false