Rasa-入门

1. 聊天机器人简介

聊天机器人,一种通过语音、文字和计算机程序进行交互,模拟人类对话的过程。

相关产品:chatGPT,阿里小蜜,Siri等

分类:闲聊型,FAQ,任务型

1.1 人机交互过程

image-20230604181627811

1.1.1 语音识别

Automatic Speech Recognition(简称:ASR),是一种语音转文字的技术,目前科大讯飞,百度等都有相关的商业解决方案,开源的项目Kaldi

1.1.2 自然语言理解

Natural Language Understanding(简称:NLU),主要用于提取用户说的一句话中,所要表达的意图,和提供的相关信息实体

比如:我要请一天假

通过NLU,转化为,意图:请假,实体:{请假时长:1天}

1.1.3 对话管理

Dialog Management(简称:DM),人机对话的控制中心,在多轮对话中,会根据对话历史推断出当前应该做什么,需要给用户响应什么数据。

比如:

NLU:意图:请假,实体:{请假时长:1天}

DM:要执行请假动作,还缺少实体,请假开始时间,请假类型

NLG:询问用户请假开始时间,请假类型

NLU:识别到用户输入的请假开始时间和类型

DM:调用业务后台API写入请假数据,提示用户请假申请完成

NLG:请假申请提交成功

1.1.4 自然语言生成

Natural Language Generation(简称:NLG),通过模板或者序列生成方式,将数据转换成人类可以识别的文本。可以转换为不同的语言风格和不同国家的语言

1.1.5 语音合成

Text To Speech(简称:TTS),将文字转语音的技术

1.2 机器学习

image-20230604181658903

训练过程:通过给机器人提供对话模板,包含标识对话的意图,对话中包含的实体,对话情景故事等,让机器人通过算法学习,生成模型

机器人:通过加载模型,让机器人知道如何根据模型响应真实用户说的话

算法:解决如何在计算机中表示文本信息,如何为文本建模。比如将文本转化为词向量,word2vec,用一个浅层神经网络,在大规模语料上进行训练,通过每个词语上下文的联系,将文本的语义嵌入一个稠密向量。通过transformer解决上下文不同语义的问题,使用transformer开发模型,BERT,GPT等,GPT-3.5目前chatGPT使用的主要模型,已经能生成质量很高的文本。

2. Rasa简介

官方网站:https://rasa.com/

开源版本文档地址:https://rasa.com/docs/rasa/

github:https://github.com/rasahq/rasa

Rasa 是通过可扩展的对话式 AI 平台,可以构建工业级的聊天机器人,主要解决FAQ和任务型对话,Apache 2.0协议,可商用。截至2020年已经筹集4000多万美元的融资。包含开源的核心包rasa,商业版本:管理后台rasa-x,rasa平台rasa platform

3. Rasa 安装&Demo

3.1 python环境

目前支持 3.7,3.8,3.9,3.10

3.2 运行一个demo

  • 创建rasa-learn项目
  • 创建虚拟环境conda create -n rasa-learn python=3.7,并切换到rasa-learn
  • pip安装rasa,最好设置下pip的镜像,加快下载速度
1
pip install rasa
  • 初始化项目
1
rasa init
  • 在执行init后

    • 第一个提示,是否创建在当前目录(直接回车)

      ? Please enter a path where the project will be created [default: current directory]

    • 第二个提示,目录不为空,是否继续(直接回车)

      ? Directory ‘D:\code\learn\rasa-learn’ is not empty. Continue? (Y/n)

    • 第三个提示,项目初始化完成,是否训练(直接回车)

      Created project directory at ‘D:\code\learn\rasa-learn’.

      Finished creating project structure.

      ? Do you want to train an initial model? 💪🏽 (Y/n)

    • 第四个提示,项目训练完后,是否开启对话(直接回车)

    • Your Rasa model is trained and saved at ‘models\20230318-134634-obsolete-armament.tar.gz’.
      ? Do you want to speak to the trained assistant on the command line? 🤖 (Y/n)

  • 现在可以开始对话

