RASA
安装
RASA3.x版本安装:pip install rasa
或者完整版pip install rasa[full]
或者spacy版pip install rasa[spacy]
Windows和Linux都用下列方式安装成功(2.x)
常用命令
rasa init
初始化一个rasa项目rasa train
训练一个模型rasa shell
交互式地跟你的rasa聊天--debug
debug模式
rasa interactive
交互式训练你的rasa
详细的参数设置参考官网:https://rasa.com/docs/rasa/command-line-interface
工作原理
首先,将用户输入的Message传递到Interpreter(Rasa NLU模块),该模块负责识别Message中的"意图(intent)“和提取所有"实体”(entity)数据;
其次,Rasa Core会将Interpreter提取到的意图和识别传给Tracker对象,该对象的主要作用是跟踪会话状态(conversation state);
第三,利用policy记录Tracker对象的当前状态,并选择执行相应的action,其中,这个action是被记录在Track对象中的;
最后,将执行action返回的结果输出即完成一次人机交互。
domain.yml配置意图intents、槽位slots、实体entities、行为actions、表单forms、回复responses(行为对应的回复)
表单对应的操作在actions.py里定义。
stories.md里面配置意图所对应的触发事件
文本进来——识别意图——触发表单事件——确定需要的槽位——用话术咨询获取到需要的实体——填充,返回
面向任务的对话系统整体流程由 NLU、DST、DPL、NLG 這 4 个主要模块组成。
整体流程
输入:用户输入(sentence) 过程: userAct <-- NLU(sentence) dialogState <-- DST(userAct, dialogHistory) systemAct <-- DPL(dialogState) reply <-- NLG(systemAct) 输出:系统回复(reply)
NLU 模块
输入:用户输入(sentence) 过程: userAct.intent <-- intentRecognizer(sentence) userAct.slotArray <-- slotFilling(sentence, userAct.intent) 输出:用户动作(userAct)
DST 模块
输入:用户动作(uesrAct) & 对话历史(dialogHistory)
过程:
if userAct.intent is not None:
dialogState.intent <-- userAct.intent
dialogState.slotArray <-- userAct.slotArray
checkDefaultSlot(dialogState)
else:
dialogState.intent <-- getIntent(dialogHistory)
dialogState.slotArray <--
updateDialogState(userAct.slotArray, dialogHistory)
输出:对话状态(dialogState)
DPL 模块
输入:对话状态(dialogState)
过程:
if dialogState.intent == "询问天气":
if dialogState.slotArray[0] is None:
systemAct.intent <-- "AskDate"
elif dialogState.slotArray[1] is None:
systemAct.intent <-- "AskLocation"
else:
systemAct.intent <-- "AskWeather"
systemAct.slotArray[0] <- getWeather(dialogState.slotArray)
elif dialogState.intent is None:
systemAct.intent <-- Exception("IntentError")
else:
OtherService
输出:系统动作(systemAct)
NLG 模块
输入:系统动作(systemAct)
过程:
if systemAct.intent == "AskDate":
reply <-- "请输入时间"
elif systemAct.intent == "AskLocation":
reply <-- "请输入地点"
elif systemAct.intent == "AnswerWeather":
reply <-- systemAct.slotArray[0]
elif systemAct.intent == "IntentError":
reply <-- "抱歉,刚刚没听出,能再说一次吗"
else:
OthersystemAct
输出:系统回复(reply)
用Response Selector实现FAQ和闲聊
目的:实现问答式的功能,每次遇到同一种问题的时候,有一样的回答。比如“你叫什么”,回答“我叫小明”
1.更新配置config.yml
加入RulePolicy和ResponseSelector(需要有featurizer和intent classifier,并且配置在这些项的后面)
ResponseSelector默认会为所有的意图创建一个检索模型,如果想为FAQ和闲聊分别创建不同的检索模型,可以配置多个ResponseSelector,并且指定retrieval_intent
字段
2.定义intents和ResponseSelector
如果有20个不同的FAQ,是不是需要定义20个对应的回复,很麻烦。与其写20条规则,不如用一个action例如utter_faq
来处理所有的FAQ,他们都在同样的intent下。
action使用ResponseSelector的输出来返回正确的回复。
3.配置rules.yml
4.更新NLU训练数据
nlu.yml文件
记得更新domain.yml
5.定义回复
domain.yml
详细参考官网:https://rasa.com/docs/rasa/chitchat-faqs
处理业务逻辑(槽位+表单)
表单,收集一些信息(槽位),槽位填充完成后,返回满足用户需求的回复
1.定义表单
定义表单以及需要填充的信息domain.yml
记得在slots
部分加入槽位相关配置
mappings是指slots的填充方式,如果是from_entity
,那么entities
部分也要加入对应的配置
还可以设置influence_conversation
为false,表示不影响对话
number
这个实体可以用DuckingEntityExtractor来抽取(config.yml):
outdoor_seating
槽位是根据用户意图来填充的,如果是affirm
,那么填充为true
,如果是deny
,填充为false
。
如何针对slots设计相应的问题呢?domain.yml
2.更新配置
config.yml
3.创建规则rules.yml
4.更新NLU训练数据
注意,from_entity的slots会忽略意图自动填充,也就是说,只要从用户消息中抽出了对应的实体,不管此时意图是什么,都会填充到对应的slots
inform是generic的意图。
更新domain.yml
5.定义最终回复
domain.yml
参考:https://rasa.com/docs/rasa/business-logic
Fallback(意料之外的、低置信度意图、Action等)
1.创建一个Out-of-scope意图nlu.yml
2.定义回复domain.yml
3.创建一条规则rules.yml
Fallbacks
Fallbacks可以处理低置信度(意图的分类置信度低)消息
NLU Fallback
使用FallbackClassifier,当所有其它意图的预测概率都低于阈值的时候,会变成意图nlu_fallback
1.配置config.yml
2.定义回复信息domain.yml
3.创建一条规则rules.yml
处理低置信度Action
1.更新配置config.yml
2.定义回复domain.yml
3.定制默认action(可选)actions.py
两阶段Fallback
(1)向用户确认意图
(2)用户确认或否认意图
(3)用户重述他们的意图
(4)用户确认或否认重述的意图
1.更新配置config.yml
2.定义fallback回复domain.yml
Rasa提供了默认的确认用户意图以及重述意图的实现,如果需要定制化可以参考default actions.
3.定义两阶段的Fallback规则rules.yml
4.定义fallback action
如果用户意图还是不能确认,需要配置一个默认的回复domain.yml
也可以定制化action
如果需要在某些情况下转给人工处理,可以配置相应的回复为“你需要转接给人工吗”,用户确认后,通过某个渠道转给人工处理。
用户回复意料之外
domain.yml
slots influence the assistant's behaviou
stories.yml
上下文相关的对话
比如有些话要结合上下文才能确定什么意思
1.定义slots
domain.yml
2.创建故事
stories.yml
3.配置TEDPolicy
stories.yml
config.yml
参考:https://rasa.com/docs/rasa/contextual-conversations
主动发消息给用户
1.更新配置
config.yml
2.添加规则
rules.yml
3.设置回复
domain.yml
1.触发意图
会触发一个名为EXTERNAL_dry_plant
的意图,同时传入实体plant
,用户是user123
2.获取Conversation ID
3.增加NLU训练数据
domain.yml
因为是通过trigger_intent
触发的意图,所以不需要训练数据
4.更新Domain
domain.yml
5.加规则
rules.yml
6.添加回复
domain.yml
Reminders
定制
actions.py
rules.yml
nlu.yml
domain.yml
config.yml
Reacting
rules.yml
domain.yml
取消reminders
rules.yml
nlu.yml
domain.yml
参考:https://rasa.com/docs/rasa/reaching-out-to-user
NLU
文本进入NLU后的标准流程:
Message——tokenizer分词——featurizer提取特征——classifier意图分类
NLU训练数据
NLU样本格式
Rasa框架提供了两种NLU模型训练样本数据格式,即Markdown
或JSON
,我们可以将NLU训练数据保存到单独的文件或者多个文件的目录。由于JSON
的可读性不是很好,通常我们使用Markdown
来存储训练数据。
nlu.md
训练数据包含四部分:
(1) Common Examples
它是NLU模型的核心,也是训练NLU模型的基础。Common examples由三部分组成:intent、text和entities,其中,text表示用户自然语言文本,即用户Message;intent表示某个意图,它应于某些text相对应;entities表示将要被提取的目标实体,我们需要在text文本中标出(如果该text存在实体的话)。Common Examples一般格式如下:
注:text中可以不包括实体,但如果包含需要用entityText进行标志。
(2) synonyms 同义词,顾名思义,对于同义词来说,在实体提取时会统一被解析成同一个意思。比如:
在我们说账户余额
、话费
等词语时,NLU在提取实体时会能够成功被捕获,并被统一解析成余额
。需要注意的是,为了在训练数据中使用同义词,需要pipeline中包含EntitySynonmMapper
组件。
(3) Regular Expression Features
正则表达式特征有助于意图分类和实体提取,但是它并不参与实体和意图的定义,仅仅是提供规则来协助意图分类和实体提取,因此,在训练文本text中,该添加的实体和意图样本需要照样添加。比如当需要用户输入的手机号实体时,我们可以再nlu.md文件中添加正则表达式特征支持,当用户输入的Message包含符合手机号正则表达式规则的内容时,Rasa可以更加容易地将其提取出来。Regular Expression Features一般格式如下:
注意:phone_number表示的既不是实体名也不是意图名,它只是一个便于我们阅读的标志而已。除了实体识别,我们还可以编写符合意图分类的正则表达式,这里就不演示了。另外,需要注意的是,对于实体提取来说,目前只有CRFEntityExtractor 实体提取器支持正则特征,像MitieEntityExtractor和SpacyEntityExtractor目前还不支持;对于意图分类器,目前均已支持正则特征。最后,为了使用正则特性,我们需要在pipline中添加RegexFeaturizer组件。
(4) lookup tables
查找表有利于在加载训练数据时,生成与Regular Expression Features相同的正则特征。当在训练数据中提供查找表时,内容被组合成一个大型、不区分大小写的regex模式,该模式在训练示例中查找精确匹配。这些正则表达式匹配多个token,其处理与训练数据中直接指定的正则表达式模式相同。查找表可以包括在训练数据中,如果外部提供的数据必须要以换行进行分隔。比如data/lookup_tables/DataPackage.txt可以包含:
对该查找表在nlu.md
文件中加载如下:
注意:mobile_data_package
表示实体名。为了查找表能够有效的被使用,训练数据中必须要有一些示例被匹配上。否则,模型不会使用查找表特征向查找表添加数据时必须小心,比如如果表中有误报或其他噪声,就会影响性能,因此请确保查找表包含干净的数据。
试了下总是报错。文件名、目录名或卷标语法不正确。: '\ufeff上海\n北京\n天津\n杭州\n南京'。试了各种编码都不行
要在config.yml中写上
阈值问题
config.yml
验证数据有效性
检查domian.yml
、NLU data
和Story data
是否有错误。
使用命令
python m rasa data validate
使用代码
意图分类器
RASA NLU意图分类器
entity
https://forum.rasa.com/t/introducing-entity-roles-and-groups/28156
Slot
词槽是机器人的记忆力,它会以key-value的形式去存储用户提供的外部信息,大多数情况下,槽值都会影响整个对话。例如用户问今天天气怎么样,这个时候,如果我们要回答用户的问题,首先需要知道用户 问的天气是指的什么地点什么时间,这里的时间和地点就是所谓的词槽,在多轮对话中,主要任务就是把词槽的值填上,所以这个时候机器需要反问用户,询问的天气是什么地点,什么时间,当机器判断槽已经填满了,就可以把答案返回给用户了。而这里的地点,就应该采用Text类型的Slot。Policy是不会读取槽值里面的内容的,它只能判断是否有值。
不同的用户行为,需要使用不同类型的词槽来存储,Rasa有以下几种类型的词槽:
Text Slot 文本类型
Boolean Slot 布尔类型
Categorical Slot 分类类型,举个例子,用户预定咖啡,咖啡分为Tall,Grande和Venti三种杯型,这时候只允许用户从这三种类型中选一个
Float Slot 浮点类型
List Slot 列表类型
Unfeaturized Slot 该类型的词槽表示那些你想保存下来的信息,但是这些信息对于对话流程来说没有任何影响。
在domain文件中以以下格式定义slot,city是词槽的key,type是词槽的类型,initial_value是默认值,默认值是可选项,可不加。
例子2
Actions
actions是接下来要执行的操作,包括返回给用户的信息。我们在上一篇博客中提到过,actions是以utter开头的,但其实actions并不只有这一种定义方法,在Rasa Core中有三种类型的actions,分别为
1.default actions:系统默认提供的action。default actions包含三个action_listen, action_restart, action_default_fallback
action_listen,表示停止预测,等待用户的输入
action_restart,表示重置整个会话
action_default_fallback,撤消最后一条用户消息,并返回机器人不理解该消息
2.utter actions:模板回复,以 utter_作为开头, 该action只能用于给用户返回信息。如果你只需要给用户直接返回一条文本信息而没有其他的操作的话,那么action需要用UtterAction,并且采用utter_的前缀,如果没有这个前缀,那这个action就会被识别为custom actions。
3.custom actions:该action可执行任何的操作。custom actions表示的是用户自定义的action,这个action是多轮的关键点。custom action需要配合代码一起使用,它能返回任何你想返回的内容,甚至一些操作,例如你的对话系统连接到了你的灯泡,你可以通过custom actions来自动打开灯,当然了,你也可以用该方法来返回文本信息,达到和utter actions一样的效果。
在 domain.yml 文件中定义 actions
在 endpoint.yml 文件中指定 webserver 的 url 地址,在指定的 webserver 中实现它
官方提供 python sdk 使用户自定义 action,需继承 Action 并重写 name 和 run 方法
custom actions必须继承于Action类,并重写其中的两个方法,name与run,name方法是用来返回当前action的名字,这个名字必须和定义在stories中的action名字一样。run方法包括三个参数,分别是dispatcher、tracker、domain,通过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。
源码
C:\ProgramData\Anaconda3\envs\rasapj\Lib\site-packages\rasa
message.py
nlu模块接受Message类型的数据作为输入,源码在rasa.shared.nlu.training_data.message.py中。默认的变量,分别为time、data、features、output_properties。其中,text中存储的是用户输入的问题,time存储的是时间,data存储的是解析后的数据。
Message中常用的方法是get和set方法,get是从data中取出想要的数据,set是将解析后的intent等信息存入data中。
model.py
rasa/nlu/model.py
model.py中定义了Interpreter类,主要用于解析数据,该类在rasa/core/interpreter.py中的RasaNLUInterpreter调用,用于解析text。
interpreter.py
rasa/core/interpreter.py
RasaNLUInterpreter
在初始化的时候,通过self._load_interpreter()来引入rasa.nlu.model中的Interpreter模块,Interpreter有一个load方法,load传入已经训练好的模型文件路径,加载出nlu模型供interpreter调用。
如果想新建自己的interpreter类,需要注意两点:
如何传入已经训练好的nlu模型文件路径和在rasa中引入自己的interpreter模块,这部分可以在endpoints.yml中引入
额外数据的引入,由于parse方法中传入了tracker,历史信息都可以在tracker中获取。
processor.py
rasa/core/processor.py
在processor中,def parse_message调用interpreter方法,然后在外层封装为def _handle_message_with_tracker
Core:对话管理
对话管理(Dialog Management,DM)控制着人机对话的进程,对话管理通过对自然语言理解模块的输出(即意图、槽位、槽位信息)的处理来进行和用户的交互和反应。针对带有明确意图的用户,在对话管理过程中用户不但可以完善或修正自己需求,管理系统也可以在用户需求不够具体的时候明确或澄清用户的需求,以达到最终的任务完成。
对话管理系统主要包括两部分:状态追踪和对话策略。对话策略通常作为对话管理的输出,如对该场景下缺失槽位的反问策略等。以某公司智能产品介绍系统的多伦对话为示例:
1
Bot
这里是 XX 公司,很高兴为您服务!
开场白
2
客户
我想了解贵司有哪些 AI 产品
意图识别(产品咨询)
3
Bot
好的,您有哪方面的业务需求?
澄清需求,以便填槽
4
客户
需要支持语音识别并自动识别出风险信息
槽填充(语音 & 信息)
5
Bot
对处理技术有要求吗?
澄清需求
6
客户
最好有大数据处理能力
槽填充(大数据)
7
Bot
好的,咱们 XX 产品非常符合您的需求!
任务完成,反馈相关产品介绍
Stories - 对话场景
对话的场景流程。人机对话过程中可能出现的故事情节,训练得到人机对话系统所需的对话模型。
场景:用 “##” 表示
意图:用 “*” 表示
活动:用 “-” 表示
Tracker数据存储与流转
rasa中的数据存储和流转都是通过tracker来实现的。tracker类的定义DialogueStateTracker在rasa/shared/core/trackers.py中。tracker的存储则定义在rasa/core/tracker_store.py中。
数据传入rasa
rasa提供了数据传入接口,我们可以通过requests模块post请求数据,在rasa内部,通过inputchannel模块接收并组织数据。
在rasa/core/channels/channel.py中有一个基类class InputChannel,该类中常用的需改写的方法就是get_metadata。该方法中定义了一个字典metadata,我们可以把任何需要的数据update到metadata字典中,然后在其他地方调用。
在实际应用中,我们常用的inputchannel模块是RestInput(在channels/rest.py中),它继承了基类class InputChannel,其中重要的方法是_extract_sender,_extract_message,分别是获取传入的id和文本内容。
他们的key分别为"sender"和"message",当然,key的名称可以自己指定,但要记住post请求的时候要用相同的key名称。
tracker的定义和数据添加
DialogueStateTracker类中定义了很多方法和要存储的数据,但在实际应用中,我们可能不满足与rasa给定的那些数据,比如在我们的智能外呼项目中,对一通完整的对话,我们需要在会话结束的时候给这一通对话打上tag并存储下来,这就需要在trakcer中添加一个tag_info字段。
对话主要入口
rasa/core/agent.py的handle_text()和handle_message()
processor.py
processor.handle_message(message)
tracker = await self.log_message(message) 根据message,sender_id获取当前的对话状态DialogueStateTracker
await self._predict_and_execute_next_action(message, tracker)循环计算下个action是哪个,得分和对应的policy
action, prediction = self.predict_next_action(tracker)计算下个action的得分和对应使用的policy
各个对话策略policy
Embedding Policy(REDP,效果最好需要重点研究)
The Recurrent Embedding Dialogue Policy (REDP) described in our paper,对应论文: Few-Shot Generalization Across Dialogue Tasks
This policy only works with FullDialogueTrackerFeaturizer(state_featurizer).
FormPolicy(槽填充,重要,比较常用,详见https://rasa.com/docs/rasa/core/forms/#forms)
KerasPolicy(LSTM)
MemoizationPolicy:如果与训练数据完全一致则得分为1,否则为0
MappingPolicy:直接把识别的意图映射到一个具体action
FallbackPolicy:识别的置信度得分小于设置的阈值时进行触发的action,可设置nlu和core的阈值
docker中运行Rasa
安装&启动docker参考 Docker中的Linux安装部分
建一个文件夹,进入文件夹,按照rasa官方教程来
1.初始化建立一个rasa项目
中间遇到的问题
没权限建立actions:https://github.com/rasahq/rasa/issues/4653,执行下面命令:
如果是已有项目,不需要运行上述命令
2.使用docker compose运行多个服务
要将Rasa与其他服务(例如用于自定义操作的服务)一起运行,建议使用Docker Compose。Docker Compose提供了一种简单的方法,可以将多个容器一起运行,而无需运行多个命令。
touch docker-compose.yml
写入内容
该文件以你要使用的Docker Compose规范版本开头。每个容器都在docker-compose文件中声明为service
。第一项服务是rasa服务。
该命令类似于docker run
命令。ports
部分定义容器和你的主机系统之间的端口映射。在这种情况下,它会在你的主机的5390
端口上提供5005
的rasa
服务。这是Rasa的REST Channel接口的端口。
注意: 由于Docker Compose启动了一组Docker容器,因此在执行 run 命令后不再可能连接到单个容器的命令行。
要运行docker-compose.yml
中配置的服务,请执行: docker-compose up
如果命令找不到,执行pip install docker-compose
后台运行是docker-compose up``-d
注:没成功过。。。。
3.shell界面运行
docker run -it -v $(pwd):/app rasa/rasa:2.8.0-full shell
4.训练模型
docker run -v $(pwd):/app rasa/rasa:2.8.0-full train --domain domain.yml --data data --out models
5.运行
创建一个网络来连接actions和rasa:docker network create my-project
然后运行actions:docker run -d -v $(pwd)/actions:/app/actions --net my-project --name action-server rasa/rasa-sdk:2.8.0
然后在endpoints.yml里面加上(这里的端口根据情况而定,默认是5055)
然后可以shell中交互docker run -it -v $(pwd):/app -p 5005:5005 --net my-project rasa/rasa:2.8.0-full shell --endpoints configs/endpoints.yml
或者docker run -it -v $(pwd):/app -p 5390:5005 --net my-project rasa/rasa:2.8.0-full run
docker run -d -v $(pwd):/app -p 5390:5005 --net my-project rasa/rasa:2.8.0-full run
如果是用公司的:
docker run -d -v $(pwd)/actions:/app/actions --net my-project --name action-server hub.cloud.ctripcorp.com/rasa2.8.0/rasa-sdk:2.8.0
docker run -d -v $(pwd):/app -p 8080:5005 --net my-project hub.cloud.ctripcorp.com/rasa2.8.0/rasafull:20210726 run
-it改成-d就是后台运行。
把endpoints.yml放到当前目录下,就不需要写endpoints参数了。
问题
time out一般是请求不到,说明端口没有放开,没权限,改端口号
如果需要在docker中自定义别的包,参考:https://rasa.com/docs/rasa/how-to-deploy#recommended-deployment-methods
将docker镜像传到公司的dockerhub仓库:http://conf.ctripcorp.com/pages/viewpage.action?pageId=605064180#id-复制从Dockerhub使用指南-复制从Dockerhub使用指南-三、Pull/Push镜像
参考资料
https://github.com/km1994/nlp_paper_study/blob/master/dialogue_system_study/rasa/rasa中文对话系统.md
https://github.com/km1994/nlp_paper_study/blob/master/dialogue_system_study/rasa/rasa中文对话系统构建.md
https://github.com/km1994/nlp_paper_study/blob/master/dialogue_system_study/rasa/rasa系列/rasa_nlu.md
多轮对话 - Core篇(有rasa多轮对话的例子)
Rasa NLU及自定义NLU组件
https://github.com/vba34520/rasa_nlu_xercis(自定义组件)
https://terrifyzhao.github.io/2019/02/26/Rasa使用指南02.html
https://github.com/GaoQ1/rasa_chatbot_cn
https://blog.csdn.net/ljp1919/category_9656007.html
https://rasachatbot.com/9_Running_Rasa_with_Docker/
智能交互助手 - Rasa自定义的component(使用BERT做分类和NER)
https://github.com/crownpku/Rasa_NLU_Chi/issues/57
训练数据的生成工具
Last updated
Was this helpful?