您当前的位置是:  首页 > 新闻 > 文章精选 >
 首页 > 新闻 > 文章精选 >

Chatopera多轮对话设计器:实现天气查询机器人的过程

2018-07-31 14:05:14   作者:   来源:CTI论坛   评论:0  点击:


  随着语音识别(ASR)和文字转语音(TTS)技术的成熟,语音交互逐渐成为人机交互重要方式,AI音箱开始流行,各种手机厂商也推出手机智能语音助手,在消费者领域,各种技能快速增长。另外一方面,消费者也趋于使用即时通讯软件来取代电话和短信。
2016-2018年亚马逊Alexa技能数量增长图
  在企业中,客服系统、工作台和办公软件也逐渐走向更加智能化和自动化的方案。不管是中小型企业或者大型公司,都在踊跃尝试AI技术飞跃带来的福利。企业IT系统将迎来更多升级,机器学习会刷新每一个软件。图片,视频和文本是人工智能技术重点发挥优势的地方。人机对话系统将迎来革命,企业会普遍应用对话应用,一种更快捷,更智能的服务方式:使用对话机器人完成业务流程,比如智能会签、智能请假、智能审批和智能客服等。
  但是目前,企业实现对话应用的方式还很笨拙:
  1)上线周期长:由于缺少实践经验,难以掌握人工智能技术的边界,往往难以做好功能设计,在开始后还会返工。
  2)需要一个华丽的团队:需要招聘目前紧俏的AI产品经理、机器学习工程师和自然语言处理工程师,亦或是标注人员和测试人员。
  3)需要接入第三方服务:比如语音识别、文字到语音和机器学习硬件依赖等。
  然而,在Chatopera,我们回顾一些对话应用,比如机器人客服场景时,和机器人相关的模块可以独立为一个产品,经过了仔细的分析,我们认为,可以开发两个产品:多轮对话设计器和智能问答引擎。前者面向企业的业务人员,撰写聊天机器人脚本;后者面向企业的IT人员,是问答服务的运行环境。
  本文重点介绍使用多轮对话设计器实现“天气查询机器人”的过程,我们选择这个场景并不是因为它简单,而是因为它容易理解,使用多轮对话设计器可以实现更复杂,更有价值的应用。我们先一睹为快,这个机器人是什么样子的。
  天气查询机器人Demo:https://v.qq.com/x/page/a0717l5z53k。html
  是不是很实用?如果你掌握了下面的技巧,它可以帮助做更多的事情。
  需求分析
  首先,我们需要梳理一下需求:
  1. 我想知道任意城市的天气信息,比如“今天上海天气怎么样”;
  2. 我还比较关心空气,我可以通过“今天上海空气怎么样”获得空气质量信息;
  3. 我想知道今天适不适合户外运动,就问“今天上海适合运动么”;
  4. 如果我问了一个城市的天气状况,我还想继续询问这个城市更多信息,这样我不用每次都告诉机器人城市名称。
  当然,我的每个意图都有多种表述方式,机器人能支持一些变化的问法。如果我的问题不够严谨,机器人还应该提醒我合理的表达。
  准备开发环境
  • 下载多轮对话设计器
  多轮对话设计器的下载地址是:
  https://www.chatopera.com/product/conversation-designer
  如果您打开后,下载按钮显示未“待发步”,是因为我们还在紧张的测试,预计2018年7月21日可以发布v1.0版本,请填写“建立联系”表单,我们发布后第一时间与您联系。