1
2
3
4
5
6
7
8
9
10
11
2023-03-18 13:51:38 INFO     root  - Rasa server is up and running.
Bot loaded. Type a message and press enter (use '/stop' to exit):
Your input -> hi
Hey! How are you?
Your input -> so perfect
Great, carry on!
Your input -> are you a bot
I am a bot, powered by Rasa.
Your input -> /stop
2023-03-18 13:54:37 INFO root - Killing Sanic server now.

  • 项目目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── actions
│ ├── __init__.py
│ └── actions.py
├── config.yml
├── credentials.yml
├── data
│ ├── nlu.yml
│ └── stories.yml
├── domain.yml
├── endpoints.yml
├── models
│ └── <timestamp>.tar.gz
└── tests
└── test_stories.yml

image-20230312013536080

4. Rasa命令

命令 影响
rasa init 使用示例训练数据、操作和配置文件创建一个新项目。
rasa train 使用您的 NLU 数据和故事训练模型,将训练后的模型保存在./models.
rasa interactive 通过与您的助手聊天,开始交互式学习会话以创建新的训练数据。
rasa shell 加载经过训练的模型并让您在命令行上与您的助手交谈。
rasa run 使用经过训练的模型启动服务器。
rasa run actions 使用 Rasa SDK 启动一个动作服务器。
rasa visualize 生成故事的视觉表示。
rasa test 在任何以test_.
rasa data split nlu 对您的 NLU 训练数据执行 80/20 拆分。
rasa data convert 在不同格式之间转换训练数据。
rasa data migrate 将 2.0 域迁移到 3.0 格式。
rasa data validate 检查域、NLU 和对话数据是否存在不一致。
rasa export 将对话从跟踪器存储导出到事件代理。
rasa evaluate markers 从现有跟踪器存储中提取标记。
rasa -h 显示所有可用命令。

5. 构建一个请假机器人

5.1 需求

  • 通过构建一个请假机器人,来认识rasa的各个组件,机器人支持FAQ,和请假任务
  • 创建一个机器人的过程
  1. 使用rasa init初始化项目
  2. 编写NLU数据
  3. 编写回应数据
  4. 编写form
  5. 编写规则
  6. 编写故事
  7. 训练模型
  8. 测试模型

5.2 初始化项目

  • 创建一个bot-leave的项目
  • 安装依赖
    • pip install rasa
    • pip install jieba
  • rasa init初始化项目

5.3 编写NLU数据

Rasa NLU负责意图提取和实体提取。比如:我想请一天假,NLU会提取意图:请假,实体:时长一天,如下编写一个请假需要的NLU数据,写在nlu.yml中

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
version: "3.1"

