rasa框架学习二(slots,entities,actions,component)
可以回顾:rasa框架学习一(domain.yml、nlu.md、stories.md)
rasa框架响应消息的基本步骤:
一、插槽:slots
slots可以充当键值存储。在domain.yml文件里设置slot的键及其类型,我们可以在action.py通过get_slo(“slot_key”)方法来获取slot的值,或者用SlotSet("slot_key","value")的方法来给slot设置值。
在domain.yml文件里进行插槽设置如下图:
1. slot类型
# text:存储文本信息; slots: country: type: text initial_value: "China" # bool:存储布尔值,True or False; slots: AKeyWord: type: bool initial_value: false # categorical:指定接收枚举所列的一个值 slots: weather: type: categorical values: - sun - rain - cloudy # float:存储浮点连续值,其中,max_value=1.0, min_value=0.0为默认值,当要存储的值大于max_value,那么slot只会存储max_value;当要存储的值小于min_value,那么slot只会存储min_value。 slots: result: type: float min_value: 0.0 max_value: 100.0 # list:存储列表数据 slots: shopping_items: type: list # unfeaturized:该插槽不会具有任何特征,因此其值不会影响对话流程,在预测机器人应执行的下一个动作时会被忽略。 slots: name: type: unfeaturized
2. 自定义插槽
自定义插槽我还没用过,下面是官方文档的例子:
from rasa.core.slots import Slot class GetData(Slot): def feature_dimensionality(self): return 2 def as_feature(self): r = [0.0] * self.feature_dimensionality() if self.value: if self.value <= 6: r[0] = 1.0 else: r[1] = 1.0 return r
二、实体:entities
实体和插槽是差不多的,是可以存在于用户消息中的,用户传过来一句:“我要吃()”,这个括号里面的东西是可变的,我们需要加个实体来代替,获取用户的消息时,也把实体的值给获取到。所以实体的效果是和插槽相同的,总的来说插槽是包括实体的。
在domain.yml里设置一个实体:menu_name
entities:
- menu_name
nlu.md里面我们就可以用这个实体来占位置
## intent:which_menu
- 我想吃[](menu_name) - 我要吃[](menu_name) - 我喜欢吃[](menu_name) - 我们点[](menu_name) 这道菜吧
在actions.py里获取这个值可以用get_slot("实体名或插槽名")。在下面的actions里会说到。
三、actions
在action.py里自定义action,除了要在domain和stories里面添加上进行训练后,还有就是要先运行下面的命令,再rasa shell,自定义的action才可以生效。
如果rasa
已经安装,请运行以下命令以启动动作服务器:
rasa run actions
否则,如果尚未安装rasa
,请运行以下命令:
python -m rasa_sdk --actions actions
******我们来展示一个对用户的一句话进行解析的完整例子:*******
nlu.md文件:
## intent:which_menu - 我想吃[](menu_name) - 我要吃[](menu_name) - 我喜欢吃[](menu_name) - 我们点[](menu_name) 这道菜吧
stories.md文件
## which_menu * which_menu - action_which_menu
domain.yml文件
设置了俩插槽,一个是context,context设置了俩值:Success和Failure,用来记录菜是否被记下;第二个插槽是which_menu_times,用来记录which_menu这个意图被问了多少次。
slots: context: type: categorical values: - Success
- Failure which_menu_times: type: unfeaturized entities: - menu_name actions: - utter_which_menu - action_which_menu - utter_three_times
templates: utter_which_menu: - text: "好的我已记下,还需要什么吗?"
actions.py文件
1. 自定义的action必须要继承Action类,AllSlotsReset, Restarted是设置此次对话结束并重启对话,我们常用在结束语境的action里。
2. dispatcher用来回复消息:dispatcher.utter_template和dispatcher.utter_message
dispatcher.utter_template("utter_which_menu",tracker)是从domain的template里读取文本信息,我们一般都这样使用这种形式
dispatcher.utter_message(“不好意思。。。”)这个直接在actions里进行文本设置回复,一般不这样用。
3. tracker是用来获取slot和entities的值,tracker还有个方法是tracker.current_state()结果是一个字典,里面有个latest_message键,tracker.current_state()["latest_message"]里的value有很多信息,比如intent,entities等等,可以看看下面的源码:
4. domain,我没用过,打印过一次,只记得里面的东西很多,想自己了解的可以print出来看看是什么
from rasa_sdk import Action from rasa_sdk.events import SlotSet,AllSlotsReset, Restarted class ActionWhichMenu(Action): def name(self): # action的名字 return "action_which_menu" def run( self, dispatcher, # type: CollectingDispatcher tracker, # type: Tracker domain, # type: Dict[Text, Any] ): # 获取是which_menu这个意图被识别了多少次,也就是这个问题被问了多少遍 which_menu_times = tracker.get_slot("which_menu_times") or 0 which_menu_times = int(which_menu_times) + 1 # 若是同个问题重复问三次及以上,则点餐失败 if which_menu_times >=3: dispatcher.utter_message("不好意思,你的问题我无法回答,此次点餐失败") # 点餐失败返回的参数:点餐结果(context) # AllSlotsReset(), Restarted()表示此次对话结束,重启对话 return [AllSlotsReset(), Restarted(), SlotSet("context", "Failure")] else: # 获取实体menu_name的值 menu_name = tracker.get_slot("menu_name") # 若是菜名存在 if menu_name: dispatcher.utter_template("utter_which_menu", tracker) # 返回参数:意图识别次数(which_menu_times),点餐结果(context) return [SlotSet("which_menu_times", which_menu_times), SlotSet("context", "Success")] else: dispatcher.utter_message("我没听清,您需要重新点一下啊") # 点餐失败返回的参数:点餐结果(context) # AllSlotsReset(), Restarted()表示此次对话结束 return [AllSlotsReset(), Restarted(), SlotSet("context", "Failure")]
代码都已完成我们来分析一下啊
“我想吃酸菜鱼”---》进入nlu.md匹配---》匹配意图成功后进入stories.md找到对应的意图触发action行为---》进入actions.py找到对应的actions类然后走逻辑代码走完后给用户返回消息(dispatcher)
四、component
component是在意图识别之前的一个过程,如下图
所以我们可以在意图识别之前在component里可以对获取的消息进行处理
我在components里用到的是设置intent和entities
这里要说一下,nlu和core这两个文件可以在你python安装下的包site-packages里的rasa里找到,拷贝到你的项目里。
1. 设置意图inent
若是用户说的是“嗯”,“好”,“好的”等信息,我们把意图强制改成affirm
class SortIntent(Component): name = "sort_intent" # 组件名字 provides = ["intent"] # 当前组件能够计算出什么 print("enter SortIntent Component") def __init__(self, component_config=None): super(SortIntent, self).__init__(component_config) def process(self, message, **kwargs): text = message.text # 获取用户信息 intent = message.get("intent") print("SortIntent text:", text) print("intent:", intent) intent_ranking = [] if text == "嗯" or text == "好" or text == "好的": intent = {"name": "affirm", "confidence": 1.0} intent_ranking.append(intent) message.set("intent", intent, add_to_output=True) message.set("intent_ranking", intent_ranking, add_to_output=True)
2. 在组件里设置实体entities
我在这里面设置实体的值是一个电话号码phonenumber,因为我之后要在actions里用到
class DealMessage(Component): """处理反馈消息""" text = message.text # text/phonenumber/str 或者 text text_list = text.split("/") # ["text", "phonenumber", "str"] 或者["text"] # text_list = ["华为"] 微信端口传过来的数据 # text_list = ["text","13100000000", "str"] 电话端口传来的数据 entities = message.get("entities") if len(text_list) > 1: message.text = text_list[0] # 把用户说的话赋值给message.text print("message.text", message.text) print("是电话") self.phonenumber = text_list[1] elif len(text_list) == 1: self.phonenumber = None print("是微信") # 设置实体的电话号码值 entities = [{ 'start': 0, 'end': 4, 'value': phonenumber, 'entity': 'phonenumber', 'confidence': 1, 'extractor': 'CRFEntityExtractor' }] message.set("entities", entities, add_to_output=True)
注意:写完自定义组件后一定要注册、配置再训练,不然用不了。
1. 注册:
进入nlu文件下的registry.py
导入这两个类
再找到component_classes列表在后面追加写上类的名字,就算注册完成了
2. 配置:
进入config.yml进行配置,这里要注意一下顺序,你写的组件,需要先用到哪个就排到前面,我这里需要先用到DealMessage所以就把它写到了SortIntent前面。
3. 一定要训练,不然没有用
rasa train
五、启动项目
运行服务器:
首先actions:
rasa run actions
nohup rasa run actions & # 加上nohup &使后台一直运行这个项目
然后rasa
rasa run -m models --port 5002 --credentials credentials.yml --endpoints endpoints.yml
nohup rasa run -m models --port 5002 --credentials credentials.yml --endpoints endpoints.yml &
查看进程
ps -ef # 产看所有进程
lsof -i:5055 #查看端口号为5055的进程
杀死进程
kill -9 2895 # 杀死pid为2895的进程
查看日志
# 查看日志 tail -f 日志 # 单个关键词高亮显示: tail -f 日志文件 | perl -pe 's/(关键词)/\e[1;颜色$1\e[0m/g' tail -f catalina.out | perl -pe 's/(DEBUG)/\e[1;34m$1\e[0m/g' 多个关键词高亮显示: tail -f catalina.out | perl -pe 's/(关键词1)|(关键词2)|(关键词3)/\e[1;颜色1$1\e[0m\e[1;颜色2$2\e[0m\e[1;颜色3$3\e[0m/g' tail -f catalina.out | perl -pe 's/(DEBUG)|(INFO)|(ERROR)/\e[1;34m$1\e[0m\e[1;33m$2\e[0m\e[1;31m$3\e[0m/g'
字体颜色设置: 30m:黑 31m:红 32m:绿 33m:黄 34m:蓝 35m:紫 36m:青 37m:白 背景颜色设置:40-47 黑、红、绿、黄、蓝、紫、青、白 其他参数说明 [1; 设置高亮加粗 [4; 下划线 [5; 闪烁