Rasa Core开发指南

1. Rasa Core简介

 Rasa Core是Rasa框架提供的对话管理模块,它类似于聊天机器人的大脑,主要的任务是维护更新对话状态和动作选择,然后对用户的输入作出响应。所谓对话状态是一种机器能够处理的对聊天数据的表征,对话状态中包含所有可能会影响下一步决策的信息,如自然语言理解模块的输出、用户的特征等;所谓动作选择,是指基于当前的对话状态,选择接下来合适的动作,例如向用户追问需补充的信息、执行用户要求的动作等。举一个具体的例子,用户说“帮我妈妈预定一束花”,此时对话状态包括自然语言理解模块的输出、用户的位置、历史行为等特征。在这个状态下,系统接下来的动作可能是:

  • 向用户询问可接受的价格,如“请问预期价位是多少?”;
  • 向用户确认可接受的价格,如“像上次一样买价值200的花可以吗?”
  • 直接为用户预订

 下面是Rasa Core文档中给出的一个对话场景:

1.1 Rasa Core消息处理流程

 由前面描述的对话管理模块了解到,它应该是负责协调聊天机器人的各个模块,起到维护人机对话的结构和状态的作用。对话管理模块涉及到的关键技术包括对话行为识别、对话状态识别、对话策略学习以及行为预测、对话奖励等。下面是Rasa Core消息处理流程:

Rasa Core响应输入文本消息基本流程

  • 首先,将用户输入的Message传递到Interpreter(NLU模块),该模块负责识别Message中的"意图(intent)“和提取所有"实体”(entity)数据;
  • 其次,Rasa Core会将Interpreter提取到的意图和识别传给Tracker对象,该对象的主要作用是跟踪会话状态(conversation state);
  • 第三,利用policy记录Tracker对象的当前状态,并选择执行相应的action,其中,这个action是被记录在Track对象中的;
  • 最后,将执行action返回的结果输出即完成一次人机交互。

注:整个执行过程由Rasa Core框架中的rasa_core.agent.Agent类处理。

1.2 安装Rasa Core
pip install rasa_core

2. Dialogue模型训练

2.1 Story样本数据

 Story样本数据就是Rasa Core对话系统要训练的样本,它描述了人机对话交互过程成可能出现的故事情节,通过对Stories样本和domain的训练得到人机对话系统所需的对话模型。每个story的格式基本是一样的,只是组成的内容不一样,即以##开头的行表示一个story的开始,跟随的文本只用于描述;以*开始的行表示一个意图和填充的slot;以缩进 - 开始表示Rasa NLU识别到该意图后Rasa Core要执行的action。以下是stories.md文件的部分内容:

## story1:greet only
* greet
    - utter_answer_greet
> check_greet

## story2:
* goodbye
	- utter_answer_goodbye

## story3:thanks
* thanks
    - utter_answer_thanks
    
## story4:change address or data-time withe greet
> check_greet
* weather_address_date-time{"address": "上海", "date-time": "明天"}
    - action_report_weather

## story5:change address or data-time withe greet
> check_greet
* weather_address_date-time{"address": "上海", "date-time": "明天"}
    - action_report_weather
    - utter_report_weather
* weather_address{"address": "北京"} OR weather_date-time{"date-time": "明天"}
    - action_report_weather
    - utter_report_weather
...
...

 其中,> check_*用于模块化和简化训练数据,即story复用;OR Statements用于处理同一个story中可能出现2个以上走向(意图),这有利于简化story,但是相应的训练时间相当于训练了两个以上故事,但也不建议使用的太密集。

Visualizing Stories:可视化Stories

 Rasa Core中提供了rasa_core.visualize模块可视化故事,这有利于我们更容易掌握设计故事流程。
命令如下:

python -m rasa_core.visualize -d domain.yml -s data/stories.md -o graph.html -c config.yml

 其中,-m指定运行模块;-d指定domain.yml文件路径;-s指定story路径;-o指定输出文件名;-c指定Policy配置文件。最终,在项目根目录得到一个graph.html,用浏览器打开可见:

graph.html

 当然,除了使用命令生成stories的可视化关系图,我们还可以创建visualize.py代码实现。

from rasa_core.agent import Agent
from rasa_core.policies.keras_policy import KerasPolicy
from rasa_core.policies.memoization import MemoizationPolicy

if __name__ == '__main__':
    agent = Agent("domain.yml",
                  policies=[MemoizationPolicy(), KerasPolicy()])

    agent.visualize("data/stories.md",
                    output_file="graph.html", max_history=2)
2.2 Domain

 domain.yml定义了对话机器人应知道的所有信息,它相当于大脑框架,指定了意图intents实体entities插槽slots以及动作actions,其中,intents、entities应与NLU模型训练样本中标记的致,slots应与标记的entities一致,actions为对话机器人对应用户的请求需作出的动作。此外,domain.yml中的templates部分针对utter_类型action定义了模板消息,便于对话机器人对相关动作自动回复。假如我们要做一个天气资讯的人机对话系统,并且定义一个查询天气和污染程度的action,那么我们要这么做。domain.yml示例:

intents:
  - greet
  - goodbye
  - thanks
  - whoareyou
  - whattodo
  - whereyoufrom
  - search_weather
  - search_weather_quality
  - search_datetime
  - search_city

slots:
  city:
    type: text
  datetime:
    type: text
  matches:
    type: unfeaturized

entities:
  - city
  - datetime

actions:
  - utter_answer_greet
  - utter_answer_goodbye
  - utter_answer_thanks
  - utter_introduce_self
  - utter_introduce_selfcando
  - utter_introduce_selffrom
  - action_search_wether
  - action_search_weather_quality

templates:
  utter_answer_goodbye:
    - text: "再见"
    - text: "拜拜"
    - text: "虽然我有万般舍不得,但是天下没有不散的宴席~祝您安好!"
    - text: "期待下次再见!"
    - text: "嗯嗯,下次需要时随时记得我哟~"
    - text: "88"

  utter_answer_thanks:
    - text: "嗯呢。不用客气~"
    - text: "这是我应该做的,主人~"
    - text: "嗯嗯,合作愉快!"

  utter_introduce_self:
    - text: "您好!我是您的AI机器人呀~"

  utter_introduce_selfcando:
    - text: "我能帮你查询天气信息"

  utter_introduce_selffrom:
    - text: "我来自xxx"

  utter_ask_city:
    - text: "请问您要查询哪里的天气?"

  utter_ask_datetime:
    - text: "请问您要查询哪天的天气"

  utter_report_search_result:
    - text: "{matches}"

  utter_default:
    - text: "小x还在学习中,请换种说法吧~"
    - text: "小x正在学习中,等我升级了您再试试吧~"
    - text: "对不起,主人,您要查询的功能小x还没学会呢~"