nlu:
- intent: greet
examples: |
- hello
- hi
- 你好
- intent: goodbye
examples: |
- 再见
- 拜拜
- bye
- intent: faq/how_to_leave
examples: |
- 怎么请假
- 如何请假
- 我想请个假该怎么做
- 请假需要做什么
- intent: faq/leave_type
examples: |
- 有哪些请假类型
- 我可以请哪些假
- 支持的请假类型有哪些
- intent: faq/leave_flow
examples: |
- 怎么查看请假审批进程
- 请假进度
- 请假审批进度
- 在哪看请假进度
- intent: leave
examples: |
- 帮我请个假
- 我想请[一天](duration)假
- 帮我请[一周](duration)假
- 我要请[三天](duration)假
- [今天](begin_time)我要请[一天](duration)假
- [10月9号](begin_time)我要请[一天](duration)假
- 我需要请[半天](duration)[事假](leave_type)
- 帮我请[1天](duration)[病假](leave_type)
- [后天](begin_time)我想请[一天](duration)[年假](leave_type)
- 从[明天](begin_time)开始请[2天](duration)[年假](leave_type)
- [6月1号](begin_time)帮我请[一天](duration)[调休假](leave_type)
- intent: info_begin_time
examples: |
- [今天](begin_time)
- [明天](begin_time)
- [后天](begin_time)
- [6月10号](begin_time)
- [3月2号](begin_time)
- [7月1号](begin_time)
- [3月3日](begin_time)
- [12月25日](begin_time)
- intent: info_duration
examples: |
- [1天](duration)
- [一天](duration)
- [两天](duration)
- [三天](duration)
- [半天](duration)
- [一周](duration)
- intent: info_leave_type
examples: |
- [事假](leave_type)
- [病假](leave_type)
- [婚假](leave_type)
- lookup: leave_type
examples: |
- 事假
- 病假
- 年假
- 婚假
- 调休假
- 育儿假
- 呱呱假
- synonym: "0.5"
examples: |
- 半天
- synonym: "1"
examples: |
- 1天
- 一天
- synonym: "2"
examples: |
- 两天
- 二天
- synonym: "3"
examples: |
- 三天
- 3天
- synonym: "7"
examples: |
- 七天
- 一周
  • intent表示意图
  • examples列举了一些提供给机器学习的样本
  • intent分为两种
    • faq/xxx,该意图表示问答类型,faq名字可以随意更换,xx/xxx,主要是提供了同一类型的意图,通用的学习处理方式,不用每个意图都去设置规则或者故事
    • 不带/的意图,普通的意图,意图的示例样本中,可以标记实体,让机器学习,标记的方式[xxx](实体类型)
  • lookup:查找表,当你在训练数据中提供查找表时,该表的内容将组合成一个大的正则表达式。此正则表达式用于检查每个训练样本,以查看它是否包含查找表中词条的匹配项。
  • synonym:同义词,通过将提取的实体映射到一个值而非提取的文字来规范化训练数据

5.4 编写回应数据和表单

5.4.1 固定响应数据和表单

响应数据,主要编写机器人应该如何回复用户,

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
version: "3.1"

intents:
- greet
- goodbye
- leave
- info_begin_time
- info_duration
- info_leave_type
entities:
- begin_time
- duration
- leave_type
slots:
begin_time:
type: any
mappings:
- type: from_entity
entity: begin_time
duration:
type: any
mappings:
- type: from_entity
entity: duration
leave_type:
type: any
mappings:
- type: from_entity
entity: leave_type
responses:
utter_default:
- text: "对不起,我没有明白您在说什么,请换个说法重试"
utter_greet:
- text: "你好,我是一个请假机器人"
utter_goodbye:
- text: "再见,感谢您的使用"
utter_faq/how_to_leave:
- text: "我可以帮您请假呦,您可以这样讲,帮我请一天假"
utter_faq/leave_type:
- text: "您可以请:事假、病假、年假、婚假、育儿假、调休假、呱呱假"
utter_faq/leave_flow:
- text: "对不起,系统暂时不支持查询请假流程,请登录员工考勤系统查看审批进程哦,我们会尽快完善功能"
utter_ask_begin_time:
- text: "请输入请假开始时间"
utter_ask_duration:
- text: "您要请多长时间"
utter_ask_leave_type:
- text: "请选择您要请假的类型"
buttons:
- title: "事假"
payload: '/info_leave_type{{"leave_type":"事假"}}'
- title: "病假"
payload: '/info_leave_type{{"leave_type":"病假"}}'
- title: "年假"
payload: '/info_leave_type{{"leave_type":"年假"}}'
- title: "婚假"
payload: '/info_leave_type{{"leave_type":"婚假"}}'
- title: "育儿假"
payload: '/info_leave_type{{"leave_type":"育儿假"}}'
- title: "调休假"
payload: '/info_leave_type{{"leave_type":"调休假"}}'
- title: "呱呱假"
payload: '/info_leave_type{{"leave_type":"呱呱假"}}'
actions:
- action_leave
forms:
leave_form:
required_slots:
- begin_time
- duration
- leave_type


