文章正文

如何落地一个智能机器人

2022-04-11 发布于 · 阅读量:471

前言

随着智能 AI 的普及,对话式人工智能产品也越来越常见。从产品定义出发,智能问答类产品最根本的价值在于以低成本的优势取代人工工作中大量重复性的部分。我司由于业务系统较为复杂,开发同学大部分的时间都在处理技术支持、业务方、测试同学反馈的真线"问题"。有些"问题"重复性极高,完全可以沉淀为 FAQ。但现状是开发同学依然在重复性地回答之前已经处理过的类似问题,这也占用了同学们大量的时间去进行无效的"沟通"。基于目前的痛点,我们觉得有必要使用智能问答机器人来管理这部分 FAQ,除此之外,智能问答机器人内部也闭环了线上 ONCALL 问答机制,这样更加方便管理所有问题的生命流程,也方便后续问题数据的总结分类及复盘。实现了 ONCALL 跟踪,QA 应答的自动化能力。本文简单聊一聊赋能给政采云同学们的智能问答机器人"贾维斯"的设计及落地推广。

架构设计

为什么要取名为"贾维斯"呢?相信喜欢钢铁侠的同学们都知道,它是钢铁侠的智能助手,也是美国漫威漫画旗下的人工智能。全称为Just A Rather Very Intelligent System(是一个相当聪明的智能系统)。"贾维斯"的架构设计大体如下:

"贾维斯"整体的设计理念是微服务化的。这样易于拓展,方便打通我司现有的任何能力,也反哺了现有能力的建设及落地场景。"贾维斯"的优势便是能力易接入、体积更小巧、离用户更近,当然我们最初给它的定位也很简单明了,能为我司的同学们提供解决常见 QA 的应答以及自动化的能力。下面我就以这两部分来介绍一下"贾维斯"。

QA 应答能力

第一部分是 QA 应答能力。秉承着"能用 JS 做的最后都会用 JS" 的态度,我们的第一版 QA 能力是由 node-nlp 提供的,为什么使用 node-nlp?第一个原因是于我们前端小伙伴来说它使用起来非常方便,第二个原因是我们想快速探索一下落地的可能性及真实使用场景。于是乎,我们的 V1.0 版本的"贾维斯"便诞生了,下面是它简易的实现过程:

贾维斯 V1.0 版本

第一步:搭建项目

打开您常用的 IDEA,新建一个工程文件夹,创建如下所示的项目结构:

├── buildable.js
├── dist
│   └── bundle.js
├── index.html
└── package.json

在 buildable.js 里写入如下基础代码:

const core = require('@nlpjs/core');
const nlp = require('@nlpjs/nlp');
const langenmin = require('@nlpjs/lang-en-min');
const requestrn = require('@nlpjs/request-rn');

window.nlpjs = { ...core, ...nlp, ...langenmin, ...requestrn };

因为我们只使用 NLP 的核心代码和一个小的英语语言包,所以只需要向你的 package.json 写入以下代码:

{
  "name": "nlpjs-web",
  "version": "1.0.0",
  "scripts": {
    "build": "browserify ./buildable.js | terser --compress --mangle > ./dist/bundle.js",
  },
  "devDependencies": {
    "@nlpjs/core": "^4.14.0",
    "@nlpjs/lang-en-min": "^4.14.0",
    "@nlpjs/nlp": "^4.15.0",
    "@nlpjs/request-rn": "^4.14.3",
    "browserify": "^17.0.0",
    "terser": "^5.3.8"
  }
}

我们可以提前在 index.html 文件内引用后面打包出来的 bundle.js 文件,写入以下代码:

