更聪明的电话语音机器人

有人说AI应用最重要的是工程实现,而不仅仅是算法,诚哉斯言。

电话语音机器人目前应用最多的是电话营销,我接过几个这样的电话,挺傻的,基本听不懂人话。

我也见过一些厂家的电话机器人界面,配置十分复杂,每个关键字要配置一个节点,存储到数据库,运行的时候关键字匹配,需要在数据库里做上百个sql查询,查来查去,效率极低不说,灵活性反而大受限制。

我实现的电话语音机器人,就是解决上述两个根本性问题,而且接口十分简单。

其实,我们完全可以采用AI里面语意描述的概念:“词槽”。比如查天气,需要的词槽有日期(今天,明天,星期几),城市。只有这两个词槽都填充了,才可以完成查询。

简单说来,词槽有这么几种类型:

const MENU_SLOT = 0;  // 菜单或选择类型,最为常见,需预先定义。
const AMOUNT_SLOT = 1;  // 数量
const MONEY_SLOT = 2;  // 金额
const TEXT_SLOT = 3;  // 普通文本输入

词槽的描述,我的解决方案是完全采用文本串,配置串可以存储在ini文件或数据库,也可以通过api传入,举个配置的例子(完全用文本描述),比如需要让用户说珠三角的几个城市,差不多是json格式,请注意我加上的注释:

slot0 = 
{

 "name" : "city",   // 词槽的名字

 "type" : MENU_SLOT,   // 菜单类型

 "init" : {"请说一个珠三角城市", "你想去哪里呢,广州、深圳、中山、香港还是澳门?", "您可以从广州、深圳、香港、中山或澳门确定一个城市", "说一个珠三角城市,区县名也可以"}, 

 "keys" : {  // 关键字组

         {"深圳","福田","南山","罗湖","盐田","龙岗","龙华","宝安","光明"},  // 第1组深圳,索引值默认为0

         {"广州","越秀","天河","番禺","增城","花都","海珠","白云","黄埔"},  // 第2组广州,索引值为1

         {"香港","屯门","元朗","新界","九龙","中环","港岛"},  // 第3组香港,索引值为2

         {"澳门"},  // 第4组澳门,只有1个关键字,索引值为3

         {"中山","小榄","东升","石岐","三角"}  // 第5组中山,索引值为4

     }

};
   
init 属性相当于欢迎词,用TTS(文本语音合成)自动播放出来。在多次交互中,系统会随机选一个欢迎词,同样意思换不同说法,显得有点智能。
keys 关键字后面的每一组关键字,具有相同的索引值,表示相同语义或相同的值。

如果用户说“我在增城市”,系统将匹配到“增城”,返回词槽的值是1,即广州那组的索引值。

让我们再来看看常见的外呼营销例子:

营销机器人为了更逼真,需要播放真人录音:

slot1 = 
{
 "name" : "sales", "type":MENU_SLOT, 
 "init" : {"voc/Invite-L7PGNX.wav"},   // 这个欢迎词只有一个,请“林志玲”录音,极尽温柔劝说之能事
 "keys" : { 
{"没兴趣","不需要","不参加","不要","不用","骚扰","举报","没有","没时间"},  // 索引值0,否定性质的关键词
{"时间","时候","哪里","地方","星期","考虑","意思","谁讲", "看看"},   // 索引值1,中性关键字
{"可以","有兴趣","还行","去吧","正要","好的","那行","那好","行吧", "好", "行"} // 索引值2,肯定
             } 
};

如果用户说话做出反应,词槽的返回值是下列之一:0否定;1中性,不明所以;2肯定。

应用层可以根据返回值做动作,如果用户肯定,可以播放进一步联系方式(加微信),或者直接转人工坐席;如果用户否定,播放一个道歉语音然后挂断;如果是中性,播放一个说明语音或详细解释的语音。

这样的配置是不是既完备又简单呢?

下面再说说算法。

有些厂家的电话语音机器人效果不好,用户问起来,便统统推给ASR厂商:语音识别不准,我有什么办法?

现在大多采用免费的ASR接口,如百度、科大讯飞、腾讯、阿里,确实本身是免费的不可能为你专门做优化,再说免费接口不能为你的特定领域做训练,识别效果马马虎虎也是无奈。

我对这个问题进行了深入研究,找到了解决方法。

一是尽量提高VAD检测效果,尽量提交完整句子。

二是对文本匹配进行深入优化,汉字很多同音字,尤其是短语或单字,我找到识别的算法,准确度极高。(我还专门为这个算法做了封装成了一个动态库)。这样省去海量训练或加很多同音关键字的麻烦。

