编辑推荐: |
本文主要介绍了
SaaS 平台架构的基本设计,融入了一些思考过程等内容。
来自于infoq,由火龙果软件Anna编辑、推荐。 |
|
1. 前言
随着自然语言处理和智能语音识别技术的发展,智能会话机器人开始部分替代人工客服。
网上关于 NLP 算法的文章有很多,但关于 Chatbot 架构的却很少,关于 Chatbot SaaS
平台架构的则更少。我是一名对机器学习感兴趣的程序员,更关注如何设计实现一个架构良好的 Chatbot
SaaS 平台,因此写下了这篇文章。
2. Chatbot 架构
2.1. 智能会话机器人的分类
智能会话机器人按照对话轮次来划分,可以分为单轮对话机器人和多轮对话机器人;按照知识领域来划分,可以分为限定域机器人和开放域机器人;按照任务类型来划分,可以分为任务型机器人、问答型机器人、闲聊型机器人和融合型机器人。
任务型:根据用户给出的信息完成指定的任务。一般限定于某个垂直领域,常采用多轮对话的形式。如订餐、订票等服务。
问答型:为用户提出的事实型、布尔型、计算型、推理型等类型的问题自动给出答案,进一步还可以再划分为常见问题解答(FAQ)和知识图谱问答(KBQA),一般限定于某个特定知识领域。
闲聊型:与用户进行开放式聊天,满足用户的情感陪护需求。一般不限定话题范围,但有可能偏向于某个领域。
融合型:一般以任务型或问答型为主,融合闲聊功能。如对于电商客服机器人来说,能完成商品推荐购买任务,能回答保修政策问题,还能陪客户闲聊天,融合型是应用场景越来越复杂的产物。
从应用的发展趋势来看:单轮对话——>多轮对话,以获得更多更完整的信息;单领域——>多领域,以满足用户更多层面的需求。
2.2. 任务型机器人交互模型
企业应用中最常见的需求是任务型机器人,上图是任务型机器人经典的交互模型。其中红色框为文字语音转换部分,这里暂不作讨论。
2.2.1. 自然语言理解(NLU)
领域识别(Domain Identification)
检测用户输入内容所涉及的领域概念。
意图识别(Intent Detection)
检测用户在特定领域下表述内容所代表的意图。
词槽填充(Slots Filling)
收集用户在对话过程中任务所必需的关键信息。
2.2.2. 对话管理(DM)
对话状态追踪(Dialogue State Tracking,DST)
记录和判断当前会话处于任务的何种状态。
对话策略(Dialogue Policy,DP)
根据对话状态决定下一步的系统行为。
2.2.3. 自然语言生成(NLG)
自然语言生成主要有两种方法:基于模板生成和基于模型生成。
基于模板生成:调用设计人员预先设计的回复模板,根据前置阶段获取到的状态和关键信息生成自然语言回复给用户。优点是响应速度较快,缺点是语言表达不够丰富,需要人工定义模板规则。因此比较适合在响应动作较少时使用,或是在系统冷启动时使用。
基于模型生成:通常使用 seq2seq 模型学习大量交互语料数据,根据用户输入直接生成相关自然语言回复给用户。优点是不需要人工定义模板规则,语言表达较为丰富;缺点是回复结果不太可控,响应速度较慢,需要学习大量的交互语料数据。因此比较适合系统资源充足且有大量交互数据时采用。
2.3. 组件集成(pipeline)
任务型机器人的系统设计中,通常采用 pipeline 方式来集成各个组件。
pipeline 的组件构成根据采用的方法不同而不同。有可能整个 pipeline 由三个组件构成,NLU、DM、NLG
分别为一个模型;也可能整个 pipeline 由两个组件构成,NLU + DM 为一个模型,NLG
是一个模型;也有可能整体仅有一个组件,NLU+DM+NLG 是一个整体的模型(end2end)。
某些情况下,为了提高响应速度或实现条件判断,还可能是一个分支流(如下图所示)。
总之,pipeline 中组件构成是不固定的,从系统设计的角度来看,可以将
pipeline 看成是一个整体模块(如下图所示)。
这种情况下,问答机器人、闲聊机器人、融合型机器人和任务型机器人都是接受输入返回输出。即无论何种类型,Chatbot
的外层结构保持不变,变化的只是 pipeline 的内部结构。再进一步抽象,其实可以通过在配置界面拖拽相关组件连接成
pipeline 来创建新的会话机器人类型。
2.4. Chatbot 基本架构
上图中蓝色部分是内部组件,灰色部分是外部服务。
对于生产级别的 Chatbot,除了要考虑算法性能,还要考虑并发性能,还有集群模式部署,会话状态共享,会话事件分析,会话交互安全……等等。
Conversation(会话交互):接受会话消息,异步响应消息(既支持接受 MQ 消息,也支持
RESTful API)。
Metrics api(度量接口):用于系统状态采集和健康检查。
Authentication(安全校验):对请求进行安全校验。
pipeline(组件流水线):用于集成算法模型。
Event Support(事件支持):与 Event Broker 交互,发送或监听会话事件。
Tracker Manager(会话状态管理):会话状态更新和保存。
Lock Helper(分布式锁):集群部署时锁定会话状态。
Action Support(动作调用):调用系统预定义动作和自定义动作。
Model Support(模型调用):调用指定的算法模型服务。
————————
Event Broker(会话事件中间件):提供会话事件中转能力,由消费者订阅相关事件,会话事件主要用于数据分析。
Tracker Store(会话状态存储):提供会话状态的保存。
Lock Store(分布式锁存储):提供分布式锁服务。
Action Serving(自定义动作服务):自定义的动作响应,一般来说需要外部交互(查询数据库、调用第三方)的响应动作,则通过
Action Serving 来处理。
Model Serving(模型能力服务):已训练好的模型部署为独立的服务,用于提供模型服务能力。如
TensorFlow Serving,TorchServe 等。
2.5. 模型训练
在上一小节中,简单介绍了 Chatbot 的基础架构,这里再解释下算法模型的训练。
自从 2018 年 Bert 横空出世刷新各大榜单,同时发布了各种语言的预训练模型,NLP 领域就迎来了预训练模型的新魔法时代。
由于预训练模型一般采用的是通用语料数据,与具体的领域和任务无关。因此需要在预训练模型上采用领域相关或任务相关的无标注数据继续预训练,得到某个特定领域或任务相关的预训练模型,然后再在该预训练模型之上使用标注数据训练。
所以模型训练分为了两个主要步骤:预训练 ——> 训练。
名词解释:PTM(Pre-trained Model)预训练模型
如上图所示,有四个主要的组件:
Pretraining Task(预训练任务):从 Model File Service 获取预训练模型(PTM),从
NLP Data Service 获取无标注数据,预训练后得到领域相关或任务相关的预训练模型,最后将模型保存到
Model File Service。
Training Task(训练任务):从 Model File Service 获取领域相关或任务相关的预训练模型(PTM),从
NLP Data Service 获取标注数据,训练后得到正式模型,最后将模型保存到 Model File
Service。
Model File Service(模型文件服务):存储预训练模型、正式模型,需有版本管理和类别管理能力。
NLP Data Service(语料数据服务):存储未标注数据和标注数据,需有版本管理和类别管理能力。
Model Serving(模型能力服务):从 Model File Service 拉取模型并部署为独立服务。
没有最好的模型,只有更好的模型。所以,预训练和训练都是不断迭代的过程。
2.6. 小结
本节首先介绍了 Chatbot 的分类,然后介绍了任务型机器人的交互框架,接着探讨了如何围绕 pipeline
为核心抽象来设计 Chatbot 的基础架构,最后介绍了算法模型的训练过程。
这里之所以要先设定 Chatbot 的基本架构,是为了避免 SaaS 平台适配不同架构的 Chatbot,从而带来额外的无谓的复杂度。
接下来的章节里,我们将开始探讨 Saas 平台该如何来设计。
3. Chatbot SaaS 平台架构
对于单租户的机器人来说,开发维护运营是比较简单的。而多租户的平台架构,需要考虑的维度则复杂得多。我们先尝试着来思考下这些问题:
有哪些用户会使用这个平台?他们各自对这个平台的核心诉求是什么?他们通过什么方式来与平台进行交互?
如何创建和销毁 Chatbot?如何管理每个 Chatbot 的生命周期?
如何保证最终用户访问的就是指定的 Chatbot?
多个租户能否共用一个类型的 Chatbot?
3.1. 需求分析
3.1.1. 系统用户
有哪些用户会与 SaaS 平台发生交互呢?这里简单梳理了下,主要的用户类型如下图所示。
可能还会有财务人员、营销人员……等其他用户,对于这些更细化的角色和权限,后续根据业务需求通过 RBAC
模型来扩展即可。
3.1.2. 功能需求
上一小节我们粗粒度地列出了用户角色类型,那么,他们各自都会有哪些核心功能需求呢?
说明:
这里仅描述了用户的关键需求,目的是先确定 SaaS 平台的核心架构,这个核心架构应该是稳定而不会轻易变化的。
3.1.3. 特性需求
除了功能需求外,架构设计中需要特别关注的是特性需求——这决定了软件系统的品质。对于 Chatbot
SaaS 平台来说,需要特别考虑以下几点。
隔离性
SaaS 平台面向的是多租户,每个租户的对话数据不同,需要的响应能力也不同,因此需要做好租户之间的隔离。采用物理隔离,隔离性好,但资源要求高;采用逻辑隔离,隔离性弱,但资源要求低。如何在保证隔离性的同时尽可能地减少资源消耗,是产品层面和技术层面都需要着重考虑的问题。
扩展性
用户的功能需求是不固定的,因此架构设计中需要考虑哪些是易变的,哪些是不变的,预留出功能扩展能力。
安全性
SaaS 平台通常部署在云端,通过对外接口来提供服务。对于租户和用户来说,除了满足功能性需求,最关心的应该是安全性问题。接口服务应该有完善的鉴权和加密机制,数据安全方面应该有完善的加密和备份机制,同时服务器、数据库等物理资源应该有严格的权限管理。
兼容性
SaaS 平台的系统改进和功能扩展对于租户和用户来说应该是无感知的。代码升级,应该能兼容已有的对外接口和数据格式;算法升级,应该能保证原有会话流正常。
伸缩性
对于 SaaS 平台来说,用户量和访问量在运营过程中可能会增长或减少,一天之中的访问量会有高峰和低谷,某些时期可能会有数倍于平常的访问量,因此平台服务应该具备弹性伸缩能力。
自动化
SaaS 平台面向的是不断新增的租户,应该尽量减少人工参与的环节,能自动化的环节都应该尽可能实现自动化。最好的情况是,租户登录平台,上传语料数据,然后自动完成模型训练更新、机器人服务上线和按需弹性伸缩,SaaS
平台运营方无需人工参与。
3.1.4. 复杂度分析
自动化:
前置动作:模型预训练、模型训练、模型部署,租户能力集成,通用第三方能力集成;
运行动作:根据运行指标弹性伸缩和限流,包括 Chatbot 服务,模型能力服务、租户能力服务及依赖的相关组件。
结束动作:Chatbot 下线,模型下线,租户能力服务下线;
附加动作:数据分析、报表统计、自动告警。
对于 Chatbot SaaS 平台来说,自动化是必须实现的特性,也是最复杂的问题之一,因此在架构设计中会着重对自动化问题进行分解和处理。
成本因素:
Chatbot SaaS 平台有不断新增的租户,每个租户的会话流、语料数据和最终模型可能不同,如何用尽可能少的资源实现用户需求,这也是需要考虑的问题。
方向一:一个模型包含多个租户的不同场景需求,这种情况下需要模型有良好的领域识别能力,缺点是隔离性差且大模型的并发性能低。
方向二:一个进程中加载多个互不相关的 pipeline,然后根据业务规则分发到不同的 pipeline,缺点是
Chatbot 耦合了业务规则。
更好的方法应该是从产品角度进行优化,总结用户需求,抽象出公共能力,从而减少服务进程的创建。
算法性能和并发性能:
通常来说,复杂算法模型的算法性能好但并发性能低,因此需要找到算法性能和并发性能的平衡点。
3.1.5. 设计目标
与具体 Chatbot 类型和算法模型无关的 Chatbot SaaS 平台,这个平台应该是一个自动化的、安全的、响应式的系统。
3.2. 架构总览
3.2.1. 整体架构
如上图所示,这是一个标准的 DDD 四层模型(4 层 + 1 监控)。
在我过往的架构实践中,我通常将业务层分成管理端和应用端。
一是为了隔离变化,管理端和应用端各自的改动不会相互影响;二是性能要求不同,应用端一般与最终用户交互,用户数量多,并发性和实时性要求高,而管理端则与企业用户交互,用户数量较少,性能要求较低。
另外,为了实现自动化的特性需求,在这个 SaaS 平台中特别增加了调度端,主要是用来管理训练任务、测试任务和
Chatbot 的生命周期。
备注:
管理端和应用端无直接交互;管理端和调度端为异步交互(使用 MQ 解耦)。
3.2.2. 组件概览
接入层
负责与企业用户(管理端)和最终用户(应用端)的交互,需适配不同的业务接入方式。
授权:负责用户的访问授权。
企业用户获得授权后才能访问管理端,最终用户获得授权后才能访问会话网关,需根据业务场景提供不同的授权方式。
会话网关:接受会话请求,转换请求参数为 Router 接受的标准格式,封装返回结果。
管理 UI:管理界面,与企业用户交互,通常为 Web UI 的形式。
业务层
负责具体的业务编排,通过组合或聚合领域服务能力完成业务规则。
管理端:
语料数据、算法模型、会话流、机器人、机器人类型、租户……等的管理维护。
调度端:
接收管理端的异步消息,负责创建任务和资源调度。如果进一步细分,还可以拆分成训练调度、测试调度和生产服务调度。
应用端:
应用端被分为三个大的模块:训练模块、测试模块、生产模块。为避免计算资源争抢,三者的运行环境需作物理隔离。另外,一些算法模型需要使用
GPU,因此调度端需考虑如何正确合理地分配物理资源。
生产模块:正式对外提供的会话服务。
训练模块:分为预训练任务和正式训练任务,由调度端创建任务并分配物理资源。
测试模块:分为组件测试和集成测试,TestExecutor 负责执行具体的测试用例,生成测试报告并保存,最后可以在管理端
UI 进行呈现。为了进行完整的系统集成测试,测试模块有独立 Router。
领域服务层
负责实际的业务逻辑,有多个独立的领域服务。譬如用户服务、模型文件服务、语料数据服务……等。
基础设施层
缓存组件、持久化组件与及相关的中间件等外部系统。
监控系统
负责监控物理资源和性能指标,采集业务日志和运行日志,并提供数据分析查看和异常告警能力。对于微服务架构来说,系统监控是必要的组件。
3.2.3. 组件交互
如上图所示,红色线为会话线,绿色线为训练线,橙色线为测试线。
会话线
最终用户携带会话消息请求 Gateway(或租户服务器中转最终用户消息到 Gateway)。
Gateway 进行权限校验,校验通过后将消息转换为标准数据格式,并将会话消息转发给 Router。
Router 将会话消息根据业务规则转发给对应的 Chatbot。
Chatbot 异步调用 ModelServing 和 ActionServing,最终响应会话消息给用户。
最终用户请求 Gateway 之前需先访问 Authorization 获得授权;Gateway
的通信协议应支持双向通信,方便实现异步消息处理和推送;Router 需具备消息缓存能力,用户断线重连后可以推送未读消息。
特别需要注意的是,会话线的整个过程都应该是异步的。Gateway 和 Router 之间采用长链接机制,Router
和 Chatbot 之间则采用 MQ 来作为通信机制。
训练线
训练任务由管理端(…… Manager)发起,需先准备语料数据、算法模型和代码。
调度端(Scheduler)根据指令信息创建训练任务。
训练任务从 Model File Service 拉取算法模型,从 NLP Data Service
拉取语料数据,然后开始训练。
训练任务训练完成后将算法模型保存到 Model File Service,并通知 Scheduler
训练完成。
测试线
测试分为算法模型测试和系统功能测试;既可以对单个组件测试,也可以进行系统集成测试。
测试任务的创建也分为两种情况:一是由管理端人工发起的测试任务;二是模型训练完成自动触发的测试任务。
Scheduler 接收到创建测试任务的指令,分配物理资源并部署相关服务。
Scheduler 通知 TestExecutor 开始执行测试任务。
TestExecutor 测试完成后通知 Scheduler 并保存测试报告。
运营管理人员根据测试报告判断是否正式部署新的算法模型(或系统功能)。
备注:
为了更清晰地表达主要的业务线,省略了部分组件和交互关系。
3.3. 其它设计
3.3.1. Chatbot 的配置维度
对于 SaaS 平台来说,可供租户创建的 Chatbot 的类型应该是多种多样的。
功能
首先是可以按功能来配置,譬如可以分为任务型、问答型、闲聊型和融合型等。
语言
其次,算法模型常常与具体语言强相关,因此 Chatbot 也需要按语言来配置。但或许可以从另外一个角度去思考:Chatbot
能做到与具体语言无关吗?
一个方向是先将不同的源语言翻译成目标语言,然后基于目标语言再进行后续的会话流程,最后再将回复消息翻译成源语言。基于当前的机器翻译技术,这应该是比较简单可行的一个方向,缺点是会因为翻译精度损失算法性能。
另一个方向是首先做语言检测,然后调用源语言训练的算法模型,优点是算法性能较好,缺点是每一种算法模型都需要采用多种语言训练。
具体采用哪种方法,需要具体看业务场景。对于 SaaS 平台来说,无论哪种方法,都没有增加额外的复杂度,只是
pipeline 的编排配置。譬如在 Chatbot 的 pipeline 起始位置加上语言检测模型(机器翻译模型),pipeline
结束位置加上机器翻译模型。
领域
最后,还应该根据领域来配置。如前所述,为了提高算法性能,通常会采用领域相关的语料进行预训练,一个大的领域下还可以继续细分子领域。通常来说领域数据越多分得越细则模型的算法性能会越好,领域数据和模型是
Chatbot SaaS 平台的核心竞争力之一。
在 SaaS 平台的管理界面,租户首先可以根据自己的业务场景选择不同的维度进行组合配置,然后编排会话流程和上传语料数据,最终构建得到属于自己的
Chatbot 类型。SaaS 平台为该 Chatbot 类型分配 ID 标识,最终用户携带这个 ID
访问会话网关,Router 可以根据 ID 路由消息到该 Chatbot 类型的运行实例。
3.3.2. 人工客服支持
场景分析
什么情况下由机器人响应消息?什么情况下由人工客服响应消息?这是一个需要定义的产品需求。每个租户的需求可能是不同的,SaaS
平台可以预先设定一些切换模式,然后再根据租户需求做定制化的方案。
技术方案
说明:
Msg Service(消息服务):用于记录和读取用户与 Chatbot、用户与人工坐席系统的交互消息。
人机切换实现并不复杂,但分发机制放在哪个模块却是一个需要衡量的问题。
如果都放在 Router,那么 Router 就需要知道所有租户的设定,可能会因为租户的个性化需求而频繁修改;如果都放在
Chatbot,那么人工坐席系统的消息也需要经过 Chatbot,不但增加了消息流转环节,而且导致人工坐席服务和
Chatbot 强耦合。
那么该如何来设计这个分发机制呢?
从需求场景来分析,可以归类为两种设定:一是全局设定,二是个性化设定。
1.全局设定
仅机器人响应和仅人工客服响应,这个可以归类到全局设定。Router 中增加 Distribution
Policy,由其直接将消息分发到对应的系统。
这里有一个明显的需求冲突:当设定为仅机器人响应时,如果最终用户要求人工客服响应,该如何来处理?
一个方案是 ActionServing 获取相关的分发策略设定,如果判断为仅机器人响应,则不通知人工坐席系统且给出预设的回复;另一个方案是
ActionServing 无需知晓全局分发策略设定,始终通知人工坐席系统且给出预设的回复,是否切换完全由人工客服决定。
这个需求冲突需由产品设计层面决定,无论哪种方式,都只是与租户相关的 ActionServing 的改动,不影响整体架构。
2.个性化设定
意图检测、情感分析……等需根据对话内容来判断是否需要切换到人工坐席的情形,这个可以归类到个性化设定,其本质也是
Chatbot 的对话流编排。
因为需要利用 Chatbot 的自然语言理解能力,因此需先将消息发送给 Chatbot,由 Chatbot
的对话管理模块根据对话流状态通知人工客服进行后续处理。人工客服一旦开始应答,Router 则开始将消息转发到人工坐席系统(自动切换)。
基本业务流程如下图所示,Router 负责全局设定,Chatbot 负责个性化的设定。
备注:
如果有更加复杂的个性化需求,可以在 Router 之后增加一个租户特有的独立的分发组件,无需修改
Router 和 Chatbot。
3.4. 技术选型
在前面的架构设计中,我一直避免限定具体的技术框架和开发语言。这是因为选择何种技术其实是非常个性化的,不仅仅与具体业务有关,也跟开发团队熟悉的技术有关。接下来,也仅仅聊一些大的方向选择,或者说是我个人的选择倾向。
异步、长链接和双向通信
Chatbot 和人工客服的消息天然就是异步响应的,一次对话过程会有多轮对话(多次通信),也可能会连续发送多条消息而无需等待对方回复。
因此,外部接入的会话网关应该能支持长链接、双向通信和异步响应。WebSocket、RSocket、MQTT
等都可以支持,综合来看,WebSocket 最为通用成熟,因此首先支持 WebSocket 协议,其它协议再根据业务需求做进一步开发。
另外,如 3.2.3. 组件交互 所描述的,内部服务组件之间的通信也应该是异步的,我更倾向于采用
MQ 来作为 Router 和 Chatbot & 人工坐席系统 之间的异步通信机制。
但这也会带来一些其它的复杂度:譬如服务端响应消息时如何才能知道客户端连接到了哪个会话网关的哪个节点?如何来判断是否响应超时?消息是否允许丢失?如何保证消息不丢失?如何保证消息不重复响应?
业界对于这些问题已有一些不错的实践,但具体而言还是要根据业务场景去选择落地方案,这里不再继续深入探讨。
语言与框架
对于较大型的项目,通常都会使用多个语言。
Java
Java 的企业应用生态最丰富完整,但有一个问题是编译后的镜像体积大启动慢。当前,Spring、Quarkus
等正与 GraalVM 密切合作以构建资源占用低启动速度快的云原生应用,但依然有很多项目不能编译成
native-image。
因此,如果选用 Java 作为主语言,SaaS 平台的管理部分可以用 Spring,而对于需要频繁创建、快速扩容且相对简单的
Chatbot 和 ActionServing,可以使用 Spring Native 编译成 native-image。
Go
Go 编译后的容器镜像较小,并发支持也不错,虽然相比 Java 而言应用生态并没有那么强大,泛型也是硬伤,但在
DevOps 领域却是绝对的主流。SaaS 平台的管理调度部分其实也是偏运维,所以其实可以用 Go
来开发。
Python
AI 领域的开发大多使用 Python,当前主流的深度学习框架有 TensorFlow 和 PyTorch,很多开源的模型也是使用这两个框架开发。相比较而言,TensorFlow
的模型部署更加强大方便。
综上:SaaS 平台的会话网关、管理端、领域服务层用 Java(Spring),Chatbot 和
ActionServing 用 Java(Spring Native);SaaS 平台的调度端可以用
Go,不过更倾向于也用 Java;机器学习的模型开发主要用 Python(具体框架取决于开源模型采用哪种框架);模型部署则与具体的深度学习框架相关,部署方式的选择需要考虑并发性能和时延,但总之最后一定要打包成
Docker 镜像,便于自动化运维。
微服务与容器化
SaaS 平台要实现自动化运维,K8s + Istio + Docker 是一套不错的组合。但 K8s
和 Istio 比较复杂,这需要技术团队对其有足够深刻的了解,还需要有一定的定制化开发能力。当然不一定非要用
K8s + Istio,也可以用 Spring Cloud + Go + Docker 的方式来开发,优点是方案会更加简单可控,缺点是需要自己实现一整套运维管理监控机制,到最后可能其实就是
K8s + Istio 功能的一个子集。
关于技术选型,最重要的其实是在保证功能需求和特性需求的基础上能够做到简单和统一,有利于后续的开发和维护。
3.5. 小结
本节首先对 SaaS 平台的需求进行了分析,然后介绍了 SaaS 平台的架构设定,最后简单探讨了技术选型。
|