多轮对话设计器下载页面
  目前,多轮对话设计器支持的操作系统包括Windows和MacOSX,在安装过程和启动过程中,如果遇到问题请通过微信公众号找到我们:
  微信公众号:获得支持和帮助
  • 调研提供天气信息查询的API
  现在很多服务以API的形式提供,从搜索引擎中查找“天气查询服务API”,我们就能得到一些供应商,经过一些比较,我选择了和风天气,它数据丰富,免费额度大方。
  • AI音箱
  Chatopera与Sayinfo达成战略合作关系,所以,我们的对话系统产品与任你说音箱可以直接集成。
  第一条规则
  第一次打开多轮对话设计器后,我们看到如下的面板,我们称之为主面板。
  点击新建按钮,弹出创建机器人的表单:
  填入“小叮当”,当前多轮对话设计器支持中文(zh_CN)和英文(en_US),我们选择“zh_CN”,点击“确认”。然后我们就得到了一个聊天机器人。
  在操作中,有几个按钮:
  • 管理:管理聊天机器人的多轮对话。
  • 版本管理:管理不同版本的机器人,导出机器人和在不同版本之间进行比对。
  • 环境变量:机器人函数中依赖的全局变量,这些变量在“设计对话”的阶段和在IT人员“部署到生产环境”下的值是不同的,比如一些接口服务的认证键值对。
  • 发布:发布当前机器人为最新版本。
  • 删除:将机器人删除。
  点击“管理”,进入多轮对话管理页面,点击“新建对话”,在弹出的窗口中,填写“对话名称”为“weather”,点击“确认”。这时,我们看到了新建的对话,我们将修改它的内容完成天气对话服务。
  点击“编辑”,进入对话编辑窗口。在左侧的“脚本区域”,写下第一条规则。
  +今天(*)天气[怎么样]
  -{keep} <cap1>天气挺好的
  点击“保存”,这时右侧的“逻辑区域”有了变化,出现了一个线条,在线条左右两端分别是问题和答案。在“对话区域”,我们输入“今天北京天气怎么样”,点击发送,这时机器人回复了。
  从我们需求上看,这没什么用,但是它工作了,我们就一点点优化它。对于在这条规则中,我们使用的语法,(*)代表一个槽位,<cap1>代表在回复中取槽位的值,[怎么样]是可有的字符串,{keep}代表这条规则始终生效,keep涉及到对话的状态管理,我们将在文档中提供更多规则的描述,现在,读者看懂本示例就可以了。
  添加函数
  在多轮对话设计器中,怎么请求和风天气的数据呢?使用函数。函数是多轮对话支持的使用JavaScript实现的程序。
  我们在“对话编辑窗口”点击函数,粘贴如下代码:
var WForewast = function (apiKey) {
    if (!apiKey) throw new Error('Invalid token, get it from http://www.heweather.com/my/service');
    this.key = apiKey;
}


WForewast.prototype.getWeatherByCity = function (city) {
    return new Promise((resolve, reject)=>{
        let url =  "https://free-api.heweather.com/v5/weather?city=" + encodeURIComponent(city) + "&key=" + this.key
        http
            .get(url)
            .then((res)=>{
                resolve(res.data.HeWeather5[0].suggestion);
            })
            .catch(function (err) {
                if (err) return reject(err);
            });
    })
}

const wf = new WForewast('182f1b6826d94c6285a489d2414f3ad0');


exports.getWeatherByCity = function(city, cb){
    debug("getWeatherByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["comf"]["txt"]
            })
        }, (err)=>{
            debug("error:%j", err)
            cb(null, {
                text: `很抱歉,没有获得${city}的天气信息。`
            })
        })
}

exports.getAirByCity = function(city, cb){
    debug("getAirByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["air"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的空气信息。`
            })
        })
}


exports.getSportByCity = function(city, cb){
    debug("getSportByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["sport"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的信息。`
            })
        })
}

