上一部分我们了解了Rasa NLU处理用户输入的过程,接下来将介绍Rasa Core在接收到NLU处理的用户意图之后,如何进行下一步操作的。

Rasa Core 基础结构

首先来看一下Rasa Core的基础架构和信息流图:

Core_Strucure

可以看出,Rasa Core主要包含的有如下几个大模块,它们各自作用不同:

  1. Agent:这个模块是Rasa核心功能的承载部分,其他模块初始化,模型存储与加载,客户信息输入输出的通道,都是Agent模块来负责的;
  2. MessageProcessor:这个模块是对输入信息处理的核心模块,Agent接收输入信息后的所有处理过程都是在这个模块进行的;
  3. Tracker:这个模块说白了,就是本轮对话的状态仓库,无论是NLU解析返回的内容,还是Action执行的内容,都需要存储到Tracker中;
  4. Policy:这个模块是策略相关模块,内置了三大类策略,根据Tracker的状态来预测下一个要执行的Action;
  5. Action:这个模块包罗万象,Rasa将一轮对话中可能遇到的所有条件都抽象成了Action来进行操作;
  6. NLG:这个模块顾名思义,就是产生给用户的回复信息,不过不要太寄予厚望,这个NLG功能非常简单,并不像想象中的智能。

接下来我们将会对上述几个模块分别进行介绍(建议结合代码看)。

Agent模块

Agent模块为Rasa提供模型训练、信息处理、模型加载、action预测、输出通道处理功能。在代码中体现的就是很简单的两个主要功能:

  1. 创建MessageProcessor、创建NLG、创建Tracker Store、创建Lock Store;
  2. 接收用户message,然后调用MessageProcessor去解析Message。

以及一些便于API调用的其他功能,如根据用户id或tracker预测下一步Action,执行一个action等内容,相对主流程来说不算重要,暂且不提。

MessageProcessor模块

这个模块是Rasa Core的重头戏之一,它提供了用于对话机器人沟通的主接口,在代码中,重点是如下几个接口:

  1. handle_message:万恶之源,所有的初始信息被送入此地,首先调用parse_message函数,并根据解析结果更新Tracker,如果有识别到的意图,则会对其进行槽位提取并填充,随后调用_run_prediction_loop开始进行内部状态循环,直到聆听状态;
  2. parse_message:消息处理的第一关,会调用NLU模块解析输入的消息,返回解析后的字典结构,包含消息的意图、包含的实体以及意图排序,比如说我们输入一个“hi”,系统会返回对应的识别结果:parse_message
  3. _run_prediction_loop:这个函数会循环判断是否需要预测下一个action以及是否需要对消息进行处理,在这里会调用predict_next_with_tracker_if_should来对当前状态进行预测,以获得下一个需要行动的Action,接下来会执行 _run_action函数来判断是否跳出本次循环;
  4. predict_next_with_tracker_if_should:这个函数首先判断是否达到了本次对话轮次数,若没有达到,会调用_predict_next_with_tracker进行预测;
  5. _predict_next_with_tracker:这个函数会去调用PolicyPrediction模块,获得预测结果,可见策略预测模块的RulePolicy预测出了最高置信度的Action应该是第22个index的Action,这里的index可以在导入的domain-action_names_or_texts中获得,也就是’utter_greet’这个action:PolicyPrediction
  6. _run_action:当上一步获得了要执行的action,在这一个函数中就可以执行对应Action的操作,包括BotResponse,Listen等等,而执行结束后也不会一走了之,需要继续对tracker进行执行状态的更新,并预测下一步要执行的action,继续3-6的循环,直至action被置为Listen状态为止。

除了上述几个主要函数,还有一些关于任务提示器reminders,通过输出channel将机器人回复推送给用户等功能,也都包含在这个模块中,可以按需查看源码。

Tracker模块

全称为DialogueStateTracker,顾名思义,一轮对话的状态记录仪,其功能也是关于各个状态增删查询:

Tracker

重点关注如下内容:

  1. events:在Rasa中,任何和对话相关的操作都会被抽象成Event存于此处,可以看出,从对话Session启动开始,到用户输入,机器人回复,监听状态等一系列操作事件都被记录于此;
  2. latest_action:这里记录了最近一次执行的action;
  3. latest_bot_utterance:这里记录了最近一次机器人的回复;
  4. latest_message:这里记录了最近一次用户的输入信息,包含用户意图,会话id等内容;
  5. slots:这里给出需要填充的一系列槽位信息。