语音系统支持标准SIP,系统运行在64位windows下,效果相当不错。欢迎索取测试程序。

-------

附:

最新蓝星际语音平台REST接口:
v1.04 2018-04-04 bluesen@sina.com
-- 2018-4-20 v1.03  增加会议接口。
-- 2018-5-11 v1.04 增加录音参数的含义,可进行无缓冲即时写文件的录音操作。
-- 2018-5-15 v1.05 增加PlayTTS可选参数charset=utf-8,可指定编码。
-- 2018-6-30 v1.06 增加暂停放音PausePlay、恢复放音ResumePlay。
-- 2018-7-4   v1.10 增加智能机器人实现和接口


注意:大小写敏感,方法名总是大写字母开头,参数名总是小写字母开头。


一、语音平台作为服务器:
1、呼叫相关:
1.1. 拨号呼叫:方法Dial:主叫号码,被叫号码
       返回:id -相当于通道标识


请求URI样例:
ttp://172.16.110.140:8000/Dial
请求消息样例:
{"caller":"4008123","callee":"13902912345"}
返回消息样例:
{"resultCode":"0","resultContext":"","id":"12"}




1.2. 对来电应答:方法Answer:id
--此方法暂时不用。


1.3. 挂断通话:方法Hangup:id
请求URI样例:
ttp://172.16.110.140:8000/Hangup
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}




1.4. 查询通道状态:方法GetState: id
       返回:state: 0-空闲,1-通话中,2-外呼振铃中,3-来电振铃中
--此方法暂时不用。


2、放音操作
2.1. 放音:方法Play:id, file
    注意:路径请勿使用反斜杠,使用顺斜杠/,参见后面样例。
     ——如果上一次放音没有结束,本次调用将自动停止上一次放音,然后开始本次放音
请求URI样例:
ttp://172.16.110.140:8000/Play
请求消息样例:
{"id":"12","file":"d:/voc/welcome.wav"}
返回消息样例:
{"resultCode":"0","resultContext":""}




2.2. TTS方式放音:PlayTTS:id,text-字符串, [charset=utf-8]
注意:如未指定charset=utf-8,默认中文请使用国标码
请求URI样例:
ttp://172.16.110.140:8000/PlayTTS
请求消息样例:
{"id":"12","text":"你的问题我们已经知道了,谢谢!"}
返回消息样例:
{"resultCode":"0","resultContext":""}
如果是utf8之请求消息样例:
{"id":"12","text":"你的问题我们已经知道了,谢谢!","charset":"utf-8"}


2.3. 停止放音:方法StopPlay:id
请求URI样例:
ttp://172.16.110.140:8000/StopPlay
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


2.4. 暂停放音:方法PausePlay:id
----暂停后可用ResumePlay方法恢复放音
请求URI样例:
ttp://172.16.110.140:8000/PausePlay
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


2.5. 恢复放音:方法ResumePlay:id
----恢复被PausePlay暂停的放音,从上次暂停的地方继续播放
请求URI样例:
ttp://172.16.110.140:8000/ResumePlay
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


3、录音操作
3.1. 开始录音:方法StartRec:id, file, isVad=[0-不检测对方说话一直录音直到挂断或调用停止录音,1-录音过程进行活动语音检测,检测结果及时上报,2-无缓冲大文件录音]
    注意:路径请勿使用反斜杠,使用顺斜杠/,参见后面样例。
      ——参数file:基本文件名,当有多个产生时,系统会自动加上后缀;如果文件已经存在,则会被覆盖
     所有file参数务必带上id,如下例:
请求URI样例:
ttp://172.16.110.140:8000/StartRec
请求消息样例:
{"id":"12","file":"d:/rec/r12","isVad":"1"}
返回消息样例:
{"resultCode":"0","resultContext":""}


3.2. 停止录音:方法StopRec:id
请求URI样例:
ttp://172.16.110.140:8000/StopRec
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4、会议
说明:转坐席相当于两路桥接(外线通道和坐席通道),即两方会议,外线通道和坐席通道加入相同的一个会议后,双方就可以通话了。
当然,更多的通道也能加入,构成真正的多方语音会议。


4.1. 加入会议:方法JoinConf:id, confName, isListen=[1,只听方式加入,如班长监听功能; 0,正常方式加入,可说可听]
      ——参数confName:会议名,标识当前会议,要有唯一性。
     将通道id加入到会议confName。