<html>
<head>
    <title>NLP in a browser</title>
    <script src='./dist/bundle.js'></script>
    <script>
        const {containerBootstrap, Nlp, LangEn, fs} = window.nlpjs;

        const setupNLP = async corpus => {
            const container = containerBootstrap();
            container.register('fs', fs);
            container.use(Nlp);
            container.use(LangEn);
            const nlp = container.get('nlp');
            nlp.settings.autoSave = false;
            await nlp.addCorpus(corpus);
            nlp.train();
            return nlp;
        };

        const onChatSubmit = nlp => async event => {
            event.preventDefault();
            const chat = document.getElementById('chat');
            const chatInput = document.getElementById('chatInput');
            chat.innerHTML = chat.innerHTML + `<p>you: ${chatInput.value}</p>`;
            const response = await nlp.process('en', chatInput.value);
            chat.innerHTML = chat.innerHTML + `<p>chatbot: ${response.answer}</p>`;
            chatInput.value = '';
        };

        (async () => {
            const nlp = await setupNLP('https://raw.githubusercontent.com/jesus-seijas-sp/nlpjs-examples/master/01.quickstart/02.filecorpus/corpus-en.json');
            const chatForm = document.getElementById('chatbotForm');
            chatForm.addEventListener('submit', onChatSubmit(nlp));
        })();
    </script>
</head>
<body>
<h1>NLP in a browser</h1>
<div id="chat"></div>
<form id="chatbotForm">
    <input type="text" id="chatInput" />
    <input type="submit" id="chatSubmit" value="send" />
</form>
</body>
</html>

第二步:打包项目

将 buildable.js 源文件打包到 dist/bundle.js 中,运行如下命令:

npm run build

第三步:运行项目

接下来在浏览器中打开 index.html,此时你应该看到了一个可以简单互动的聊天机器人,如下图所示: demo截图

使用它很方便也很香,因为这三步下来,你已经训练了一个人工智能,这种感觉可太棒了。但考虑到浏览器缺少对语料库的安全和隐私保护,以及常见的自然语言处理功能。比如无法提供主动学习、实体提取、个性化需求定制的能力,所以 V1.0 版本我们也只是更多的去探索可能性,有兴趣的同学可以跟着这篇文章把玩一波。

贾维斯 V2.0 版本

鉴于第一版能力的局限,所以我们在开发第二版的时候,依靠着公司强大的资源,同 AI 团队的同学进行协作,底层的训练能力完全由 AI 团队托管。比如使用市面上更流行的 BM25 快速算法进行匹配搜索以及 BERT 进行语义解析构建网络模型等。我们只需要提供标准的训练数据源以及对应的落地页来承载它即可。这样一来,真正意义上打造了一个属于我们公司自己的安全可靠的智能问答机器人。

如何同 AI 团队进行协作呢?我们约定使用 RESTful 标准设计接口来进行底层的通信。这样一来,我们便拥有了更多的交互形态,比如 web 端、移动端、各类插件...,这些场景"贾维斯"都能应付自如。除此之外,我们还赋予了"贾维斯"更加丰富的交互形式,比如支持点赞、点踩、评论...,这些反馈数据后期都会作为负样本进行模型训练。

关于 web 端的搭建,我们使用了流行的 ChatUI 来帮助我们开发。它是一个服务于对话领域的设计和开发体系,助力智能对话机器人的搭建框架,简单几步就可以搭建出来:

使用 ChatUI

编写一个 HTML 文件(index.html)和运行文件(setup.js):

在 index.html 中写入以下代码:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta name="renderer" content="webkit" />
    <meta name="force-rendering" content="webkit" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
    <title>贾维斯</title>
    <link rel="stylesheet" href="//g.alicdn.com/chatui/sdk-v2/0.2.4/sdk.css">
  </head>
  <body>
    <div id="root"></div>
    <script src="//g.alicdn.com/chatui/sdk-v2/0.2.4/sdk.js"></script>
    <script src="//g.alicdn.com/chatui/extensions/0.0.7/isv-parser.js"></script>
    <script src="/setup.js"></script>
    <script src="//g.alicdn.com/chatui/icons/0.3.0/index.js" async></script>
  </body>