exports.getDresscodeByCity = function(city, cb){
    debug("getDresscodeByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["drsg"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的信息。`
            })
        })
}
 
  在函数中,我们实现了根据城市请求天气、空气质量、着装建议和运动建议的接口,分别是getWeatherByCity,getAirByCity,getDresscodeByCity和getSportByCity。
  细心的读者会发现,在函数中,多轮对话设计器直接支持了http,debug作为工具类,发起网络请求和输出日志信息。这两个接口极大的扩展了函数的能力,我们也会在函数中详细描述它们的使用。
  然后,回到“脚本区域”,修改一下规则,更新如下:
  +今天(*)天气[怎么样]
  -{keep}^getWeatherByCity(<cap1>)
  在回复中,我们调用了getWeatherByCity,并且传入了城市名称。接着,在“对话区域”,输入“今天北京天气怎么样”,回复与上次不一样了。
  这次,我们看到了期望的回复,正是从和风天气返回的北京今天的天气状况。
  使用环境变量
  在上面的函数中,我们有一个敏感的信息:和风天气的API密钥。在实际应用中,我们希望设计阶段和部署阶段,它的值是不同的。这时,就需要使用环境变量,环境变量正是为解决这个问题而设计的。
  回到主面板,在“小叮当”操作中,点击环境变量,创建如下键值对:
  读者可以从和风天气获得该密钥,为验证用途,可以粘贴下面的值:
   "HEWEATHER_URL": "https://free-api.heweather.com/v5",
        "HEWEATHER_KEY": "182f1b6826d94c6285a489d2414f3ad0"
  保存后,回到天气对话脚本的“对话编辑窗口”,在函数中,使用下面的脚本:
/**
 * Plugin
 */

var WForewast = function (apiKey) {
    if (!apiKey) throw new Error('Invalid token, get it from http://www.heweather.com/my/service');
    this.key = apiKey;
}


WForewast.prototype.getWeatherByCity = function (city) {
    return new Promise((resolve, reject)=>{
        let url = config["HEWEATHER_URL"] + "/weather?city=" + encodeURIComponent(city) + "&key=" + this.key
        http
            .get(url)
            .then((res)=>{
                resolve(res.data.HeWeather5[0].suggestion);
            })
            .catch(function (err) {
                if (err) return reject(err);
            });
    })
}

const wf = new WForewast(config["HEWEATHER_KEY"]);


exports.getWeatherByCity = function(city, cb){
    debug("getWeatherByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["comf"]["txt"]
            })
        }, (err)=>{
            debug("error:%j", err)
            cb(null, {
                text: `很抱歉,没有获得${city}的天气信息。`
            })
        })
}



exports.getAirByCity = function(city, cb){
    debug("getAirByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["air"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的空气信息。`
            })
        })
}


exports.getSportByCity = function(city, cb){
    debug("getSportByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["sport"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的信息。`
            })
        })
}

exports.getDresscodeByCity = function(city, cb){
    debug("getDresscodeByCity: %s", city);
    wf.getWeatherByCity(city)
        .then((suggestions)=>{
            cb(null, {
                text: suggestions["drsg"]["txt"]
            })
        }, (err)=>{
            cb(null, {
                text: `很抱歉,没有获得${city}的信息。`
            })
        })
}
  这次,代码内容和前一版本相比,使用了config对象,config是一个包含环境变量的JSON数据。所以,我们更佳利于将来部署对话应用了。
  支持更多对话
  回想我们需要的几种天气信息,我们根据需求变更脚本,一个满足需求的脚本呈现如下:
/**
 * 一个能回答天气的对话脚本
 * author: Hai Liang Wang<hain@chatopera.com>
 * date:   2018-06-20
 */


// 技能介绍

+ 你知道哪些天气信息
- 我知道今天的空气,着装建议和适不适合运动

// 天气

+ 今天 (*) 天气 [怎么样]
- {keep} ^getWeatherByCity(<cap1>)

+ [今天] (天气|气候) [怎么样]
- {@__wf_guide_}

+ (*) 今天天气 [怎么样]
- {keep} ^getWeatherByCity(<cap1>)

    + (*) 空气 (*)
    % ^getWeatherByCity(<cap1>)
    - {keep} ^getAirByCity(<p1cap1>)