如下例:
请求URI样例:
ttp://172.16.110.140:8000/JoinConf
请求消息样例:
{"id":"12","confName":"20180420_131201_12","isListen":"0"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4.2. 退出会议:方法ExitConf:id
     将通道id退出会议。本方法不是必须,通道挂断会自动退出会议。
如下例:
请求URI样例:
ttp://172.16.110.140:8000/ExitConf
请求消息样例:
{"id":"12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4.3. 对会议放音:方法PlayConf:id,confName, file
    注意:路径请勿使用反斜杠,使用顺斜杠/,参见后面样例。
     ——如果上一次会议放音没有结束,本次调用将什么也不干
    注意:本方法放音全体成员均可听到。某通道加入会议后,2.1.放音方法Play仍然可用,但Play只对id通道进行放音,会议的其它成员听不到。
请求URI样例:
ttp://172.16.110.140:8000/PlayConf
请求消息样例:
{"id":"12","confName":"20180420_131201_12","file":"d:/voc/welcome.wav"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4.4. 停止对会议放音:方法StopPlayConf:id,confName
请求URI样例:
ttp://172.16.110.140:8000/StopPlayConf
请求消息样例:
{"id":"12","confName":"20180420_131201_12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4.5. 对会议录音:方法RecConf:id,confName, file
    注意:路径请勿使用反斜杠,使用顺斜杠/,参见后面样例。
    注意:某通道加入会议后3.1.开始录音方法StartRec仍然可用,但StartRec只对id通道的说话进行录音,不会录进其他成员说话的声音。
请求URI样例:
ttp://172.16.110.140:8000/RecConf
请求消息样例:
{"id":"12","confName":"20180420_131201_12","file":"d:/voc/rec12.wav"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4.6. 停止对会议录音:方法StopRecConf:id,confName
请求URI样例:
ttp://172.16.110.140:8000/StopRecConf
请求消息样例:
{"id":"12","confName":"20180420_131201_12"}
返回消息样例:
{"resultCode":"0","resultContext":""}


5、语音机器人
需要预先配置好AI.ini,并获取相关模块的授权。
本系统会字段自动执行相关流程,放音并获取对方说话送至百度asr服务器进行识别,然后进行AI匹配。
AI结果将报告返回。
5.1. AI流程请求:方法AI: id, slotName-词槽名称,需预先在AI.ini配置好。
一般在外呼对方摘机后调用,或收到来电应答后调用。
请求URI样例:
ttp://172.16.110.140:8000/AI
请求消息样例:
{"id":"12","slotName":"city"}
返回消息样例:
{"resultCode":"0","resultContext":""}
{"resultCode":"-1","resultContext":"slot not found"}




二、语音平台作为客户端:
1、报告通道状态变化:方法OnState:id, state=[-1-对方挂断, 1-接通,2-外呼振铃, 3-有来电],callerId-主叫号码,calleeId-被叫号码
请求URI样例:
ttp://172.16.110.140:8020/OnState
请求消息样例:
{"id":"12","state":"1","caller":"8888333","callee":"13902912345"}
返回消息样例:
{"resultCode":"0","resultContext":""}


2、报告VAD(语音活动检测)结果:方法OnVad:id,file,vadVal=[1侦测到说话,2侦测到说话结束(静音)]
请求URI样例:
ttp://172.16.110.140:8020/OnVad
请求消息样例:
{"id":"12","vadVal":"2","file":"d:/rec/r12_1_2_3"}
返回消息样例:
{"resultCode":"0","resultContext":""}


3、报告DTMF按键:方法OnDtmf:id,dtmf=[可能的值:0-9,*,#]
请求URI样例:
ttp://172.16.110.140:8020/OnDtmf
请求消息样例:
{"id":"12","dtmf":"1"}
返回消息样例:
{"resultCode":"0","resultContext":""}


4、报告机器人AI执行结果: 方法OnAI: id, resultIndex-匹配到词槽关键字组的下标, retsultKey-匹配到的关键字, inputTxt-用户说的原始内容文本
请求URI样例:
ttp://172.16.110.140:8020/OnAI
请求消息样例1:
{"id":"12","resultIndex":"1", "resultKey":"白云", "inputTxt":"白云区附近"}
请求消息样例2:
{"id":"12","resultIndex":"-100", "resultKey":"time out", "inputTxt":""}
请求消息样例3:
{"id":"12","resultIndex":"-1", "resultKey":"not match", "inputTxt":"河水东区啊"}


返回消息样例:
{"resultCode":"0","resultContext":""}




注:暂不提供放音结束的报告,应用层能够根据文件长度估算出放音时长。


三、配置:
1、Web服务端口
2、应用层Web服务地址和端口


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值