2.0横空出世,之前写的项目需要升级,趁此机会边用边整理下rasa一些技术细节,自己看的,不要纠结格式。(内容可能包含原1.x部分)
(1)story和rule
新加了一个rule,与story区别:
rule用于单轮对话的固定规则,core部分训练会校验是否只有一个user_uttered
rule是固定规则(整合了原来的trigger intent),如果需要根据前文灵活设置对话线,还是要用story
(2)State
代表tracker的状态,键值对表示,由tracker转换而来,key和value如下,rulepolicy和memorypolicy时(self.lookup)作为获取cation的key(后者将key压缩并base64转码,前者原值存储)
# State is a dictionary with keys (USER, PREVIOUS_ACTION, SLOTS, ACTIVE_LOOP)
# representing the origin of a SubState;
# the values are SubStates, that contain the information needed for featurization
SubState = Dict[Text, Union[Text, Tuple[Union[float, Text]]]]
State = Dict[Text, SubState]
state = {
rasa.shared.core.constants.USER: self._get_user_sub_state(tracker),
rasa.shared.core.constants.SLOTS: self._get_slots_sub_state(tracker),
rasa.shared.core.constants.PREVIOUS_ACTION: self._get_prev_action_sub_state(
tracker
),
rasa.shared.core.constants.ACTIVE_LOOP: self._get_active_loop_sub_state(
tracker
),
}
样例
<class 'dict'>: {'prev_action': {'action_name': 'form_search_person_by_car_no'}, 'active_loop': {'name': 'form_search_person_by_car_no'}, 'user': {'intent': 'stop'}}
(3)trackers_as_states和trackers_as_actions的获取
tracker->states->取每个ActionExecuted(因为预测的就是action)的前max_history个states->sliced_states->需要的话通过hash去重->sliced_states添加到trackers_as_states、当前event.action添加到trackers_as_actions
trackers_as_states :List[List[State]]
trackers_as_actions : List[List[Text]]
(4)form rejected
源码逻辑:form启动之后(2.0表示为activate_loop),追问过程中用户没有提供任何有用的slot,则进入一轮rejected,由其他policy预测下一步动作。如果在故事或rules中又显式启动当前form,在执行form动作之后update ActionExecuted event时会重置form的validate状态为true、rejected状态为false。(继续当前form)
(5)关于story和rules中的active_loop:xxxx
从1.x开始这里就不是很明白,最近重新研究下,可能不一定准确,仅供参考
- action: form_xxx # 启动form
- active_loop: form_xxx # 在故事或rules中增加ActiveLoop event,表示启动
- active_loop: null # 在故事或rules中增加ActiveLoop event,表示停止
- action: action_submit # form获取信息后,完成后续动作
官网解释是说训练过程中不会调用action server,所以action返回的event需要手动填写到story或rules中。
实测:
a 训练时如果故事如上例,form中间没有其他流程时(仅有form自身的追问过程,且happy path),后面两个active_loop写与不写,对话时流程都会走到最后的submit。因为memony和rule policy在训练时生成的trackers_as_states不会有{'active_loop':xxxx}状态(form从开始到结束没有被中断,在生成states时是一步完成),对话的tracker同样不会产生这一状态,故可匹配。
b 当训练过程中form中间包含unhappy path时,如
- action: form_xxx # 启动form
- active_loop: form_xxx # step1
- intent: stop # unhappy path
- action: utter_stop #
- action: form_xxx # 重新回到form
- active_loop: form_xxx # step2
- active_loop: null # step3
- action: action_submit # form获取信息后,完成后续动作
若step1注调,则无法匹配后面的unhappy path,因为实际对话tracker此时会产生含有{'active_loop':xxxx}的状态(因为form没有连续一次性完成),与训练的states无法匹配。
step2和3注调任何一个均会产生故事冲突,无法通过验证(此处逻辑尚未明确确定,初步结果是rule预测的action为listen而story预测结果为action_submit)
总之:设置slot和启动/停止form两种event会形成对应states,在写故事和rule时,需要在对应action后面添加到step中
(6)rule中的lookup
和memony policy中的lookup类似,lookup['rule']是rule训练数据中的step逻辑
而lookup['rules_for_loop_unhappy_path']则是处理loop过程中的unhappy path的情况,有do_not_validate_loop和do_not_predict_loop_action两个类型,分别在特定状态下跳过validate和predict当前loop环节(因为loop过程中需要处理story中的其他action和intent,不需要遵从当前form的loop)。
注意,loop在activate状态下,如果没有rejected,下一轮对话rule policy会优先预测当前form的action。
(7)loop中的unhappy_lookup
rule policy训练部分一段代码
for states, actions in zip(trackers_as_states, trackers_as_actions):
action = actions[0]
active_loop = get_active_loop_name(states[-1])
# even if there are two identical feature keys
# their loop will be the same
if not active_loop:
continue
states = self._states_for_unhappy_loop_predictions(states)
feature_key = self._create_feature_key(states)
if not feature_key:
continue
# Since rule snippets and stories inside the loop contain
# only unhappy paths, notify the loop that
# it was predicted after an answer to a different question and
# therefore it should not validate user input
if (
# loop is predicted after action_listen in unhappy path,
# therefore no validation is needed
is_prev_action_listen_in_state(states[-1])
and action == active_loop
):
lookup[feature_key] = DO_NOT_VALIDATE_LOOP
elif (
# some action other than active_loop is predicted in unhappy path,
# therefore active_loop shouldn't be predicted by the rule
not is_prev_action_listen_in_state(states[-1])
and action != active_loop
):
lookup[feature_key] = DO_NOT_PREDICT_LOOP_ACTION
unhappy代表循环中间有其他步骤,当循环已经是启动状态,从其他步骤回到form不用重新validate(最后一个状态前面动作是listen,说明form是被打断过的,最近这次用户输入也是对其他问题的回答,比如是否继续,用户说是,因此不用验证);最后一个状态前面动作不是listen,且预测动作不是当前循环,说明是当前路径的连续其他动作,不预测当前loop,注意,reject只是从当前form跳过一次,上面是policy层面不再进入当前form
(8)form中间追问是否影响memory policy
当form过程没有触发reject exception时(正常追问过程或重写form的validate方法),整个从form启动到结束的过程在memory策略的predict_action_probabilities(featurizer.prediction_states)中被忽略,也就是都归为正常追问(happy path)合并为一步。注意,实际tracker中依然记录的全部的每个交互步骤。
如果form被rejected或检测到接下来执行的并不是当前form(例如rule中增加的chichat/退出部分),则会被判定为unhappy路径,因此此前步骤不会忽略(featurizer.prediction_states),从而影响memory相关策略判定。
总结,happy path会忽略中间步骤,unhappy path不会忽略,reject状态很重要,如果非reject状态,rule policy会优先走happy path,编写的unhappy rule因此也不会起作用。