说明:

intents things you expect users to say. See Rasa NLU
actions things your bot can do and say
templates template strings for the things your bot can say
entities pieces of info you want to extract from messages. See Rasa NLU
slots information to keep track of during a conversation (e.g. a users age) - see Using Slots
2.2.0 intents

 intents,即意图,这里枚举了在训练NLU模型样本时,样本中标出的所有intent。

intents:
  - greet
  - goodbye
  - thanks
  - search_weather
  - search_weather_quality
  - search_datetime
  - search_city
2.2.1 actions

 当Rasa NLU识别到用户输入Message的意图后,Rasa Core对话管理模块就会对其作出回应,而完成这个回应的模块就是action。Rasa Core支持三种action,即default actions、utter actions以及 custom actions,它们的作用和区别如下:

1. default actions

 DefaultAction是Rasa Core默认的一组actions,我们无需定义它们,直接可以story和domain中使用。包括以下三种action:

  • action_listen:监听action,Rasa Core在会话过程中通常会自动调用该action;
  • action_restart:重置状态,比初始化Slots(插槽)的值等;
  • action_default_fallback:当Rasa Core得到的置信度低于设置的阈值时,默认执行该action;

2. utter actions

 UtterAction是以utter_为开头,仅仅用于向用户发送一条消息作为反馈的一类actions。定义一个UtterAction很简单,只需要在domain.yml文件中的actions:字段定义以utter_为开头的action即可,而具体回复内容将被定义在templates:部分,这个我们下面有专门讲解。定义utter actions示例如下:

actions:
  - utter_answer_greet
  - utter_answer_goodbye
  - utter_answer_thanks
  - utter_introduce_self
  - utter_introduce_selfcando
  - utter_introduce_selffrom

3. custom actions

 CustomAction,即自定义action,允许开发者执行任何操作并反馈给用户,比如简单的返回一串字符串,或者控制家电、检查银行账户余额等等。它与DefaultAction不同,自定义action需要我们在domain.yml文件中的actions部分先进行定义,然后在指定的webserver中实现它,其中,这个webserver的url地址在endpoint.yml文件中指定,并且这个webserver可以通过任何语言实现,当然这里首先推荐python来做,毕竟Rasa Core为我们封装好了一个rasa-core-sdk专门用来处理自定义action。关于action web的搭建和action的具体实现,我们在后面详细讲解,这里我们看下在在Rasa Core项目中需要做什么。假如我们在天气资讯的人机对话系统需提供查询天气和空气质量两个业务,那么我们就需要在domain.yml文件中定义查询天气和空气质量的action,即:

actions:
  ...	
  - action_search_weather
  - action_search_weather_quality
2.2.2 templates

 在前面我们说的,domain.yml的templates:部分就是描述UtterActions具体的回复内容,并且每个UtterAction下可以定义多条信息,当用户发起一个意图,比如"你好!",就触发utter_answer_greet操作,Rasa Core会从该action的模板中自动选择其中的一条信息作为结果反馈给用户。templates部分示例如下:

templates:
  utter_answer_greet:
    - text: "您好!请问我可以帮到您吗?"
    - text: "您好!请说出您要查询的具体业务,比如跟我说'查询身份证号码'"
    - text: "您好!

  utter_answer_goodbye:
    - text: "再见"
    - text: "拜拜"
    - text: "虽然我有万般舍不得,但是天下没有不散的宴席~祝您安好!"
    - text: "期待下次再见!"
    - text: "嗯嗯,下次需要时随时记得我哟~"
    - text: "88"

  utter_answer_thanks:
    - text: "嗯呢。不用客气~"
    - text: "这是我应该做的,主人~"
    - text: "嗯嗯,合作愉快!"

  utter_introduce_self:
    - text: "您好!我是您的AI机器人呀~"

  utter_introduce_selfcando:
    - text: "我能帮你查询天气信息"

  utter_introduce_selffrom:
    - text: "我来自xxx"

  utter_ask_city:
    - text: "请问您要查询哪里的天气?"

  utter_ask_datetime:
    - text: "请问您要查询哪天的天气"

  utter_report_search_result:
    - text: "{matches}"

  utter_default:
    - text: "小x还在学习中,请换种说法吧~"
    - text: "小x正在学习中,等我升级了您再试试吧~"
    - text: "对不起,主人,您要查询的功能小x还没学会呢~"

注:utter_default是Rasa Core默认的action_default_fallback,当Rasa NLU识别该意图时,它的置信度低于设定的阈值时,就会默认执行utter_default中的模板。
 除了回复简单的Text Message,Rasa Core还支持在Text Message后添加按钮图片,以及访问插槽中的值(如果该插槽的值有被填充的话,否则返回None)。举个栗子:

  utter_introduce_self:
    - text: "您好!我是您的AI机器人呀~"
      image: "https://i.imgur.com/sayhello.jpg"
  utter_introduce_selfcando:
    - text: "我能帮你查询天气信息"
      buttons:
    	- title: "好的"
          payload: "ok"
   	 	- title: "不了"
          payload: "no"
  utter_ask_city:
    - text: "请问您要查询{ datetime }哪里的天气?"  
  utter_ask_datetime:
    - text: "请问您要查询{ city }哪天的天气"

 当然,上面描述的template reponse是我们通过训练后,对话机器人识别到相应的意图后,自动触发对应的UtterAction,然后选取一条template文本作为回复消息。其实,我们还可以在自定义action中使用dispatcher.utter_template("utter_greet")函数生成一条消息Message反馈给用户。示例代码:

from rasa_core_sdk.actions import Action

class ActionGreet(Action):
  def name(self):
      return 'action_greet'

  def run(self, dispatcher, tracker, domain):
      dispatcher.utter_template("utter_greet")
      return []
2.2.3 entities

 entities,即实体,这里枚举了在训练NLU模型样本时,样本中标出的所有entity。一般而言,entities和slots的内容应该是slots包含entities关系。

entities:
  - city
  - datetime
2.2.4 slots

 Slots,即插槽,它就像对话机器人的内存,它通过键值对的形式可用来收集存储用户输入的信息(实体)或者查询数据库的数据等。以天气查询为例,那就意味着对话机器人必须知道具体的时间和地点方能查询,因为在domain.yml文件中我们就需要在slots部分定义两个插槽,即city、datetime,而matches则用来存储最后查询的结果,其中,type表示slot存储的数据类型;initial_value为slot初始值,该值可有可无(无意义)。示例如下:

