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; 闪烁

  

 

 

posted @ 2020-01-02 13:11  这里有个博客  阅读(6860)  评论(1编辑  收藏  举报