至于函数功能,主要就是一个update不断更新Events队列,其他的也是和查询相关的功能居多。

Policy模块

Policy模块负责对话策略管理,也是算法相关的重点模块,Rasa内置了3个对话策略,分别为:

  1. 规则策略(rule_policy):基于规则设置的策略,拥有最高的权重,依据手动配置的rules.yml来做判断,当满足条件时,对应action的置信度设为1,否则设为0;
  2. 记忆策略(memorization_policy):这个策略是从stories.yml中学习而来,如果当前对话匹配stories中曾经配置过的对话信息,则会将对应的action置信度直接设为1,否则设为0。在这个配置中会有max_history超参数,代表story可被切分的对话轮次数;
  3. TED策略(ted_policy):全称为基于Transformer嵌入的对话管理策略(Transformer Embedding Dialogue Policy),用于下一步Action预测和实体识别任务,NER任务依旧是通过Transformer+CRF完成,而Action预测通过将Transformer的输出映射到另一个embedding,同目标label的embedding计算余弦相似度获得。
  4. 可能的几个小问题:
    1. TED模型的训练数据是什么样子的?在Rasa中,会将Story抽成[user, prev_action]对,从0到max_history次数不等,而要预测的目标则是对应的action;
    2. 这些数据怎么转换成向量的?这些数据会被转换成intent/action_name长度的一维one-hot向量,随后通过scipy转换为稀疏特征向量;
    3. intent和action_name长度不一样的话,维度不一致,怎么做到一致的呢?答案是,分开处理,在这里,用户的输入信息会被送入单向用户信息处理Transformer,而之前的action信息会被送入单向机器人信息处理Transformer,最后将两个feature concat之后送入下一级Transformer;
    4. 这些特征之间是怎么映射起来的?通过StarSpace方法,将对话特征与action特征联合建模;
    5. 推理的时候怎么做的呢?计算所有action与对话特征之间的相似度,选择相似度最高的action返回。
    6. UnexpecTEDIntentPolicy也是一种策略吗?看名字可以看出,这个策略使用的和TED是一类模型,但是其主要目的不在于预测策略,而在于处理用户输入信息不符合预期的情况,唯一触发action_unlikely_intent。
Action模块

Action是Rasa对用户信息处理的标准操作方法,Rasa内部定义了许多类型的Action,以供维护对话状态使用,其中内置的有如下Action:

  1. action_session_start:只在一通对话刚刚开始时设置,并重置对话Tracker,也支持重写该Action以在对话开始时发送消息给用户;
  2. action_listen:让机器人停止其他操作,等待用户输入;
  3. action_restart:接收到/restart指令时,重置本轮对话,清除历史记录;
  4. action_default_fallback:当开启兜底机制,并且所有的意图预测置信度都非常低的情况下会被触发,发送兜底消息给用户;
  5. action_deactivate_loop:跳出激活的active loop,重置槽位,当表格填充时出现意外(非预想场景)时会有这种情况;
  6. action_two_stage_fallback:在处理低置信度场景时可以启用,会不断向用户提出澄清意图的请求;
  7. action_default_ask_affirmation:当action_two_stage_fallback启用时,会向用户请求澄清意图,若用户确认时返回该Action;
  8. action_default_ask_rephrase:当action_two_stage_fallback启用时,会向用户请求澄清意图,若用户不确认时返回该Action;
  9. action_back:当输入/back指令时,撤销上一轮用户-机器人的对话;
  10. action_unlikely_intent:只会由UnexpecTEDIntentPolicy触发,
  11. action_extract_slots:针对表格填充使用,更新对话的槽位信息。

除此之外,对用户的各种回复信息则属于Responses系列action,以utter_作为开头,在domain.yml中的responses定义。

最后,还有自定义action,继承rasa_sdk的Action类即可,你想要的,只要能写出来,都可以应写尽写,存放于actions文件夹,并在domain.yml定义,需要注意:自定义的action需要action server启动才会被调用到!

NLG模块

依据对话状态产生机器人回复信息,一句random_response解君愁,它会很贴心的在你配置好的responses中随机挑选一句返回用户。

总结

至此,我们一起学习了Rasa Core的结构,以及内部的一些算法,这样,Rasa最重要的两个部分,NLU和Core就大致看完了,下一节会对一些细节进行补充,并介绍部分3.x版本和2.x版本的不同点。