slots:
  city:
    type: text
    initial_value: "北京"
  datetime:
    type: text
    initial_value: "明天"
  matches:
    type: unfeaturized
    initial_value: "none"

(1) slot类型

 关于**rasa_core.slots.Slot值的type**,Rasa Core为我们提供了多种类型,以满足不同情况需求,具体分析如下:

  • text:存储文本信息;

  • bool:存储布尔值,True or False;

  • categorical:指定接收枚举所列的一个值,如:

    slots:
       risk_level:
          type: categorical
          values:
          - low
          - medium
          - high
    
  • float:存储浮点连续值,其中,max_value=1.0, min_value=0.0为默认值,当要存储的值大于max_value,那么slot只会存储max_value;当要存储的值小于min_value,那么slot只会存储min_value。示例如下:

    slots:
       temperature:
          type: float
          min_value: -100.0
          max_value:  100.0
    
  • list:存储列表数据,且列表的长度不影响对话;

  • unfeaturized:用于存储不影响会话流程的数据。这个槽不会有任何的特性,因此它的值不会影响对话流,并且在预测机器人应该运行的下一个动作时被忽略。

  • 自定义slot类型:详见Custom Slot Types

(2) 填充slots的值

 在一次对话中,存在多种方式填充slots的值,下面我们详细分析下这几种情况。

  • Slots Set from NLU

 当我们在训练NLU模型时,标记了一个名为name的实体,并且在Rasa Core的domain.yml文件中也包含一个具有相同名称的slot(插槽),那么当用户输入一条Message时,NLU模型会对这个name进行实体提取,并自动填充到这个名为name的slot中。示例如下:

# story_01
* greet{"name": "老蒋"}
  - slot{"name": "老蒋"}
  - utter_greet

注:在上述情况下,就算我们不包含- slot{"name": "老蒋"}部分,name也会被自动填充。

  • Slots Set By Clicking Buttons

 前面说到,在domain.yml的templates:部分,Rasa Core还支持在Text Message后添加按钮,当我们点击这个按钮后,Rasa Core就会向RegexInterpreter发送以/开头的Message,当然对于RegexInterpreter来说,NLU的输入文本的格式应该与story中的一致,即/intent{entities}。假设对话机器人询问是否需要查询天气信息时,我们在NLU训练样本中标记一个choose意图和decision实体,然后再在domain.yml中将decision标记为slot,当我们点击按钮后,"好的"或“不了”会被自动填充到decision的这个slot中。也就是说,当我们想Rasa Core发送"/choose{"decision": "好的"}"后,会直接识别到意图choose,并提取实体decision的值。

 templates:
    utter_introduce_selfcando:
    - text: "我能帮你查询天气信息"
      buttons:
    	- title: "好的"
          payload: "/choose{"decision": "好的"}"
   	 	- title: "不了"
          payload: "/choose{"decision": "不了"}"
     ...
  • Slots Set by Actions

 以查询天气质量为例,先看下Rasa Core项目中domain.yml和stories.md

# domain.yml
...
actions:
  - action_search_weather_quality

slots:
   weather_quality:
      type: categorical
      values:
      ---...

# stories.md
* greet
  - action_search_weather_quality
  - slot{"weather_quality" : "优"}
  - utter_answer_high
    
* greet
  - action_search_weather_quality
  - slot{"weather_quality" : "中"}
  - utter_answer_midddle 

* greet
  - action_search_weather_quality
  - slot{"weather_quality" : "差"}
  - utter_answer_low
# 注:官方文档这里说,如果slot的类型是categorical时,在stories.md的故事情节中使用- slot设置值有利于提高正确action的执行率?

 在自定义action中,我们先查询天气数据库,以json格式返回,然后提取出json中weather_quality字段的值填充到weather_quality slot中返回。代码如下:

from rasa_core_sdk.actions import Action
from rasa_core_sdk.events import SlotSet
import requests

class ActionSearchWeatherQuality(Action):
    def name(self):
        return "action_search_weather_quality"

    def run(self, dispatcher, tracker, domain):
        url = "http://myprofileurl.com"
        data = requests.get(url).json
        # 解析json,填充slot
        return [SlotSet("weather_quality", data["weather_quality"])]

3 .训练和使用对话模型

3.1 训练对话模型
python -m rasa_core.train -d domain.yml -s data/stories.md -o models/current/dialogue -c config.yml

命令说明:

usage: train.py default [-h] [--augmentation AUGMENTATION] [--dump_stories]
                        [--debug_plots] [-v] [-vv] [--quiet] [-c CONFIG] -o
                        OUT (-s STORIES | --url URL) -d DOMAIN
  • -m mod 指定要运行的module

  • -d或--domain 指定对话机器人的domain.yml文件路径;

  • -s或--stories 指定stories.md文件路径,需要注意的是,我们可以将故事情节根据某种分类保存在多个.md文件中,比如将所有.md文件存放在data目录的stories目录下,此时命令行的参数应该改为-s data/stories/

  • -o或--out 指定对话模型输出路径,训练好的模型会自动保存到该路径下;

  • -c或--c 指定Policy规范文件,config.yml配置文件(默认参数)如下:

    policies:
      - name: KerasPolicy
        epochs: 100
        max_history: 5
      - name: FallbackPolicy
        fallback_action_name: 'action_default_fallback'
      - name: MemoizationPolicy
        max_history: 5
      - name: FormPolicy
    
  • --augmentation AUGMENTATION 该参数默认开启,Rasa Core将通过将故事文件中的故事随机地粘合在一起来创建更长的故事。如果我们希望每次回复都执行相同的action,无论之前的会话历史是什么,可以通过--augmentation 0关闭这种功能。(训练时该参数可选)。

  • --url URL 从URL网络中下载一个stories.md文件用于训练。

 接下来,我们重点介绍下Policy(策略)模块。Policies是Rasa Core中的策略模块,即类rasa_core.policies.Policy,它的作用就是预测对话中,而具体选择哪个action将由预测的置信度决定,哪个的置信度越高就执行哪个。下面是我在测试过程中的debug信息,当我向机器人输入"帮我查手机号码12345"时,Policies模块就会预测到执行action_search_num_business这个action:

2019-04-15 16:46:07 DEBUG    rasa_core.processor  - Received user message '帮我查手机号码12345' with intent '{'name': 'search_plate_number', 'confidence': 0.4848141755831445}' and enti
ties '[{'entity': 'item', 'value': '手机号码12345', 'start': 3, 'end': 12, 'confidence': None, 'extractor': 'ner_mitie'}]'