</html>

在 setup.js 中写入以下代码:

var bot = new ChatSDK({
  config: {
    navbar: {
      title: '智能助理'
    },
    robot: {
      avatar: '//gw.alicdn.com/tfs/TB1U7FBiAT2gK0jSZPcXXcKkpXa-108-108.jpg'
    },
    messages: [
      {
        type: 'text',
        content: {
          text: '智能助理为您服务,请问有什么可以帮您?'
        }
      }
    ]
  },
  requests: {
    send: function (msg) {
      if (msg.type === 'text') {
        return {
          url: '//api.server.com/ask',
          data: {
            q: msg.content.text
          }
        };
      }
    }
  },
  handlers: {
    parseResponse: function (res, requestType) {
      if (requestType === 'send' && res.Messages) {
        // 解析 ISV 消息数据
        return isvParser({ data: res });
      }

      return res;
    }
  }
});

bot.run();

在浏览器中打开 index.html 即可看到如下:

更详细的使用文档可以参考官网

搭建钉钉机器人

除了以上的载体,我们还深度结合钉钉机器人及钉钉推送的能力做了很多有意思的事。一方面是公司同事都是使用钉钉办公,另一方面是消息推送的即时性能够更好的体现出来。如何开发一个企业内部机器人呢?以下流程可简单快速的搭建一个钉钉机器人:

  1. 登录钉钉开发者后台,依次选择应用开发 > 企业内部开发 > 机器人,点击创建应用。并完善相关的配置机器人的基本信息。
  2. 当用户@机器人时,钉钉会通过机器人开发者的HTTPS服务地址,把消息内容发送出去,报文协议如下。
    {
      "Content-Type": "application/json; charset=utf-8",
      "timestamp": "1577262236757",
      "sign":"xxxxxxxxxx"
    }
  3. 开发者可以根据自己的业务需要,选择回复一段消息到群中。目前支持text、Markdown、整体跳转actionCard类型、独立跳转actionCard类型、feedCard这5种消息类型。详情参考消息类型和数据格式

当然你也可以去自定义更多的消息模版,甚至在钉钉上完成一些复杂人机交互形式,让你的机器人看起来更酷。比如下面这样:

  1. 企业成员使用路径:进入要使用机器人的群依次点击 群设置 > 智能群助手 > 添加机器人,在企业机器人列表中即可找到。

更详细的使用文档可以参考这篇文章《企业内部开发机器人》。这样一来我们便完成了钉钉机器人的搭建,后续使用需要手动在群内引入企业机器人即可。

打通钉钉推送

钉钉推送能力则是使用 ding-bot-sdk 来实现的:

const Bot = require('ding-bot-sdk')

const bot = new Bot({
  access_token: 'xxx', // Webhook地址后的access_token // 必填
  secret: 'xxx' // 安全设置:加签的secret 必填
})

bot.send({
  "msgtype": "text",
  "text": {
      "content": "我就是我, @150XXXXXXXX 是不一样的烟火"
  },
  "at": {
      "atMobiles": [
          "150XXXXXXXX"
      ],
      "isAtAll": false
  }
})

完成以上这些,借助钉钉机器人推送能力,我们的 QA 应答能力便更方便落地。

自动化能力

第二部分是"自动化能力"。自动化是相对人工概念而言的。指的是在没人参与的情况下,利用脚本使被控对象或过程自动地按预定规律运行。其最大好处是可以节省劳动力、将繁琐的动作一键化,我们只需要提前将相应的系统流程编排起来的就可以实现自动化的工作。

"贾维斯"通过指令作为载体来触发对应的脚本。这里我们有一个思考,如何区分用户输入的问题是不是我们内置的指令呢?我们简单的做了两件事。

指令模式

一是内置的指令尽可能的简短,我们定义为某个特定单词或者限定长度的字符,比如:ONCALL、值班、前端小报、百策这种关键词触发背后对应的脚本。