session_config:
session_expiration_time: 60
carry_over_slots_to_new_session: true

回复数据写在domain.yml中,主要包含以下内容:

  • intents:定义所有的意图
  • entities:定义所有用到的实体
  • slots:插槽,槽内的值可以让机器人知道之前的对话里,用户提供了哪些关键信息
  • responses:给用户回复的模板,定义了机器人回复用户的内容,可以包含,文本,图片,按钮,富文本等信息
  • actions:定义了所有的自定义动作,应为我们要处理任务,比如提交一个请假数据,单靠机器人是无法做到的,这时候就需要结合我们的提交接口去完成,actions中可以编写我们自己的业务代码,完成业务逻辑处理。
  • forms:表单,定义完成某项任务必须要收集到的用户数据,在没有收集完数据前,机器人会循环询问用户所需要的参数,比如请假,需要请假开始时间,请假时长,请假类型,三个参数,如果一开始用户说“我要请一天假“,之提供了时长,这时候,机器人就会问用户,什么时候开始请假,请什么假来收集数据

5.4.2 自定义响应

我们需要通过自定义动作来实现业务逻辑处理,通过实现Action类的方法,来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from typing import Any, Text, Dict, List

from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher


class ActionLeaveWorld(Action):

def name(self) -> Text:
return "action_leave"