2019-04-15 16:46:07 DEBUG    rasa_core.policies.memoization  - There is no memorised next action
2019-04-15 16:46:07 DEBUG    rasa_core.policies.form_policy  - There is no active form
2019-04-15 16:46:07 DEBUG    rasa_core.policies.ensemble  - Predicted next action using policy_0_KerasPolicy
2019-04-15 16:46:07 DEBUG    rasa_core.processor  - Predicted next action 'action_search_num_business' with prob 0.98.
2019-04-15 16:46:07 DEBUG    rasa_core.actions.action  - Calling action endpoint to run action 'action_search_num_business'.
2019-04-15 16:46:08 DEBUG    rasa_core.processor  - Action 'action_search_num_business' ended with events '[]'
2019-04-15 16:46:08 DEBUG    rasa_core.processor  - Bot utterance 'BotUttered(text:  ['手机号码12345', '123556', None] 所属人为张三,这是他的业务信息, data: {
  "elements": null,
  "buttons": null,
  "attachment": null
})'

DPL(Dialogue Policy Learning),即对话策略学习,也被称为对话策略(Policy)优化,根据当前对话状态,对话策略决定下一步执行什么系统动作(action)。系统行动与用户意图类似,也由意图和槽位构成。DLP模块的输入时DST(Dialogue state tracker,对话状态跟踪)输出的当前对话状态,通过预设的对话策略选择系统动作作为输出。Rasa Core中拥有不同的policy,且策略配置文件可以同时包含不同的policy

  • Memoization Policy

 MemoizationPolicy只记住(memorizes)训练数据中的对话。如果训练数据中存在这样的对话,那么它将以置信度为1.0预测下一个动作,否则将预测为None,此时置信度为0.0。下面演示了如何在策略配置文件config.yml文件中,配置MemoizationPlicy策略,其中,max_history(超参数)决定了模型查看多少个对话历史以决定下一个执行的action。

 policies:
    - name: "MemoizationPolicy"
    max_history: 5

注:max_history值越大训练得到的模型就越大并且训练时间会变长,关于该值到底该设置多少,我们可以举这么个例子,比如有这么一个Intent:out_of_scope来描述用户输入的消息off-topic(离题),当用户连续三次触发out_of_scope意图,这时候我们就需要主动告知用户需要向其提供帮助,如果要Rasa Core能够学习这种模型,max_history应该至少为3。story.md中表现如下:

* out_of_scope
   - utter_default
* out_of_scope
   - utter_default
* out_of_scope
   - utter_help_message
  • Keras Policy

 KerasPolicy策略是Keras框架中实现的神经网络来预测选择执行下一个action,它默认的框架使用LSTM(Long Short-Term Memory,长短期记忆网络)算法,但是我们也可以重写KerasPolicy.model_architecture函数来实现自己的框架(architecture)。KerasPolicy的模型很简单,只是单一的LSTM+Dense+softmax,这就需要我们不断地完善自己的story来把各种情况下的story进行补充。下面演示了如何在策略配置文件config.yml文件中,配置KerasPolicy策略,其中,epochs表示训练的次数,max_history同上。

policies:
  - name: KerasPolicy
    epochs: 100
    max_history: 5
  • Embedding Policy

 基于机器学习的对话管理能够学习复杂的行为以完成任务,但是将其功能扩展到新领域并不简单,尤其是不同策略处理不合作用户行为的能力,以及在学习新任务(如预订酒店)时,如何将完成一项任务(如餐厅预订)重新应用于该任务时的情况。EmbeddingPolicy,即循环嵌入式对话策略(Recurrent Embedding Dialogue Policy,REDP),它通过将actions和对话状态嵌入到相同的向量空间(vector space)能够获得较好的效果,REDP包含一个基于改进的Neural Turing Machine的记忆组件和注意机制,在该任务上显著优于基线LSTM分类器。EmbeddingPolicy包含以下的步骤:

(1) apply dense layers to create embeddings for user intents, entities and system actions including previous actions and slots(稠密嵌入,包括用户意图、实体和系统行为:以前的动作和槽)
(2) use the embeddings of previous user inputs as a user memory and embeddings of previous system actions as a system memory.(使用以前的用户输入作为用户memory和以前的系统行为作为系统memory)
(3) concatenate user input, previous system action and slots embeddings for current time into an input vertor to rnn(合并用户输入,以前系统行为和槽作为当前时间用户的输入向量给rnn模型)
(4) using user and previous system action embeddings from the input vector, calculate attention probabilities over the user and system memories(使用输入向量中用户输入和以前的系统行为嵌入,来计算用户和系统的注意力向量)
(5) sum the user embedding and user attention vector and feed it and the embeddings of the slots as an input to an LSTM cell(用户词嵌入和用户注意力向量相加再和槽向量一起作为LSTM的输入)
(6) apply a dense layer to the output of the LSTM to get a raw recurrent embedding of a dialogue(应用LSTM的输出来获得一个对话的原始循环嵌入)
(7) sum this raw recurrent embedding of a dialogue with system attention vector to create dialogue level embedding, this step allows the algorithm to repeat previous system action by copying its embedding vector directly to the current time output(将对话的原始循环嵌入和系统注意力向量相加,来创建对话层的嵌入。这一步允许算法通过直接拷贝它的向量到当前的输出来重复之前的系统行为)
(8) weight previous LSTM states with system attention probabilities to get the previous action embedding, the policy is likely payed attention to(加权以前的LSTM状态和系统注意力来获取以前的行为嵌入,policy最有可能需要注意的)
(9) if the similarity between this previous action embedding and current time dialogue embedding is high, overwrite current LSTM state with the one from the time when this action happened(如果以前的行为嵌入和当前的对话嵌入相似度很高,overwrite当前的LSTM状态)
(10) for each LSTM time step, calculate the similarity between the dialogue embedding and embedded system actions(对于LSTM的每一步,计算对话嵌入和系统行为嵌入的相似度)

EmbeddingPolicy

 所以EmbeddingPolicy效果上来说会比较好,但是它有个问题是耗时,而且尤其是官网的源码,它并没有使用GPU、没有充分利用CPU资源。下图为KerasPolicy和EmbeddingPolicy比较效果图,可见EmbeddingPolicy效果明显优于KerasPolicy:

配置EmbeddingPolicy参数:

policies:
  - name: EmbeddingPolicy
    epochs: 20
    featurizer:
    - name: FullDialogueTrackerFeaturizer
      state_featurizer:
        - name: LabelTokenizerSingleStateFeaturizer