当然,除了纯指令的场景,我们还支持通过交互形式获取到更多的参数,支持更多自定义的能力。如下所示:

这部分能力的实现只需要确定好一个参数标识,通过截取就可以拿到。类似于我们 npm install webpack --save 中定义为 -- 来作为参数标识。稍微复杂的一个例子是,我们可以直接打通公司的基建百策系统,为我们提供页面检测功能,如下所示:

阈值计算模式

二是基于阈值进行弱匹配模式,我们会基于用户的问题分数进行检测,只有在低于阈值时才会走指令弱匹配模式并提示相关指令使用方式。这很类似于我们在终端使用脚手架拼错单词时,大部分 CLI 都会有一个很友好的提示一样: 下面是伪代码实现方式:

const THRESHOLD = 0.25;
const questionStr = '今天谁值班';
const instructionMap = [
    {
        instruction: '值班',
        handler: () => console.log('获取当前值班人员'),
    },
    {
        instruction: 'oncall',
        handler: () => console.log('触发ONCALL相关'),
    },
];

const { scroe, qaAns } = await getQA(questionStr);
if (scroe > THRESHOLD) {
    return qaAns;
}
// 获取到指令及对应的脚本动作
const [{ instruction, handler }] = instructionMap
    .filter(({ instruction }) => questionStr.indexOf(instruction) > -1);
return handler();

实现的效果如下所示:

系统编排

除了单一系统实现自动化外,我们还可以将多个系统的核心逻辑进行编排组合。比如"贾维斯"的 ONCALL 能力,便是基于我司的值班系统、ICS 系统、语音系统及贾维斯内置的工单系统组合而成的一套解决线上问题告警及跟踪的方案。

再结合"贾维斯"自身的 AI 能力,不仅可以对一个 ONCALL 问题进行跟踪管理,还可以把 ONCALL 结单的数据反哺给 QA 进行训练,这样便形成了 ONCALL 提问,QA 解答的闭环能力。

提问同学视角:

值班同学视角:

下次用户再遇到类似的问题时,只需要问"贾维斯"就可以获取答案。于用户而言,我们只需要去使用贾维斯即可,背后的那些自动化动作则全部由"贾维斯"托管。

推广落地

首先我们需要肯定自己的产品,并清楚自己做的产品是有价值的,可以从以下角度思考:

  1. 解决了什么问题
  2. 如何解决
  3. 用户及场景分析

智能机器人的定位就是帮助同学们解决常见 QA 的应答以及自动化的能力。明白了产品价值,下面一步就是推广使用。

本着以小到大的策略,我们选择从身边的研发同学推广起来。研发同学更多的是诉求问题,比如支持脚本录入 QA 数据、支持外部系统一键接入"贾维斯"...,通过不断的调研,我们完成了其中部分"需求",不断优化体验甚至是添加功能,目的是留住这些宝贵的 VIP 用户。初期的用户口碑是非常重要的,他们能给产品带来更多的推广。

最后一步就是收集用户反馈及使用痛点,可以用问卷调查的形式,也可以通过收集埋点数据进行复盘。目的是更好的清楚自己的产品使用情况。既能做到及时的查漏补缺也能了解到用户的声音。 总体来看就是以一个小点作为突破口,不断探索发现更多的可能。在这个过程中不断去调研并进行能力抽象,最后肯定它推广它,后面落地也是很简单的一件事。

总结和未来规划

"贾维斯"的能力体系如下图所示:

未来,"贾维斯"还有很多事情要做,比如支持上下文对话,能实现更精准的 QA 能力,或者打通更多的外部系统,沉淀出一套通用的编排能力,又或者是更好的分析同学们遇到的真实问题,合力解决问题,提升辛福感。未来希望"贾维斯"能够小而美,巧而强,这一切都未完待续......。

❉ 作者介绍 ❉