Rasa使用指南02

前言

对于还不清楚Rasa是什么的小伙伴,请先参阅我的Rasa使用指南01

最近工作很忙,重心也一直在模型方面,例如BERT、GPT-2等等,对于Rasa系列的博文实在是没有时间更新。最近有不停的收到一些小伙伴发来的信息,希望能看到Rasa使用指南02,既然如此,那就满足各位好了,近期也会花更多的时间来更新此系列的内容,如果您对此系列的文章感兴趣那就给我一个评论、一个点赞吧,你门的支持也是我更新的最大动力。

简述

本节的内容主要是再深入讲解一下Rasa core并把上一节未提到的Slot即填槽讲解一下。在开始之前,我们先来看一下Rasa处理对话时的一个流程图。

1、首先接受用户消息,并将该消息送到Interpreter,可以理解为一个解析器,它会把用户的输入内容转变为一个字典,其中包括原始信息,意图,实体等等,其实就是Rasa的NLU模块所做的事情。 2、接下来会把字典送给Tracker,这个是用来记录对话状态并跟踪对话进度的。 3、Policy会接收到Tracker当前的状态,并根据这个状态选择一个合适的Action 4、Action一方面会把信息发送给Tracker,让它记录下当前的状态,另一方面还会给用户发送信息。

整个流程理解起来可能会有点复杂,没关系,在下文我会举一个实例来讲述总的流程细节。在此之前,我们要先看一些基础概念Slot与Action。

Slot

词槽是机器人的记忆力,它会以key-value的形式去存储用户提供的外部信息,大多数情况下,槽值都会影响整个对话。例如用户问今天天气怎么样,这个时候,如果我们要回答用户的问题,首先需要知道用户 问的天气是指的什么地点什么时间,这里的时间和地点就是所谓的词槽,在多轮对话中,主要任务就是把词槽的值填上,所以这个时候机器需要反问用户,询问的天气是什么地点,什么时间,当机器判断槽已经填满了,就可以把答案返回给用户了。而这里的地点,就应该采用Text类型的Slot。Policy是不会读取槽值里面的内容的,它只能判断是否有值。

不同的用户行为,需要使用不同类型的词槽来存储,Rasa有以下几种类型的词槽:

定义Slot

domain文件中以以下格式定义slot,city是词槽的key,type是词槽的类型,initial_value是默认值,默认值是可选项,可不加。

slots:
  city:
    type: text
    initial_value: "深圳"

对于Categorical 类型的slot定义方式如下:

slots:
   account_type:
      type: categorical
      values:
      - premium
      - basic

slot的使用需要action的配合,接下来先看下action再以一个例子把这两者串起来。

Actions

回忆一下domain,domain当中有一个属性叫做actions,actions是接下来要执行的操作,包括返回给用户的信息。我们在上一篇博客中提到过,actions是以utter开头的,但其实actions并不只有这一种定义方法,在Rasa Core中有三种类型的actions,分别为

default actions

default actions包含三个action_listen, action_restart, action_default_fallback

utter actions

如果你只需要给用户直接返回一条文本信息而没有其他的操作的话,那么action需要用UtterAction,并且采用utter_的前缀,如果没有这个前缀,那这个action就会被识别为custom actions。

custom actions

custom actions表示的是用户自定义的action,这个action多轮的关键点。custom action需要配合代码一起使用,它能返回任何你想返回的内容,甚至一些操作,例如你的对话系统连接到了你的灯泡,你可以通过custom actions来自动打开灯,当然了,你也可以用该方法来返回文本信息,达到和utter actions一样的效果。

官方还提供了一个小的python sdk来方便用户编写自定义的action,首先需要安装一下对应的rasa_core_sdk

pip install rasa_core_sdk

然后是代码编写,custom actions必须继承于Action类,并重写其中的两个方法,namerunname方法是用来返回当前action的名字,这个名字必须和定义在stories中的action名字一样run方法包括三个参数,分别是dispatchertrackerdomain,通过tracker可以获取到槽值与用户最近输入的内容,如果想给用户返回信息,可以使用dispatcher,调用 dispatcher.utter_template, dispatcher.utter_message或者rasa_core_sdk.executor.CollectingDispatcher 方法。run方法返回的是给下一个action用的槽值,注意返回值必须是[SlotSet(key,value)]的格式。

custom actions在Rasa中的使用方式并不是直接调用,而是采用服务的形式,所以如果想使用自定义的action,还需要定义一个endpoints.yml文件,文件内容如下:

action_endpoint:
  url: 'http://localhost:5055/webhook'

在启动会话的时候添加额外的命令--endpoints endpoints.yml,该命令会在5055端口启动一个服务,这个服务就是我们定义的action。

Slot与Action的配合使用

接下来我们来看一个例子,我们这里以询问天气为例,该例子我们不考虑时间,只引入了一个“位置”的槽。

首先定义自定义的aciton,其中name方法返回了该action的名字action_ask_weatherrun方法中,先给用户输出了一段文本信息:“您问的天气地点是哪里呢”,这里是在引导用户来填槽,因为还没涉及到实体识别,所以无法读取到用户输入的实体内容,我们这里先默认返回一个槽值即[SlotSet('city', '深圳')]

```python
from rasa_core_sdk import Action
from rasa_core_sdk.events import SlotSet

class ActionAskWeather(Action):
    def name(self):
        return 'action_ask_weather'    
    
    def run(self, dispatcher, tracker, domain):
        dispatcher.utter_message(f'您问的天气地点是哪里呢')
        return [SlotSet('city', '深圳')]
```

编写好后把action_check_restaurants添加到domain文件的actions中,然后编写story

## story_ask_weather 
* ask_weather
  - action_ask_weather
* weather_city
  - utter_weather_good

我们在自定义的action_ask_weather中返回了一个槽值,那么怎么才能获取到这个槽值呢,两种方法,一种发放是在下一个custom action中采用city = tracker.get_slot("city")方法来获取,还有一种方法是使用utter_action,在 templates中直接以{}的方式输出即可。

templates:
  utter_weather_good:
    - text: "{city}天气很好呢"

在我们的这个例子里,就是直接采用第二种方法,最后的运行结果如下图:

最后给一下例子的github地址

额外提一点,所有的配置文件的内容必须使用双引号,单引号会报错!!!

本文讲解的内容虽然不多,例子也比较简单,但是对于其中action与slot的定义以及使用都需要读者好好消化,这也是多轮对话的关键点,下一篇博客(近期就会更新)将会为大家介绍怎么做实体识别与自动填槽。

赞 赏