注:详情请参考Embedding Policy

  • Form Policy

 FormPolicy是MemoizationPolicy的扩展,用于处理(form)表单的填充事项。当一个FormAction被调用时,FormPolicy将持续预测表单动作,直到表单中的所有槽都被填满,然后再执行对应的FormAction,详情可查阅Slot Filling

  • Mapping Policy

 MappingPolicy可用于直接将意图映射到要执行的action,从而实现被映射的action总会被执行,其中,这种映射是通过triggers属性实现的。举个栗子(domain.yml文件中):

intents:
 - greet: {triggers: utter_goodbye}

 其中,greet是意图;utter_goodbye是action。一个意图最多只能映射到一个action,我们的机器人一旦收到映射意图的消息,它将执行对应的action。然后,继续监听下一条message。需要注意的是,对于上述映射,我们还需要要在story.md文件中添加如下样本,否则,任何机器学习策略都可能被预测的action_greet在dialouge历史中突然出现而混淆。

  • Fallback Policy

 如果意图识别的置信度低于nlu_threshold,或者没有任何对话策略预测的action置信度高于core_threshold,FallbackPolicy将执行fallback action。通俗来说,就是我们的对话机器人意图识别和action预测的置信度没有满足对应的阈值,该策略将使机器人执行指定的默认action。configs.yml配置如下:

policies:
  - name: "FallbackPolicy"
    # 意图理解置信度阈值
    nlu_threshold: 0.3
    # action预测置信度阈值
    core_threshold: 0.3
    # fallback action
    fallback_action_name: 'action_default_fallback'

 其中,action_default_fallback是Rasa Core中的一个默认操作,它将向用户发送utter_default模板消息,因此我们需要确保在domain.yml文件中指定此模板。当然,我们也可以在fallback_action_name字段自定义默认回复的action,比如my_fallback_cation,就可以这么改:

policies:
  - name: "FallbackPolicy"
    nlu_threshold: 0.4
    core_threshold: 0.3
    fallback_action_name: "my_fallback_action"
3.2 使用对话模型

 运行Rasa Core模块命令:

python -m rasa_core.run -d models/dialogue -u models/nlu/current  --port 5002 --credentials credentials.yml --endpoints endpoints.yml --debug -o out.log

参数说明:

  • -m mod 指定运行模块;
  • -d modeldir 指定dialog对话路径;
  • -u modeldir 指定nlu模型路径;
  • --port 指定Rasa Core web应用运行的端口号;
  • --credentials credentials.yml 指定通道(input channels)属性;
  • ----endpoints endpoints.yml 该文件用于指定Rasa Core连接其他web server的url地址,比如nlu web或custom action web;
  • -o file 指定输出log日志文件路径;
  • --debug 打印调试信息,在显示的信息中,我们可以了解到用户输入Message后NLU模块是否提出出实体、意图及其置信度;插槽的填充情况和使用哪个policy(策略)来预测要执行的下一个action,如果这个 exact story已经在训练数据中,并且MemoizationPolicy是集成的一部分,那么它将被用于预测下一次动作的概率为1。注意(重要):如果所有的插槽值和NLU信息均是符合预期,但是仍然预测执行错误的action,我们就需要检测是哪个policy决定的这个action,如果是MemoizationPolicy,则说明在stories.md中我们设计的故事情节有问题;如果是KerasPolicy ,说明我们的模型预测得不对,这里就建议开启交互式学习来创建故事相关(relevant stories)的数据,并添加到我们的stories中。

接下来,我们重要解释下credentials.yml和endpoints.yml。

(1) credentials.yml

 当我们的AI对话系统需要跟外部世界联系时,比如需要连接到facebook, slack, telegram, mattermost and twilio时,就需要使用credentials.yml来存储对应的访问权限信息。如果要连接到这些channels,Rasa Core将从yaml格式的凭证文件中读取这些属性。示例枚举如下:

twilio:
  account_sid: "ACbc2dxxxxxxxxxxxx19d54bdcd6e41186"
  auth_token: "e231c197493a7122d475b4xxxxxxxxxx"
  twilio_number: "+440123456789"

slack:
  slack_token: "xoxb-286425452756-safjasdf7sl38KLls"
  slack_channel: "@my_channel"

telegram:
  access_token: "490161424:AAGlRxinBRtKGb21_rlOEMtDFZMXBl6EC0o"
  verify: "your_bot"
  webhook_url: "your_url.com/webhook"

mattermost:
  url: "https://chat.example.com/api/v4"
  team: "community"
  user: "[email protected]"
  pw: "password"

facebook:
  verify: "rasa-bot"
  secret: "3e34709d01ea89032asdebfe5a74518"
  page-access-token: "EAAbHPa7H9rEBAAuFk4Q3gPKbDedQnx4djJJ1JmQ7CAqO4iJKrQcNT0wtD"

webexteams:
  access_token: "ADD-YOUR-BOT-ACCESS-TOKEN"
  room: "YOUR-WEBEXTEAMS-ROOM-ID"
    
rocketchat:
  user: "yourbotname"
  password: "YOUR_PASSWORD"
  server_url: "https://demo.rocket.chat"

# socket通道
# 前两个配置值定义Rasa Core在通过socket.io发送或接收消息时使用的事件名称
socketio:
  user_message_evt: user_uttered
  bot_message_evt: bot_uttered
  session_persistence: true/false
    
# rest通道
rest:
  # you don't need to provide anything here - this channel doesn't
  # require any credentials
 
# CallbackInput通道
callback:
  # URL to which Core will send the bot responses
  url: "http://localhost:5034/bot"

 当我们需要从自己开发的客户端访问人机对话系统,可以通过使用socket和http通道来实现,它们分别对应socketio输入通道和rest输入通道,而callback与rest通道类似,均是走HTTP协议,但是它不会直接将bot消息返回给发送消息的HTTP请求,而是调用一个URL,我们可以指定该URL来发送bot消息。由于我们使用HTTP情况比较多,该情况下credentials.yml的配置如下:

rest:
  # you don't need to provide anything here - this channel doesn't
  # require any credentials

(2) endpoints.yml

 在Rasa Core项目创建endpoint.yml文件,该文件用于指定Rasa Core将要访问的CustomeAction web和nlu web,当rasa core需要执行意图、实体提取和执行action时,就会根据url找到对应的web进行执行。这里假设CustomeAction web和NLU web是独立的项目,其中,localhost,即IP地址,表示部署在本地,如果部署在其他终端,改成对应的IP即可;5055默认为CustomeAction web端口;5000默认为NLU web。

