1. 对话管理
1.1 多轮对话
 多轮对话是相对于单轮对话而言的,单轮对话侧重于一问一答,即直接根据用户的问题给出精准的答案。问答更接近一个信息检索的过程,虽然也可能涉及简单的上下文处理,但通常是通过指代消解和 query 补全来完成的,而多轮对话侧重于需要维护一个用户目标状态的表示和一个决策过程来完成任务,具体来说就是用户带着明确的目的而来,希望得到满足特定限制条件的信息或服务,例如:订餐,订票,寻找音乐、电影或某种商品等。因为用户的需求可以比较复杂,可能需要分多轮进行陈述,用户也可能在对话过程中不断修改或完善自己的需求。此外,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。因此,任务驱动的多轮对话不是一个简单的自然语言理解加信息检索的过程,而是一个决策过程,需要机器在对话过程中不断根据当前的状态决策下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求,等等)从而最有效的辅助用户完成信息或服务获取的任务。

注:任务驱动的多轮对话系统通常为封闭域(domain)(闲聊系统为开放域),而特定限制条件对应于槽(Slot),也就是说,用户在满足特定限制条件时就是一次槽值填充的过程,如果用户能够在一次会话中,满足全部的限制条件,那么就不必进行多轮对话,即可直接使用户得到满意的信息或服务。

1.2 对话管理
 对话管理,即Dialog Management(DM),它控制着人机对话的过程,是人机对话系统的重要组成部分。DM会根据NLU模块输出的语义表示执行对话状态的更新和追踪,并根据一定策略选择相应的候选动作。 简单来说,就是DM会根据对话历史信息,决定此刻对用户的反应,比如在任务驱动的多轮对话系统中,用户带着明确的目的如订餐、订票等,用户需求比较复杂,有很多限制条件,可能需要分多轮进行陈述,一方面,用户在对话过程中可以不断修改或完善自己的需求,另一方面,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。如下图所示,DM 的输入就是用户输入的语义表达(或者说是用户行为,是 NLU 的输出)和当前对话状态,输出就是下一步的系统行为和更新的对话状态。这是一个循环往复不断流转直至完成任务的过程。

 从本质上来说,**任务驱动的对话管理实际就是一个决策过程,系统在对话过程中不断根据当前状态决定下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求等),从而最有效的辅助用户完成信息或服务获取的任务。**对话管理的任务大致有:

对话状态维护(dialog state tracking, DST)
 对话状态是指记录了哪些槽位已经被填充、下一步该做什么、填充什么槽位,还是进行何种操作。用数学形式表达为,t+1 时刻的对话状态S(t+1),依赖于之前时刻 t 的状态St,和之前时刻 t 的系统行为At,以及当前时刻 t+1 对应的用户行为O(t+1)。可以写成S(t+1)←St+At+O(t+1)。

生成系统决策(dialog policy)
 根据 DST 中的对话状态(DS),产生系统行为(dialog act),决定下一步做什么 dialog act 可以表示观测到的用户输入(用户输入 -> DA,就是 NLU 的过程),以及系统的反馈行为(DA -> 系统反馈,就是 NLG 的过程)。

作为接口与后端/任务模型进行交互

2. Rasa Core

2.1 Stories
 Rasa的故事是一种训练数据的形式,用来训练Rasa的对话管理模型。故事是用户和人工智能助手之间的对话的表示,转换为特定的格式,其中用户输入表示为相应的意图(和必要的实体),而助手的响应表示为相应的操作名称。Rasa核心对话系统的一个训练示例称为一个故事。这是一个故事数据格式的指南。两段对话样本示例:
 

<!-- ##表示story的描述,没有实际作用 -->
## greet + location/price + cuisine + num people
* greet
   - utter_greet
* inform{"location": "rome", "price": "cheap"}
   - action_on_it
   - action_ask_cuisine
* inform{"cuisine": "spanish"}
   - action_ask_numpeople    
* inform{"people": "six"}
   - action_ack_dosearch
  
<!-- Form Action-->
## happy path 
* request_weather
   - weather_form
   - form{"name": "weather_form"}
   - form{"name": null}

Story格式大致包含三个部分:

  • 1. 用户输入User Messages

使用*开头的语句表示用户的输入消息,我们无需使用包含某个具体内容的输入,而是使用NLU管道输出的intent和entities来表示可能的输入。需要注意的是,如果用户的输入可能包含entities,建议将其包括在内,将有助于policies预测下一步action。这部分大致包含三种形式,示例如下:

(1)* greet 表示用户输入没有entity情况;
(2)* inform{"people": "six"} 表示用户输入包含entity情况,响应这类intent为普通action;
(3)* request_weather 表示用户输入Message对应的intent为form action情况;

2. 动作(Actions)
 使用-开头的语句表示要执行动作(Action),可分为utterance actions和custom actions,其中,前者在domain.yaml中定义以utter_为前缀,比如名为greet的意图,它的回复应为utter_greet;后者为自定义动作,具体逻辑由我们自己实现,虽然在定义action名称的时候没有限制,但是还是建议以action_为前缀,比如名为inform的意图fetch_profile的意图,它的response可为action_fetch_profile。

3. 事件(Events)
 Events也是使用-开头,主要包含槽值设置(SlotSet)和激活/注销表单(Form),它是是Story的一部分,并且必须显示的写出来。Slot Events和Form Events的作用如下:

(1)Slot Events

 Slot Events的作用当我们在自定义Action中设置了某个槽值,那么我们就需要在Story中Action执行之后显著的将这个SlotSet事件标注出来,格式为- slot{"slot_name": "value"}。比如,我们在action_fetch_profile中设置了Slot名为account_type的值,代码如下:
 

from rasa_sdk.actions import Action
from rasa_sdk.events import SlotSet
import requests

class FetchProfileAction(Action):
    def name(self):
        return "fetch_profile"

    def run(self, dispatcher, tracker, domain):
        url = "http://myprofileurl.com"
        data = requests.get(url).json
        return [SlotSet("account_type", data["account_type"])]