+ __wf_guide_
- {keep} 添加城市名哦,比如“今天北京天气怎么样”或者“北京天气怎么样”
- 我需要知道城市名称,比如“今天北京天气怎么样”或者“北京天气怎么样”
- 要告诉我城市名,比如“今天北京天气怎么样”或者“北京天气怎么样”


// 空气

+ [今天] 空气 [怎么样]
- {@__wf_guide_air}

+ (*) 今天空气 [怎么样]
- {keep} ^getAirByCity(<cap1>)

+ 今天 (*) 空气 [怎么样]
- {keep} ^getAirByCity(<cap1>)

+ __wf_guide_air
- {keep} 添加城市名哦,比如“今天北京空气怎么样”或者“北京空气怎么样”
- 我需要知道城市名称,比如“今天北京空气怎么样”或者“北京空气怎么样”
- 要告诉我城市名,比如“今天北京空气怎么样”或者“北京空气怎么样”


// 运动

+ [今天] 适(合|宜)运动(么|吗)
- {@__wf_guide_sport}

+ (*) 今天适(合|宜)运动(么|吗)
- {keep} ^getSportByCity(<cap1>)

+ 今天 (*) 适(合|宜)运动(么|吗)
- {keep} ^getSportByCity(<cap1>)

+ __wf_guide_sport
- {keep} 添加城市名哦,比如“今天北京适合运动么”或者“北京今天适合运动么”
- 我需要知道城市名称,比如“今天北京适合运动么”或者“北京今天适合运动么”
- 要告诉我城市名,比如“今天北京适合运动么”或者“北京今天适合运动么”


// 衣着

+ [今天] 适(合|宜)穿什么
- {@__wf_guide_dresscode}

+ (*) 今天适(合|宜)穿什么
- {keep} ^getDresscodeByCity(<cap1>)

+ [今天] (*) 适(合|宜)穿什么
- {keep} ^getDresscodeByCity(<cap1>)


+ __wf_guide_dresscode
- {keep} 添加城市名哦,比如“今天北京适合穿什么”或者“北京今天适合穿什么”
- 我需要知道城市名称,比如“今天北京适合穿什么”或者“北京今天适合穿什么”
- 要告诉我城市名,比如“今天北京适合穿什么”或者“北京今天适合穿什么”
  这也就是我们在天气查询机器人Demo中看到的机器人的脚本,在设计过程中,我们通过对话区域来测试机器人的回复是否符合预期,我们通过逻辑窗口来查看当前机器人的思维逻辑导图,当前机器人对话的状态会被高量,被命中的规则呈现为路径。
  另外,在设计过程中,每次保存自动为脚本和函数生成快照,使用快照下拉列表,我们能方便的回退。
  这就是我们追求的体验,业务人员可以更专注于对话机器人的对话逻辑满足需求。
  发布机器人
  现在,有了可以工作的脚本,我们想发布一个版本,这时回到主面板,点击“发布”,填入如下信息,点击“确认”。
  导出机器人
  最终,我们需要得到一个“服务”,它能时刻被访问,以及和AI音箱集成。我们需要将天气机器人导出为对话应用,然后部署到智能问答引擎。在主面板,点击版本管理,我们看到操作中有三项。
  • 对比差异:在多个版本中比较差异,包括脚本和函数。
  • 导出:将机器人导出为对话应用文件。
  • 覆盖:使用这个版本覆盖当前机器人,包括脚本和函数等。
  什么是智能问答引擎
  智能问答引擎是多轮对话的运行时,同时也包含知识库和统计监控等功能。智能问答引擎和多轮对话设计器会同时发布。
  如果您想参加这两个产品的发布会,请报名Chatopera产品发布会:追求高度智能化和自动化的企业服务。
【免责声明】本文仅代表作者本人观点,与CTI论坛无关。CTI论坛对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。请读者仅作参考,并请自行承担全部责任。

专题