# 指定custom action web url
action_endpoint:
  url: "http://localhost:5055/webhook"
# 指定nlu web url
nlu:
  url: "http://localhost:5000"
  # you can also specify additional parameters, if you need them:
  # headers:
  #   my-custom-header: value
  # token: "my_authentication_token"    # will be passed as a get parameter
  # basic_auth:
  #   username: user
  #   password: pass
# 指定models url,即从其他服务器获取模型数据
models:
  url: http://my-server.com/models/default_core@latest
  wait_time_between_pulls:  10   # [optional](default: 100)

注:HTTP的POST方式访问url,其中,POST方式的body举例如下:

{
"tracker": {
 "latest_message": {
   "text": "/greet",
   "intent_ranking": [
     {
       "confidence": 1.0,
       "name": "greet"
     }
   ],
   "intent": {
     "confidence": 1.0,
     "name": "greet"
   },
   "entities": []
 },
 "sender_id": "22ae96a6-85cd-11e8-b1c3-f40f241f6547",
 "paused": false,
 "latest_event_time": 1531397673.293572,
 "slots": {
   "name": null
 },
 "events": [
   {
     "timestamp": 1531397673.291998,
     "event": "action",
     "name": "action_listen"
   },
   {
     "timestamp": 1531397673.293572,
     "parse_data": {
       "text": "/greet",
       "intent_ranking": [
         {
           "confidence": 1.0,
           "name": "greet"
         }
       ],
       "intent": {
         "confidence": 1.0,
         "name": "greet"
       },
       "entities": []
     },
     "event": "user",
     "text": "/greet"
   }
 ]
},
"arguments": {},
"template": "utter_greet",
"channel": {
 "name": "collector"
}
}

endpoint的response举例如下:

{
 "text": "hey there",
 "buttons": [],
 "image": null,
 "elements": [],
 "attachments": []
}

4. 搭建CustomActions服务器

 前面说到,CustomAction的具体业务逻辑是实现在一个独立的web server中,当然,我们也可以直接写到Rasa Core项目中。但是考虑到代码的可维护性和模块化,这里还是建议重新创建一个web server项目。对于这个web server的开发语言,虽然Rasa官方基本没有什么限制,但是我还是建议使用python,因为Rasa Core专门为此提供了一个SDK,即rasa-core-sdk,便于我们快速开发action web。基本步骤如下:

第一步:创建action web项目,安装rasa-core-sdk

pip install rasa_core_sdk

注:目前最新版本为0.13.0

第二步:在web项目的根目录下创建actions.py文件,该文件实现具体的action业务逻辑,当然这个文件的名字可以任意命名,也不必要一定要放在根目录下,只是在启动web时需要改下参数。这里仍然以查询天气和空气质量举例,actions.py代码如下:

from rasa_core_sdk import Action
from rasa_core_sdk.events import SlotSet

