对话系统rasa示例简析 - concertbot

链接

https://github.com/RasaHQ/rasa/tree/master/examples/concertbot

示例说明

此处不赘述,参见:https://sourcegraph.com/github.com/RasaHQ/rasa/-/blob/examples/concertbot/README.md。

domain描述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
slots:      <!-- 定义了两个slot,concerts和venues,他们的类型都是list -->
  concerts:
    type: list
  venues:
    type: list

intents:   <!-- 定义意图 -->
 - greet
 - thankyou
 - goodbye
 - search_concerts
 - search_venues
 - compare_reviews
 - bot_challenge

entities:  <!-- 定义实体 -->
 - name

templates:                       <!-- 对话模板,用于简单的对话选择 -->
  utter_greet:
    - text: "hey there!"
  utter_goodbye:
    - text: "goodbye :("
  utter_default:
    - text: "default message"
  utter_youarewelcome:
    - text: "you're very welcome"
  utter_iamabot:
    - text: "I am a bot, powered by Rasa."

actions:                        <!-- 动作名称 -->
  - utter_default
  - utter_greet
  - utter_goodbye
  - utter_youarewelcome
  - action_search_concerts
  - action_search_venues
  - action_show_concert_reviews
  - action_show_venue_reviews
  - utter_iamabot

故事描述

故事用来设定意图和响应是怎么对应起来的。故事内容,以及对应的注释如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
## greet <!-- 常规的故事描述,一个意图对应一个对话响应, ## 开头的是描述,可以是任何内容 -->
* greet  <!-- *开头的是意图 -->
    - utter_greet <!-- -开头的是action -->

## happy
* thankyou
    - utter_youarewelcome

## goodbye
* goodbye
    - utter_goodbye

## venue_search
* search_venues
    - action_search_venues <!-- 自定义的action -->
    - slot{"venues": [{"name": "Big Arena", "reviews": 4.5}]} <!-- slot对应的默认值 -->

## concert_search
* search_concerts
    - action_search_concerts
    - slot{"concerts": [{"artist": "Foo Fighters", "reviews": 4.5}]}

## compare_reviews_venues
* search_venues
    - action_search_venues
    - slot{"venues": [{"name": "Big Arena", "reviews": 4.5}]}
* compare_reviews <!-- search_venues, compare_reviews顺序执行时,会匹配到下面定义的action -->
    - action_show_venue_reviews

## compare_reviews_concerts
* search_concerts
    - action_search_concerts
    - slot{"concerts": [{"artist": "Foo Fighters", "reviews": 4.5}]}
* compare_reviews
    - action_show_concert_reviews

## bot challenge
* bot_challenge
  - utter_iamabot

rasa visualize运行结果如下:

Action描述

actions.py中给出了自定义的action,可以参见:对话系统Rasa - Actions 翻译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from rasa_sdk import Action
from rasa_sdk.events import SlotSet

# 继承Action实现自定义action,通常需要实现两个函数,
# name,返回的是该action的名字,需要和stories.md和domain.yml中保持一致,
# run,具体实现的代码
class ActionSearchConcerts(Action):
    def name(self):
        return "action_search_concerts"

    def run(self, dispatcher, tracker, domain):
        concerts = [
            {"artist": "Foo Fighters", "reviews": 4.5},
            {"artist": "Katy Perry", "reviews": 5.0},
        ]
        description = ", ".join([c["artist"] for c in concerts])
        dispatcher.utter_message("{}".format(description)) # 返回对话内容
        return [SlotSet("concerts", concerts)] # 将值设定到slot中


class ActionSearchVenues(Action):
    def name(self):
        return "action_search_venues"

    def run(self, dispatcher, tracker, domain):
        venues = [
            {"name": "Big Arena", "reviews": 4.5},
            {"name": "Rock Cellar", "reviews": 5.0},
        ]
        dispatcher.utter_message("here are some venues I found")
        description = ", ".join([c["name"] for c in venues])
        dispatcher.utter_message("{}".format(description))
        return [SlotSet("venues", venues)]


class ActionShowConcertReviews(Action):
    def name(self):
        return "action_show_concert_reviews"

    def run(self, dispatcher, tracker, domain):
        concerts = tracker.get_slot("concerts") # 从tracker中,根据slot名字,获取slot的值
        dispatcher.utter_message("concerts from slots: {}".format(concerts))
        return []


class ActionShowVenueReviews(Action):
    def name(self):
        return "action_show_venue_reviews"

    def run(self, dispatcher, tracker, domain):
        venues = tracker.get_slot("venues")
        dispatcher.utter_message("venues from slots: {}".format(venues))
        return []

endpoints描述

1
2
action_endpoint: <!-- 自定义action 的通信通道 -->
  url: http://localhost:5055/webhook

config描述

关于pipeline可以参见:对话系统Rasa - choosing a pipeline 翻译。简单描述pipeline定义了自然语言理解过程中的各个步骤,以supervised_embeddings为例,其先后包括了:分词(WhitespaceTokenizer),正则化特征化(RegexFeaturizer),实体提取(CRFEntityExtractor),实体同义词映射(EntitySynonymMapper),词袋特征化(CountVectorsFeaturizer),意图分类(EmbeddingIntentClassifier)。

通过pipeline我们可以将输入转换成意图,那么针对意图如何选择对应的action呢,policies定义了行为选择的策略。其中,

  • KerasPolicy用Keras中实现的神经网络来选择下一个action;
  • FallbackPolicy,当满足以下条件中的其中一个时候会触发回退,1. 意图识别的confidence的值低于nlu_threshold,2. 识别出来的意图中,confidence最高的和第二高的之间的差距小于ambiguity_threshold,3. 没有一个对话策略预测的行为的confidence值高于core_threshold
  • MemoizationPolicy用来记忆你的训练数据中的对话,当在训练数据中能够找到对应的对话,那么confidence为1,否则confidence为0;
  • MappingPolicy:能够将意图映射到action。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
language: en

pipeline: supervised_embeddings

policies:
  - name: KerasPolicy
    epochs: 200
    batch_size: 50
    max_training_samples: 300
  - name: FallbackPolicy
  - name: MemoizationPolicy
  - name: MappingPolicy

对话记录

需要注意的是这个例子中没有提供NLU数据,因此在对话中要直接输入意图。而没有合适结果的对话,都会被导向default message回复。