一、rasa的工作原理
rasa3.0中,NLU和Core都作为有向无环图(DAG)计算的一部分进行编排。即所有的计算过程(也就是组件)都作为图的节点,计算过程之间的依赖关系通过有向边表示。所有的计算过程和依赖关系就构成了一个DAG。
在训练和预测时使用两个不同的DAG。下面按照组件的两个阶段(训练阶段和推理阶段)解释组件的工作原理。
1.1 训练阶段:
模型训练源码:rasa.model_training.train()函数,函数调用其他代码完成训练。逻辑上说,训练阶段有三个步骤:
- 从配置文件和参数中构造有向无环图
- 运行有向无环图,这是模型实际进行训练和持久化的过程
- 将元数据连同(通过持久化过程形成的)组件文件打包成模型
1.1.1 构造DAG
根据配置文件(config.yml)和参数(通过调用train命令时的命令行制定)来构建DAG。
构造DAG的过程涉及多个类的逐层抽象和转换,总体流程比较复杂。
- 核心代码位于:rasa.engine.recipes.default_recipe.DefaultV1Recipe.graph_config_for_recipe()方法中。
- 其输入是来自config.yaml的配置信息。
- 输出是图的配置信息,包含图中所有节点的定义、rasa NLU的输出目标和rasa Core的输出目标。①节点的定义包含了节点对应的组件信息、组件的入口方法和该组件对其他组件的依赖关系
rasa的训练和推理使用不同的DAG,在构造训练DAG的同时,推理DAG也会被构造,并作为元信息的一部分写入模型包。
1.1.2 运行DAG
运行DAG的过程即为模型训练和持久化的过程。
- 核心代码位于:rasa.engin.runner.dask.DaskGraphRunner.run()方法。
- 其输入是DAG,
- 输出是目标的值,即我们所需的结果。
运行DAG时,会一一运行每个节点:调用节点所存储的组件的对应方法,完成其功能。功能包含:载入资源、训练和进行推理。在载入资源和训练功能中需要组件完成组件的系列化,这样就可以写入磁盘供推理使用
1.1.3 打包模型
将运行DAG过程中的产物写入目录,从而创建rasa模型文件。
- 核心代码:rasa.engine.storage.local_storage.LocalModelStorage.create_model_package()
- 首先将各个组件序列化后的资源文件和配置文件拷贝进工作目录,并将包含模型训练和推理的2个DAG的元信息序列化成文件,拷贝进工作目录,最后将工作目录压缩为单一文件,即为rasa模型文件
1.2 推理阶段
推理阶段工作函数:rasa.core.run.serve_application()函数。逻辑上讲推理阶段有三个步骤:
- 建立connector,对外提供访问接口
- 从磁盘载入模型(DAG)
- 处理用户消息
1.2.1 建立connector
connector是rasa对外提供服务的一种接口机制。用户客户端与connector通信从而实现和rasa机器人沟通。
rasa服务器(推理功能)是基于sanic(类似于flask的高性能一步web框架,具有高扩展能力)实现。
rasa中,每个connector都是可插拔的sanic扩展。
rasa按照配置(credentials.yml)在启动时候载入这些connector。
载入connector的核心代码在rasa.core.run.create_http_input_channels()
1.2.2 载入模型
rasa的模型是以DAG组织的。rasa从模型的元信息中还原推理用的DAG(在训练阶段生成)。
载入DAG的核心代码:rasa.engine.loader.load_predict_graph_runner()
1.2.3 处理用户消息
处理用户消息的核心代码:rasa.core.agent.Agent.handle_message()。
逻辑上讲,处理过程分为2个阶段:①自然语言理解(NLU),即从用户的消息中提取意图和实体;②对话管理(DM),由Rasa Core负责,这部分人Rasa会根据上下文选择合适的动作进行执行。
Rasa NLU主要函数:rasa.core.processor.MessageProcessor.log_message(),它会根据DAG得到NLU结果,这些结果更新tracker的状态,随后按照词槽(slot)的映射(mapping)配置更新slot的值,并更新tracker的状态。这一过程由rasa.core.processor.MessageProcessor.run_action_extract_slots()方法实现。
最后Core把tracker的状态作为输入,通过运行DAG预测下一步要执行的动作并执行这一动作。
动作的执行可能会带来新的tracker状态,因此“预测-执行”的过程可能会循环多次,直到满足停止条件。这一过程核心代码在rasa.core.processor.MessageProcessor._run_prediction_loop()