-
Notifications
You must be signed in to change notification settings - Fork 119
系统设计与实现
构建聊天机器人依赖于数据,这些数据可以由业务人员整理,或者从客户数据库分析,比如聚类分析等方法整理而来。这些对话之前通常是人工座席应答,随着客户业务的增长,人工座席呈现线性增长,这带来了不可控制的开销,成本急剧增加。为此,企业寻求替代方案:智能对话机器人,也称为聊天机器人,机器人客服,机器人等。
聊天机器人可以724365的工作,标准回复,立即响应。虽然一些棘手的问题,机器人还不能完成,但是很多咨询问题都是常见的,或话术都是固定的。先通过机器人应答或者由机器人收集信息,进一步再由人工座席处理棘手的问题已成为一种最佳实践。很多证券、电商、保险行业已经成功上线了机器人客服,这一趋势大约从2015年就迅速发展。这一趋势得益于技术的进步,甚至出现了一个新兴岗位:AI训练师。
那么,如何落地企业智能问答机器人呢?
Clause开源项目就是回答这个问题的。
为了方便概览,后文的内容同时也整理为幻灯片,参考下载地址。 幻灯片中的内容相对于后文是有所删减的,如需了解详情,请继续阅读后文。
Clause使用关系型数据库存储对象,包括词典、意图、说法和槽位等,数据库表介绍:
表 | 描述 |
---|---|
cl_dicts | 词典,包括自定义词典和系统词典 |
cl_dictwords | 自定义词典的词条 |
cl_intents | 意图 |
cl_intent_slots | 意图槽位 |
cl_intents_utters | 意图说法 |
cl_chat_sessions | 会话 |
cl_chat_msgs | 对话消息 |
cl_bot_sysdict | 机器人关联系统词典关系 |
cl_prod_vers | 生产版本 |
cl_dev_vers | 调试版本 |
目前,Clause的数据库表关联关系如下:
其中包括10个对象,7个约束外键,数据库的初始化SQL文件也在项目中(链接),查看该SQL获取详细信息。
- 高性能
核心代码使用C++,模块间调用以及暴露的接口主要采用RPC,RPC使用长连接,效率高。
- 可拓展
支持不同容器的水平伸缩,尤其是计算量大的clause节点和intent节点之间使用消息服务,intent节点可以随意的横向扩张,clause通过activemq分发计算任务。
- 自动化
支持聊天机器人的数据标注,训练,调试和上线的完整生命周期。
clause服务通过docker-compose编排,各模块介绍如下:
模块名称 | 功能 | 依赖 |
---|---|---|
clause | 对外暴露RPC接口,管理关系型对象,会话管理,聊天管理,版本管理 | sysdicts, redis, mysql, activemq |
sysdicts | 提供系统词典,标记系统词典的实体 | 无 |
intent | 提供训练机器人的接口,包括分类模型、NER模型、自定义词典和分词器等 | activemq |
mysql | 关系型数据的存储 | 无 |
redis | 会话数据存储,版本数据,训练状态等需要快速读取和写入的数据 | 无 |
activemq | clause服务和intent服务交换信息的消息服务中间件 | 无 |
其中,clause、sysdicts和intent是核心实现模块,实现均为C++,使用Apache Thrift框架和Protobuf;mysql、redis作为数据持久化数据库;activemq作为消息服务。
注意:在模块中有一个模块也命名为clause,其原因是该模块是最主要的模块,后文使用“clause模块”指代。“clause服务”则代表通过docker-compose运行起来的整个应用。“clause项目”代表Clause服务的开源码项目。
为了灵活和更好的扩展性,Clause暴露的集成接口的输入和输出采用统一的数据类型:chatopera::bot::clause::Data,简称Data。再由Data数据类型包含其它对象的列表或单个对象,Data数据类型包含的数据很多,简要介绍主要字段:
Data字段 | 描述 |
---|---|
rc | 返回值的代码,成功返回则rc=0,否则为异常返回 |
error | 异常返回值的原因,当成为返回值,error为空 |
msg | 成功返回的消息 |
id | 某个对象的ID |
chatbotID | 聊天机器人的唯一标识 |
customdicts | 自定义词典列表 |
sysdicts | 系统词典列表 |
botsysdicts | 机器人引用的系统词典列表 |
dictwords | 自定义词典的词条列表 |
messages | 聊天消息列表 |
intents | 意图列表 |
slots | 意图槽位列表 |
utters | 意图说法列表 |
devver | 调试版本信息 |
prover | 生产版本信息 |
session | 聊天会话 |
message | 聊天消息 |
currpage | 翻页信息,当前页面 |
totalpage | 翻页信息,页面总数 |
totalrows | 翻页信息,全部数据条数 |
pagesize | 翻页信息,页面大小 |
query | 翻页信息,查询条件 |
完整的Data数据定义如下:
在查看Clause提供的具体接口前,可以通过server.dsl了解Clause对数据结构的定义和接口定义。
子模块 | 接口 | 描述 |
---|---|---|
词典相关 | ||
postCustomDict | 创建自定义词典 | |
putCustomDict | 更新自定义词典 | |
getCustomDicts | 获得自定义词典列表 | |
getCustomDict | 获得自定义词典详情 | |
delCustomDict | 删除自定义词典 | |
postSysDict | 创建系统词典 | |
putSysDict | 更新系统词典 | |
getSysDicts | 获得系统词典列表 | |
refSysDict | 引用系统词典 | |
unrefSysDict | 取消引用系统词典 | |
myDicts | 获得指定机器人的所有自定义词典和引用词典 | |
mySysDicts | 只获得指定机器人的引用的系统词典列表 | |
putDictWord | 创建或更新自定义词条 | |
getDictWords | 获得自定词典的词条列表 | |
delDictWord | 删除词条 | |
hasDictWord | 检查一个词汇是否在自定义词典中 | |
意图相关 | ||
postIntent | 创建新的意图 | |
putIntent | 更新意图 | |
getIntents | 获得意图列表 | |
delIntent | 删除意图 | |
postSlot | 创建意图槽位 | |
putSlot | 更新意图槽位 | |
getSlots | 获得槽位列表 | |
delSlot | 删除槽位 | |
postUtter | 创建意图说法 | |
putUtter | 更新意图说法 | |
delUtter | 删除意图说法 | |
getUtter | 获得意图说法详情 | |
训练机器人 | ||
train | 训练机器人 | |
status | 获得机器人训练状态 | |
版本管理 | ||
devver | 获得最新调试版本信息 | |
prover | 获得最新生产版本信息 | |
version | 获得生产版本和调试版本信息 | |
online | 将指定调试版本升级为生产版本 | |
对话相关 | ||
putSession | 创建会话,会话的概念参考 | |
getSession | 获得会话详情 | |
chat | 和指定的机器人进行对话 |
关于服务端这些接口的定义,可以参考 API Docs。
关于Clause客户端的使用,也有这些接口的描述,开发者在掌握了一个语言的客户端的使用,可以很容易的掌握其他语言的客户端,因为接口的设计是一致的。Clause客户端的使用请参考示例程序和系统集成。
- 分发训练任务
- 检查工作内容
- 构建词典
- 生成拓展的说法
- 基于说法构建索引
- 构建NER模型
- 返回训练执行结果
- 确认会话的有效
- 确认目标BOT存在
- 检查是否确定了意图
- 确认新的信息提取到了槽位信息
clause服务使用了如下项目:
http://thrift.apache.org/ Apache Thrift提供了跨语言的RPC服务方案,通过一种服务定义语言,描述接口,Apache Thrift就可以生成兼容不同编程语言的服务端和客户端代码。这给用户集成Server端提供的服务带来极大方便。Clause暴露的服务的描述文件是server.dsl。
https://developers.google.com/protocol-buffers/
Protobuf是Google发布的用于消息传递的库,可以用一种描述语言定义数据结构,然后Protobuf生成不同语言的类和管理方法。Protobuf的最大优点是减小了数据传输体积,加快了数据读取速度,因为其优良数据编码,使用Protobuf会比使用JSON数据格式快7倍。 在Clause中,Intent模块和Clause模块通过ActiveMQ交换数据,就使用Protobuf完成。
http://www.chokkan.org/software/crfsuite/
CRFSuite是一个实现CRF算法的C++工具库,CRF算法是目前实现序列标注最好的实践,就算是基于深度学习的算法也是与CRF结合,因为CRF可以充分考虑一个字与周围的字组成的特征来标记一个类别。CRF算法本身可以在不依赖大量数据前提下取得比较好的准确率,而且训练速度快,CRFSuite又使用C++实现,在追求线上训练和预测时,以及在成本可控的前提下,成为了最好的选择。Clause服务中,在解决命名实体标识时,使用自定义词典、用户说法和槽位训练NER模型。
https://www.paddlepaddle.org.cn/ 源于产业实践的开源深度学习平台
Clause的包含系统词典类型,系统词典极大的简化了用户使用Clause服务的难度,Clause目前版本中提供了人名(@PER),地名(@LOC),组织结构名(@ORG)和时间(@TIME)是基于中文词法分析(LAC)的,这个选择基于下面的原因:
- 经测试,LAC的识别效果很不错
LAC发布的模型是基于亿级预料训练
- LAC项目发布了训练脚本
这样,Chatopera团队或开发者可以增加训练语料,识别新的类。而且,训练脚本使用Python,这样减少了学习成本。
- LAC预测使用C++
LAC的预测库使用C++,保证了线上使用的高性能。
- LAC的算法和框架可靠
LAC在给出项目的代码,模型和训练脚本,同时也提供了算法的论文,以及PaddlePaddle的持续更新和开放,让我们相信LAC是一个很好的选择。
Xapian是使用C++实现的搜索引擎,Chatopera团队在32GB内存,8核CPU上测试Xapian的性能,测试条件为1500W条文档,平均每个文档~1000字,Xapian可以实现30QPS的超高性能。在Clause中,Xapian用于召回意图,由Xapian构建的索引以及其它排名算法,作为分类模型使用。
Clause还使用了其它的技术,做更简单的介绍
- gtest
单元测试
- glog, gflags
日志以及启动参数
- leveldb:快速检索自定义词表
https://github.com/google/leveldb
- hat-trie: 高性能前缀树
https://github.com/Tessil/hat-trie
- jieba: 中文分词器
https://github.com/yanyiwu/cppjieba
更多技术栈参考thirdparty。
北京华夏春松科技有限公司 https://www.chatopera.com/