rules
是一种基于规则的对话流程定义方式,它的作用是根据用户输入的意图和实体,触发相应的机器人响应。每一次用户的输入都会被独立处理,与之前的输入没有关系,因此在 rules
中是没有上下文的。
特别说明:使用rules定义的action,在action方法里面是获取不到槽的。需要使用槽,请使用基于stories的对话流程定义方式。
为了说明rules和stories的区别,我们先使用rules来定义对话:
rules定义对话:
定义意图:
- intent: query_weekday
examples: |
- [今天](date_time)星期几?
- [明天](date_time)星期几?
- [昨天](date_time)星期几?
- [今天](date_time)星期几?
- 星期几?
定义rules
- story: ask weekday path
steps:
- intent: query_weekday
entities:
- date_time: 今天
- action: action_query_weekday
定义domain
intents:
- bot_challenge
- query_weekday
entities:
- date_time
slots:
date_time:
type: text
influence_conversation: false
mappings:
- type: from_entity
entity: date_time
actions:
- action_query_weekday
config.yml
# The config recipe.
# https://rasa.com/docs/rasa/model-configuration/
recipe: default.v1
# Configuration for Rasa NLU.
# https://rasa.com/docs/rasa/nlu/components/
language: zh
pipeline:
- name: JiebaTokenizer
- name: LanguageModelFeaturizer
model_name: bert
model_weights: bert-base-chinese
- name: "DIETClassifier"
epochs: 100
# learning_rate: 0.001
# intent_classification: true
# entity_recognition: true
# - name: ResponseSelector
# epochs: 100
# Configuration for Rasa Core.
# https://rasa.com/docs/rasa/core/policies/
policies:
- name: MemoizationPolicy
- name: TEDPolicy
max_history: 5
epochs: 200
- name: RulePolicy
编写action
在actions目录下,创建queryWeekday.py
from typing import Any, Text, Dict, List
from datetime import datetime,timedelta
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
def text_date_to_int(text_date):
if text_date=="今天":
return 0
if text_date =="明天":
return 1
if text_date=="昨天":
return -1
# in other case
return None
weekday_mapping = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
def weekday_to_text(weekday):
return weekday_mapping[weekday]
class ActionQueryWeekday(Action):
def name(self) -> Text:
return "action_query_weekday"
def run(self, dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
# 如果是使用rules定义的对话,想要使用实体,可以使用tracker.latest_message["entities"]来找
# 但是rules定义的,tracker.get_slot("XX")无法使用,因为rules不存在上下文
text_date_entity = next((entity for entity in tracker.latest_message["entities"] if entity['entity'] == 'date_time'), None)
if text_date_entity:
text_date = text_date_entity['value']
# 使用stories定义的对话,可以使用tracker.get_slot("XX")获取词槽,因为stories就是为多轮对话服务的,前提是slot需要在domain中定义
# text_date = tracker.get_slot("date_time")
int_date = text_date_to_int(text_date)
if int_date is not None:
delta = timedelta(days=int_date)
current_date = datetime.now()
target_date = current_date + delta
dispatcher.utter_message(text=weekday_to_text(target_date.weekday()))
else:
dispatcher.utter_message(text="系统暂不支持'{}'的星期查询".format(text_date))
return []
action方法说明:
def name(self) -> Text:
return "action_query_weekday"
这个name方法定义的返回值,要和domain.yml里面一致,Rasa是根据这个来找对应的action动作的。
测试:
训练模型
rasa train
启动actions动作服务器,另外开一个窗口执行:
rasa run actions
使用shell测试
rasa shell
rasa shell --debug 启动,可以看到debug日志,方便排查问题。
Your input -> 明天星期几
系统暂不支持'None'的星期查询
rasa train nlu 测试意图和实体正常识别
{
"text": "今天星期几",
"intent": {
"name": "query_weekday",
"confidence": 0.7667776942253113
},
"entities": [
{
"entity": "date",
"start": 0,
"end": 2,
"confidence_entity": 0.7740038633346558,
"value": "今天",
"extractor": "DIETClassifier"
}
],
....
说明虽然意图和实体已经识别出来了,但是使用rules定义的对话,在aciton中获取不到槽,
text_date = tracker.get_slot("date")获取的结果为空,
只能使用tracker.latest_message["entities"],获取实体
print(tracker.latest_message["intent"])
#结果:{'name': 'query_weekday', 'confidence': 0.4339539706707001}
print(tracker.latest_message["entities"])
#结果:[{'entity': 'date', 'start': 0, 'end': 2, 'confidence_entity': 0.608760416507721, 'value': '明天', 'extractor': 'DIETClassifier'}]
stories定义对话:
下面,我们把rules定义的改到stories定义中。
注意:删掉默认的story,不然会影响到我们自己的query_weekday故事。
version: "3.1"
stories:
- story: greet path
steps:
- intent: greet
- action: utter_greet
- story: ask weekday path
steps:
- intent: query_weekday
entities:
- date_time: 今天
- action: action_query_weekday
在修改action,使用slot直接获取槽值:
# 如果是使用rules定义的对话,想要使用实体,可以使用tracker.latest_message["entities"]来找
# 但是rules定义的,tracker.get_slot("XX")无法使用,因为rules不存在上下文
# text_date_entity = next((entity for entity in tracker.latest_message["entities"] if entity['entity'] == 'date'), None)
# if text_date_entity:
# text_date = text_date_entity['value']
print(tracker.latest_message["intent"]) #结果:{'name': 'query_weekday', 'confidence': 0.4339539706707001}
print(tracker.latest_message["entities"]) #结果:[{'entity': 'date', 'start': 0, 'end': 2, 'confidence_entity': 0.608760416507721, 'value': '明天', 'extractor': 'DIETClassifier'}]
# 使用stories定义的对话,可以使用tracker.get_slot("XX")获取词槽,因为stories就是为多轮对话服务的,前提是slot需要在domain中定义
text_date = tracker.get_slot("date_time")
再重新训练和测试,
遇到action不执行的情况,可以删掉自己手写的部分,使用交互式学习来让rasa帮我们生成,然后再修改即可,命令如下:
rasa interactive