# 查询天气action
class ActionSearchWeather(Action):
   def name(self):
      # type: () -> Text
      return "action_search_weather"

   def run(self, dispatcher, tracker, domain):
      city = tracker.get_slot('city')
	  datetime =  tracker.get_slot('datetime')
      # 执行天气查询业务逻辑
   	  ....
      
      # 回复用户Message方法1:使用dipatcher
      # dipatcher.utter_message(‘result if result is not None else []’)  
      # return []
      # 回复用户Message方法2:使用SlotSet
      return [SlotSet("matches", result if result is not None else [])
              
  
# 查询空气质量action
class ActionSearchWeatherQuality(Action):
   def name(self):
      # type: () -> Text
      return "action_search_weather_quality"

   def run(self, dispatcher, tracker, domain):
      city = tracker.get_slot('city')
	  datetime =  tracker.get_slot('datetime')
      # 执行查询空气质量业务逻辑
   	  ....
              
      # 回复用户Message方法1:使用dipatcher
      # dipatcher.utter_message(‘result if result is not None else []’)  
      # return []
      # 回复用户Message方法2:使用SlotSet
      return [SlotSet("matches", result if result is not None else [])]

 需要注意的是,编写action的响应代码,必须遵守以下三个规则

  • 创建一个继承于Action的类,这个类的名字可以任意,但是这里还是建议直接根据action的名使用驼峰命名法命名;

  • 重写Action的name函数,返回值必须为对应的action名,因为这是Rasa Core定位到该action类的关键所在;

  • 重写Action的run函数,这个函数就是我们具体的业务所在,即当Rasa Core匹配action成功后,会自动执行该函数完成具体的操作并返回响应给用户。run函数需要传递四个参数,即selfdispatchertracker以及domain,其中后三个尤其重要。

    函数原型:Action.run(dispatcher, tracker, domain)

    (1) 参数说明

    • dispatche:该对象用于向用户回复消息,通过dipatcher.utter_message()dispatcher.utter_template以及其他rasa_core_sdk.executor

      .CollectingDispatcher方法。

    • tracker:该对象描述当前会话的状态,通过该对象的tracker.get_slot(slot_name)方法可以轻松地获得指定插槽中的值,或者通过tracker.latest_message.text获取最新的用户信息等;

    • domain:该对象即为domain.yml

    (2)返回值

     返回一个列表[],该列表可包含多个rasa_core_sdk.events.Event对象,比如上面代码中的SlotSet对象就是一个event,它的作用就是完成插槽值设定这么一个时间。

 从分析Action.run()函数原型可知,它的返回值是一个包含多个rasa_core_sdk.events.Event对象的列表,下面我们就具体介绍下这个Event对象。Event对象是Rasa Core描述会话中发生的所有事件和明确rasa.core.trackers.DialogueStateTracker该如何更新其状态的基类,因此不能被直接使用,而是通过它包含的具体事件(event)实现。具体如下:

(a) SlotSet:设置插槽值

Class rasa_core.events.SlotSet(key, value=None, timestamp=None)
描述 该事件用于实现设置对话tracker中插槽(slot)的值,其中,参数key表示插槽名,参数value表示要设置的值
JSON {
‘event’: ‘slot’,
‘name’: ‘departure_airport’,
‘value’: ‘BER’
}

(b) Restarted:重置tracker

Class rasa_core.events.Restarted(timestamp=None)
描述 该事件用于重置tracker,即初始化tracker的状态,包括所有的会话历史,slots值。
JSON {
‘event’: ‘restart’
}

(3) AllSlotsReset:重置一次会话中所有的插槽

Class rasa_core.events.AllSlotsReset(timestamp=None)
描述 该事件用于初始化会话中所有插槽(slots),当我们希望保留对话历史,仅仅是重置所有slot的值就可以使用AllSlotsReset事件。
JSON {
‘event’: ‘reset_slots’
}

(4) ReminderScheduled:定时执行某个action

Class rasa_core.events.ReminderScheduled(action_name, trigger_date_time, name=None, kill_on_user_message=True, timestamp=None)
描述 该事件用于设置定时执行某个事件,其中,参数action_name为需要执行的action名字,参数trigger_date_time为时间。
JSON {
‘event’: ‘reminder’,
'action': 'my_action',
'date_time': '2018-09-03T11:41:10.128172',
'name': 'my_reminder',
'kill_on_user_msg': True

} |

(5) ConversationPaused:暂停会话

Class rasa_core.events.ConversationPaused(timestamp=None)
描述 该事件用于暂停会话,即对话机器人忽略用户输入的Message,不会执行预测到的action,直到执行了resume事件。通过该事件,我们可以人工接管对用户输入的Message作出响应。
JSON {
‘event’: ‘pause’,
}

(6) ConversationResumed:设置插槽值

Class rasa_core.events.ConversationResumed
描述 该事件用于恢复之前被暂停的会话,对话机器人继续负责对用户输入作出响应。
JSON {
‘event’: ‘resume’,
}

(7) FollowupAction:强制设置next action为指定的action

Class rasa_core.events.FollowupAction(name, timestamp=None)
描述 该事件的作用是强制设定Rasa Core的next action为某个固定的action,而不再通过预测来确定next action是什么。
JSON {
‘event’: ‘followup’,
'name': 'my_action'

} |

(1) UserUttered:用户向对话机器人发送一条Message

Class rasa_core.events.UserUttered(text, intent=None, entities=None, parse_data=None, timestamp=None, input_channel=None, message_id=None)[source]
描述 该事件的作用为用户向对话机器人发送一个条Message,但是当执行该事件后,会自动在Tracker对象中创建一个Turn(注:不知道这个Turn是什么意思)。
JSON {
‘event’: ‘user’,
'text': 'Hey',
'parse_data': {
    'intent': {'name': 'greet', 'confidence': 0.9},
    'entities': []
}

} |

(2) BotUttered:对话机器人向用户发送一条Message

Class rasa_core.events.BotUttered(text=None, data=None, timestamp=None)
描述 该事件用于对话机器人向用户发送一条Message,需要注意的是,BotUttered并不需要训练,它被包含在ActionExecuted类中,Track对象默认有一个实体。
JSON {
‘event’: ‘bot’,
'text': 'Hey there!',
'data': {}

} |

(3) UserUtteranceReverted:当最后一个UserUttered被执行后撤销所有 side effects

Class rasa_core.events.UserUtteranceReverted(timestamp=None)
描述 当最后一个UserUttered被执行后,该事件用于撤销所有side effects
JSON {
‘event’: ‘rewind’
}

(4) ActionReverted:当最后一个action被执行后,撤销所有 side effects

Class rasa_core.events.ActionReverted(timestamp=None)
描述 机器人撤消它的最后一个动作
JSON {
‘event’: ‘undo’,
}

(5) ActionExecuted:Logs an action the bot executed to the conversation. Events that action created are logged separately.

Class rasa_core.events.ActionExecuted(action_name, policy=None, confidence=None, timestamp=None)[source]
描述 An operation describes an action taken + its result.It comprises an action and a list of events. operations will be appended to the latest Turn in the Tracker.turns.
JSON {
‘event’: ‘action’,
'name': 'my_action'

} |

第三步:启动action web服务器,其中bot即bot.py,为action具体实现文件。

python -m rasa_core_sdk.endpoint --actions bot

5.交互式训练

 虽然我们可以容易的人工构建story样本数据,但是往往会出现一些考虑不全,甚至出错等问题,基于此,Rasa Core框架为我们提供了一种交互式学习(Interactive Learning)来获得所需的样本数据。在互动学习模式中,当你与机器人交谈时,你会向它提供反馈,这是一种强大的方法来探索您的机器人可以做什么,也是修复它所犯错误的最简单的方法。基于机器学习的对话的一个优点是,当你的机器人还不知道如何做某事时,你可以直接教它。
 首先,我们创建一些story样本。

## story1:greet only
* greet
    - utter_answer_greet
> check_greet

## story2:
* goodbye
	- utter_answer_goodbye

## story3:thanks
* thanks
    - utter_answer_thanks
    
## story4:change address or data-time withe greet
> check_greet
* weather_address_date-time{"address": "上海", "date-time": "明天"}
    - action_report_weather

## story5:change address or data-time withe greet
> check_greet
* weather_address_date-time{"address": "上海", "date-time": "明天"}
    - action_report_weather
    - utter_report_weather
* weather_address{"address": "北京"} OR weather_date-time{"date-time": "明天"}
    - action_report_weather
    - utter_report_weather
...
...

 其次,启动交互式学习,其中,第一行命令用于启动custom action服务器;第二行命令用于启动交互式学习模式。在交互模式下,机器人会要求你确认NLU和Core做出的每一个预测。

python -m rasa_core_sdk.endpoint --actions actions&

python -m rasa_core.train \
  interactive -o models/dialogue \
  -d domain.yml -c policy_config.yml \
  -s data/stories.md \
  --nlu models/current/nlu \
  --endpoints endpoints.yml
    
# 或者直接使用已经训练好的对话模型
# python -m rasa_core.train \
#  interactive --core models/dialogue \
#  --nlu models/current/nlu \
#  --endpoints endpoints.yml

 当然,在交互式学习过程中,我们还可以通过Rasa Core提供的可视化图形跟踪会话状态,只需要在浏览器输入http://localhost:5005/visualization.html即可。

6. 评估模型

6.1 评估对话模型

 为了评估训练好的对话模型,Rasa Core提供了rasa_core.evaluate模块来实现。当执行下列命令后,rasa_core.evaluate模块会将预测action不正确的所有story存储到results/failed_stories.md文件中。此外,该命令还会自动生成一个混淆矩阵(confusion matrix)文件results/story_confmat.pdf,所谓的混淆矩阵表示的是预测domain.yml文件中每个action被预测命中的频率以及一个错误action被预测的频率。

python -m rasa_core.evaluate --core models/dialogue --stories test_stories.md -o results

参数说明:

usage: evaluate.py default [-h] [-m MAX_STORIES] [-u NLU] [-o OUTPUT] [--e2e]
                           [--endpoints ENDPOINTS]
                           [--fail_on_prediction_errors] [--core CORE]
                           (-s STORIES | --url URL) [-v] [-vv] [--quiet]
  • -m module 指定运行模块;
  • -- core dir 指定对话模型路径;
  • -- stories file或-s file 指定用于测试的story文件路径;
  • -o dir 指定评估结果输出路径;
  • -m number或--max_stories number 指定要测试的story的最大数量;
  • -u NLU或--nlu NLU 指定NLU模型路径;
  • --url URL 指定训练对话模型的story文件URL;
  • --endpoints ENDPOINTS 指定端点文件;
  • --e2e, --end-to-end 为combined action和意图预测运行一个端到端(end-to-end)评估;
6.2 评估NLU和Core

 假设我们的机器人使用对话模型与Rasa NLU模型组合来解析意图,希望评估这两个模型如何在整个对话中一起执行。rasa_core.evaluate通过使用--e2e参数选项,允许将Rasa NLU意图预测和Rasa Core行为预测结合,端对端地评估对话模型。执行脚本如下:

python -m rasa_core.evaluate default --core models/dialogue --nlu models/nlu/current  --stories e2e_stories.md --e2e

 需要注意的是,用于端到端评估的story格式与标准的Rasa Core story格式略有不同,前者必须使用自然语言包含用户消息,而不仅仅是它们的意图,即* <intent>:<Rasa NLU example>
e2e_stories.md示例如下:

## end-to-end story 1
* greet: hello
   - utter_ask_howcanhelp
* inform: show me [chinese](cuisine) restaurants
   - utter_ask_location
* inform: in [Paris](location)
   - utter_ask_price

## end-to-end story 2
...
6.3 对比评估Policies

 在评估模型的时候,需要我们构造一些对话测试集,通常这些测试集从训练的对话数据中抽取而来。但是,作为一个刚启动的项目,毕竟用于训练的对话数据非常有限,肯定是舍不得抽一部分用于测试评估的。因此,为了缓解这个问题,Rasa Core为我们提供一些脚本,允许我们对使用的Policies方案进行对比,以选择一个性能最好的方案来训练我们的数据。步骤如下:
(1) 创建多个Policies配置文件,即不同方案,执行下列脚本

python -m rasa_core.train compare -c policy_config1.yml policy_config2.yml -d domain.yml -s stories_folder -o comparison_models --runs 3 --percentages 0 5 25 50 70 90 95

 其中,-c file1 file2..指定要对比的方案;-o dir 指定输出结果路径;--run num 指定run次数,以确保结果一致;-- percentages指定Rasa Core将使用0、5、25、50、70进行多次训练

(2) 评估训练得到的对比模型,确定那种policies方法更好

python -m rasa_core.evaluate compare --stories stories_folder --core comparison_models -o comparison_results

 其中,--core指定对比模型所在路径;-o 指定输出结果。需要注意的是,如果不确定要比较哪些策略,官方建议可以从EmbeddingPolicy和KerasPolicy入手,确定哪种更适合。

1557369029171

7. FormAction

 在Rasa Core中,当我们执行一个action需要同时填充多个slot时,可以使用FormAction来实现,因为FormAction会遍历监管的所有slot,当发现相关的slot未被填充时,就会向用户主动发起询问,直到所有slot被填充完毕,才会执行接下来的业务逻辑。

7.1 添加form字段到Domain

 在doamin文件下新增forms:部分,并将所有用到的form名称添加到该字段下:

intents:
	- request_restaurant
    - request_weather
    
slots:
    - cuisine
    	type: unfeaturized
    - num_people
    	type: unfeaturized
    - outdoor_seating
    	type: unfeaturized
    - preferences
    	type: unfeaturized
    - feedback
    	type: unfeaturized
            
actions:
    ...

# form action?
forms:
  - restaurant_form
  - weather_form
  ...

templates:
	- utter_ask_cuisine
	- utter_ask_num_people
	- utter_ask_outdoor_seating
	- utter_ask_preferences
	- utter_ask_feedback
    - utter_ask_continue
    ...
7.2 重新构造story

 在story中,不仅需要考虑用户按照我们的设计准确的提供有效信息,而且还要考虑用户在中间过程改变要执行的意图情况或称输入无效信息,因为对于FormAction来说,如果无法获得预期的信息,就会报``ActionExecutionRejection异常,这里我们分别称这两种情况为happy path、unhappy path。示例如下:

## dhappy path for restaurant
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
    - form{"name": null}

## unhappy path for restaurant,chitchat exclude ActionExecutionRejection
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
* chitchat
    - utter_chitchat
    - restaurant_form
    - form{"name": null}
 
## unhappy path for restaurant,change mind suddenly
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
* stop
    - utter_ask_continue
* deny
    - action_deactivate_form
    - form{"name": null}
...

 注:* request_restaurant为意图;- restaurant_form为form action;- form{"name": "restaurant_form"}为激活表单(form);- form{"name": null}为禁止表单;- action_deactivate_form为默认的action,它的作用是用户可能在表单操作过程中改变主意,决定不继续最初的请求,我们使用这个default action来禁止(取消)表单,同时重置要请求的所有slots。

7.3 实现自定义action
class RestaurantForm(FormAction):
   """Example of a custom form action"""

   def name(self):
       # type: () -> Text
       """Unique identifier of the form"""

       return "restaurant_form"

   @staticmethod
   def required_slots(tracker):
       # type: () -> List[Text]
       """A list of required slots that the form has to fill"""

       return ["cuisine", "num_people", "outdoor_seating",
               "preferences", "feedback"]

   def submit(self, dispatcher, tracker, domain):
       # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict]
       """Define what the form has to do
           after all required slots are filled"""

       # utter submit template
       dispatcher.utter_template('utter_submit', tracker)
       return []

 当form action第一被调用时,form就会被激活并进入FormPolicy策略模式。每次执行form action,required_slots会被调用,当发现某个还未被填充时,会主动去调用形式为uuter_ask_{slotname}的模板(注:定义在domain.yml的templates字段中);当所有slot被填充完毕,submit方法就会被调用,此时本次form操作完毕被取消激活。

7.4 配置Configuration

 修改Rasa Core的Policy配置文件configs.yml,新增FormPolicy策略:

policies:
  - name: KerasPolicy
    epochs: 500
    max_history: 5
  - name: FallbackPolicy
    fallback_action_name: 'action_default_fallback'
  - name: MemoizationPolicy
    max_history: 5
  - name: "FormPolicy"
发布了83 篇原创文章 · 获赞 293 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/AndrExpert/article/details/92805022