def run(self, dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
begin_time = tracker.get_slot("begin_time")
duration = tracker.get_slot("duration")
leave_type = tracker.get_slot("leave_type")
print(f"请假参数信息,开始时间:{begin_time}, 时长:{duration},类型:{leave_type}")
dispatcher.utter_message(text=f"您从{begin_time}开始,为期{duration}天的{leave_type}申请已提交,请关注审批进度")
return []

  • name表示响应的名称,要和domain中定义的名称一致
  • run中编写业务代码

5.5 编写规则

规则,就是机器人接受到用户发送的某个意图后,不管在什么情况,都会按照固定的方式响应用户。编写在rules.yml中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3.1"

rules:
- rule: FAQ
steps:
- intent: faq
- action: utter_faq
- rule: activate leave form
steps:
- intent: leave
- action: leave_form
- active_loop: leave_form
- rule: submit form
condition:
- active_loop: leave_form
steps:
- action: leave_form
- active_loop: null
- action: action_leave

  • rule定义了每一条规则
  • steps定义了一个固定的用户对话情景步骤
  • intent收到用户的意图
  • action机器人该如何响应该意图

该配置文件定义了3条规则

  • faq规则
    • 因为FAQ的模式是固定的,就是一问一答,不需要考虑上下文语境
  • 表单开始规则
    • 定义了,一旦收到用户发起请假申请的意图,就会自动开启请假表单,并且开始收集表单需要的参数
  • 表单提交规则
    • 定义了,在开启leave_form表单的条件下,一旦所有参数收集完成(active_loop: null),就提交表单(action_leave)

5.6 编写故事

一个故事就是一个剧本,故事可让机器人知道如何响应用户的请求,正常完成一个任务,需要哪些步骤来完成,怎么引导用户完成任务,故事是可以让机器进行学习的,机器学习完成后,可以一定程度上泛化这个故事,不用把每一种场景都写成一个故事。故事写在stories.yml中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3.1"

stories:

- story: happy path
steps:
- intent: greet
- action: utter_greet
- intent: leave
- action: leave_form
- active_loop: leave_form
- intent: faq
- action: utter_faq
- action: leave_form

该故事,编写了一种场景,用户在和机器人打招呼后,发起请假流程,正常情况,机器人会收集3个参数,并完成表单提交,但是有一种场景,机器人在收集参数的过程中,用户并没有回答机器人收集的参数,而是提了一个faq的问题,比如,机器人提示,请问您需要请什么假,这时候用户提了一个问题,有哪些请假类型,机器人需要先回答用户的问题,再接着收集参数,这种场景,就需要单独编写一个故事,在form中插入faq的意图。

5.7 训练数据

首先,需要添加配置,在config.yml中,language 和 pipeline 指定模型用于NLU预测的组件。policies 定义模型用于预测下一步动作的策略。

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
recipe: default.v1
assistant_id: 20230604-204819-obliging-stave
language: zh

pipeline:
- name: JiebaTokenizer
token_pattern: (?u)\b\w+\b
dictionary_path: "data/"
- name: RegexFeaturizer
- name: LexicalSyntacticFeaturizer
- name: CountVectorsFeaturizer
OOV_token: oov
- name: CountVectorsFeaturizer
analyzer: char_wb
min_ngram: 1
max_ngram: 4
- name: DIETClassifier
epochs: 200
ranking_length: 5
- name: EntitySynonymMapper
- name: RegexEntityExtractor
use_word_boundaries: false
use_lookup_tables: true
use_regexes: true
- name: ResponseSelector
epochs: 100
retrieval_intent: faq
- name: FallbackClassifier
threshold: 0.7
policies:
- name: RulePolicy
core_fallback_threshold: 0.3
core_fallback_action_name: "action_default_fallback"
enable_fallback_prediction: true
- max_history: 6
name: AugmentedMemoizationPolicy
- name: TEDPolicy
max_history: 10
epochs: 40
batch_size:
- 32
- 64

  • 这里每一个name都是训练模型需要经过的一个组件,其中主要分为以下几块
    • 分词器,这里使用中文的 Jieba 分词器
    • 文本特征化器:主要返回特征向量
    • 意图分类器:提取意图
    • 实体提取器:提取实体
    • 选择器:FAQ
  • 这些组件可以根据具体的情况进行更换,rasa对这些基础的算法做了集成和封装,极大的简化了非专业人员入手机器人的门槛。

5.8 其他修改

5.8.1 开启自定义动作服务器

在endpoints.yml中,配置动作服务器的地址

1
2
action_endpoint:
url: "http://localhost:5055/webhook"

5.8.2 开启websocket

这里通过一个页面来访问写好的机器人,在 credentials.yml 文件中配置websocket

1
2
3
4
socketio:
user_message_evt: user_uttered
bot_message_evt: bot_uttered
session_persistence: false

5.8.3 编写一个访问页面

该前端页面使用了开源的项目rasa-webchat(https://github.com/botfront/rasa-webchat)进行构建。

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Demo for bot</title>
</head>

<body>
<script>
!(function () {
let e = document.createElement("script"),
t = document.head || document.getElementsByTagName("head")[0];
(e.src = "https://cdn.jsdelivr.net/npm/rasa-webchat@1.x.x/lib/index.js"),
(e.async = !0),
(e.onload = () => {
window.WebChat.default(
{
customData: { "language": "en"},
socketUrl: "http://127.0.0.1:5005",
socketPath: "/socket.io",
title: "请假机器人",
initPayload: "hi"
},
null
);
}),
t.insertBefore(e, t.firstChild);
})();
</script>
</body>
</html>

5.9 启动项目

5.9.1 训练模型

打开终端,输入:rasa train,训练完成可以看到类似信息

1
Your Rasa model is trained and saved at 'models\20230604-223615-numerous-dune.tar.gz'.

5.9.2 启动rasa

在终端中输入:rasa run --cors "*",启动完成后,可以看到如下日志信息

1
2023-06-04 22:57:20 INFO     root  - Rasa server is up and running.

5.9.3 启动自定义动作服务

新开一个终端,输入:rasa run actions,启动完成,可以看到如下日志信息

1
2023-06-04 21:35:43 INFO     rasa_sdk.endpoint  - Action endpoint is up and running on http://0.0.0.0:5055

5.9.4 启动web服务器

新开一个终端,输入:python -m http.server,启动完成,可以看到如下日志信息

1
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

注意:总共需要3个终端分别启动3个服务

5.9.5 在浏览器访问

现在可以在浏览器输入:http://localhost:8000/ 来体验机器人

image-20230605002859633