手把手教你做自然语言理解智能对话的微信小程序【完整源码分享】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011211290/article/details/77247674

闲聊-智能对话:微信小程序详解

重要提醒:第三方私人语音接口已关闭,现已更新至官方语音接口,如有问题请联系博主

重要更新!!!!

现在“智能聊”小程序支持语音输入了!!!!!
完整代码请参考下面这篇文章!!
微信小程序语音聊天智能对话(源码分享)

======================
本文原地址:http://blog.csdn.net/u011211290/article/details/77247674
源代码地址:链接: https://pan.baidu.com/s/1H-QMy_9xb3lJ9eynTbyKgA 密码: 9xx7
公用App Key:80ccf2fdeba243f49c014af42f571e25
公用App Secret:22cd5040b9d347f2b338eeb46f1770be
(把公用的Key和Secret放到app.js中的globalData中)
重要提醒:第三方私人语音接口已关闭,现已更新至官方语音接口,如有问题请联系博主

重要说明:如果需要自定义语法需要按照下面的提示去新建账号

QQ交流群:656580961

小程序名:智能聊

微信扫一扫就可以打开。
二维码 小程序码

这段时间开发了一个智能对话的微信小程序,下面就把这个介绍一下。

0.介绍

界面:

chat

对话:

talk
功能:目前支持闲聊,问时间,问天气,算24点,单位换算,汇率查询,邮政编码,笑话,故事,算数功能。
说明:这些功能可以在语义后台单独添加,不需要更新微信小程序。

1.智能对话接口

首先是对话的接口,用的是OLAMI的接口,可以自己定义需要的对话,也有系统提供的对话模块。
对话模块定义好之后,查看API文档,将对话通过API发送之后就可以得到回答。

API调用代码:用来发送语义的API请求。

  NLIRequest:function(corpus,arg) { // corpus是要发送的对话;arg是回调方法
    var that = this;
    // appkey
    var appkey = that.globalData.NLPAppkey;
    // appsecret
    var appSecret = that.globalData.NLPAppSecret;
    var api = "nli";
    var timestamp = new Date().getTime();
    // MD5签名
    var sign = MD5.md5(appSecret + "api=" + api + "appkey=" + appkey + "timestamp=" + timestamp + appSecret);
    var rqJson = { "data": { "input_type": 1, "text": corpus }, "data_type": "stt" };
    var rq = JSON.stringify(rqJson);
    var nliUrl = that.globalData.NLPUrl;
    // cusid是用来实现上下文的,可以自己随意定义内容,要够长够随机
    var cusid = that.globalData.NLPCusid;
    console.log("[Console log]:NLIRequest(),URL:" + nliUrl);
    wx.request({
      url: nliUrl,
      data: {
        appkey: appkey,
        api: api,
        timestamp: timestamp,
        sign: sign,
        rq: rq,
        cusid: cusid,
      },
      header: { 'content-type': 'application/x-www-form-urlencoded' },
      method: 'POST',
      success: function (res) {
        var resData = res.data;
        console.log("[Console log]:NLIRequest() success...");
        console.log("[Console log]:Result:");
        console.log(resData);
        var nli = JSON.stringify(resData);
        // 回调函数,解析数据
        typeof arg.success == "function" && arg.success(nli);
      },
      fail: function (res) {
        console.log("[Console log]:NLIRequest() failed...");
        console.error("[Console log]:Error Message:" + res.errMsg);
        typeof arg.fail == "function" && arg.fail();
      },
      complete: function () {
        console.log("[Console log]:NLIRequest() complete...");
        typeof arg.complete == "function" && arg.complete();
      }
    })
  }

随机生成设备识别码(识别码用来支持语义中的上下文,每个客户端需要不同的识别码用来识别设备,准确的说是区分客户端)

  // onLaunch()函数,小程序生存周期内只调用一次。
  onLaunch: function () {
    //调用API从本地缓存中获取数据
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
    // 如果cusid(设备识别码)不存在或者长度小于20,,则重新生成。
    var cusid = this.globalData.NLPCusid;
    if (cusid == null || cusid.length < 20){
      this.setCusid();
    }
  },
  // cusid生成函数
  setCusid:function(){
    // 大小写字母和数字
    var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    // 生成30位随机字符串
    var cusidLength = 30,cusid = '';
    for(var i = 0; i < cusidLength; i++){
      var oneStr = str.charAt(Math.floor(Math.random() * str.length));
      cusid += oneStr;
    }
    this.globalData.NLPCusid = cusid;
    console.log("[Console log]:New cusid:" + cusid);
  }

2.对话内容显示

前端显示代码:用的是scroll-view的标签,里面循环输出对话列表,用位置信息标记是用户输入还是系统对话,使用scroll-into-view来滚动到最新的位置。

<view class="container">
  <scroll-view class="scrool-view" scroll-y="true" scroll-with-animation="true" scroll-into-view="{{scrolltop}}" enable-back-to-top="true">
    <view class="chat-list">
      <block wx:for="{{chatList}}" wx:key="time">
        <view id="roll{{index + 1}}" class="chat-left" wx:if="{{item.orientation == 'l'}}">
          <image class="avatar-img" src="/res/image/chat_logo.png"></image>
          <text>{{item.text}}</text>
          <image class="avatar-img"></image>
        </view>
        <view id="roll{{index + 1}}" class="chat-right" wx:if="{{item.orientation == 'r'}}">
          <image class="avatar-img"></image>
          <text>{{item.text}}{{item.url}}</text>
          <image class="avatar-img" src="{{userLogoUrl}}"></image>
        </view>
      </block>
    </view>
  </scroll-view>
  <view id="rollend" class="weui-footer weui-footer__text">语义解析技术由OLAMI提供</view>
  <form bindsubmit="sendChat">
    <view class="ask-input-word">
      <input class="input-big" placeholder="" name="ask_word" type="text" bindconfirm="sendChat" bindinput="Typing" value="{{askWord}}" />
      <button formType="submit" size="mini" disabled="{{sendButtDisable}}">发送</button>
    </view>
  </form>
</view>

【1】scroll-into-view=”{{scrolltop}}”是将对话滚动到最新位置,在js中把最新的id赋给scrolltop,页面会自动滚动到指定位置。
【2】chatList存储对话内容,循环渲染对话框。orientation是左右位置,左边是答案,右边是用户输入。
【3】userLogoUrl是用户头像的url,如果用户不授权使用用户公开信息,则使用默认的用户头像。

其他控制代码

index.js:主要用来控制页面的动态显示和需要的网络请求发送。

var app = getApp();
var that;
var chatListData = [];

Page({
  data: {
    // 自定义欢迎语的语料(界面加载时调用API,通过语义后台定义的回答,返回内容,后面会有语义平台的配置。)
    defaultCorpus:'你都会什么',
    // 用户输入
    askWord: '',
    // 发送按钮disable
    sendButtDisable:true,
    // 用户信息
    userInfo: {},
    // 对话信息
    chatList: [],
    // 对话滚动标志
    scrolltop:'',
    // 用户Logo,如果用户不授权,则使用默认图像
    userLogoUrl:'/res/image/user_default.png',
  },
  onLoad: function () {
    that = this;
    // 取得用户信息,把用户头像放入用户Logo中
    app.getUserInfo(function (userInfo) {
      var aUrl = userInfo.avatarUrl;
      if(aUrl != null){
        that.setData({
          userLogoUrl: aUrl
        });
      }
    });
    // 自定义欢迎语
    this.sendRequest(this.data.defaultCorpus);
  },
  onReady: function () {

  },
  // 监听用户输入
  Typing:function(e){
    var inputVal = e.detail.value;
    var buttDis = true;
    if(inputVal.length != 0){
      var buttDis = false;
    }
    that.setData({
      sendButtDisable: buttDis,
    })
  },
  // 调用语义接口
  sendChat: function (e) {
    let word = e.detail.value.ask_word ? e.detail.value.ask_word : e.detail.value;
    that.addChat(word, 'r');
    that.setData({
      askWord: '',
      sendButtDisable: true,
    });
    // 请求函数
    that.sendRequest(word);
  },
  // 发送网络请求
  sendRequest(corpus){
    app.NLIRequest(corpus, {
      'success': function (res) {
        if (res.status == "error") {
          wx.showToast({
            title: '返回数据有误!',
          })
          return;
        }
        that.NLIProcess(res);
      },
      'fail': function (res) {
        wx.showToast({
          title: '请求失败!',
        })
        return;
      }
    }); 
  },
  // 处理语义(拿到回答)
  NLIProcess: function(res){
    var nlires = JSON.parse(res);
    var nliArray = nlires.data.nli;
    if(nliArray.length == 0){
      wx.showToast({
        title: '返回数据有误!',
      })
      return;
    }
    var answer = nliArray[0].desc_obj.result;
    if(answer == null){
      wx.showToast({
        title: '返回数据有误!',
      })
      return;
    }
    // 增加回答
    that.addChat(answer, 'l');
    var dataArray = nliArray[0].data_obj;
    // 对特殊信息处理,比如新闻中除了回答还有返回的新闻信息
    if(dataArray != null && dataArray.length > 0){
      var objType = nliArray[0].type;
      // 对选择数据进行显示
      if(objType == 'selection' && dataArray.length > 1){
        that.newsProcess(dataArray);
        return;
      }
      // 对新闻单独处理
      if (objType == 'news' && dataArray.length == 1) {
        var title = dataArray[0].title;
        var detail = dataArray[0].detail;
        var news = title + "\n" + detail; 
        that.addChat(news, 'l');
        return;
      }
      // 如果有其他回答,也进行输出
      var content = dataArray[0].content;
      if (content != null && content != answer){
        that.addChat(content, 'l');
      }
    }
    return;
  },
  // 新闻选择数据显示
  newsProcess(selectionArray){
    for(var i = 0; i < selectionArray.length; i++){
      var title = selectionArray[i].title;
      var detail = selectionArray[i].detail;
      var selectiondetail = "[第" + (i+1) + "条]:" + title + "\n" + detail;
      that.addChatWithFlag(selectiondetail, 'l',false);
    }
  },
  // 显示回答,并滚动到最新位置
  addChat: function (word, orientation) {
    that.addChatWithFlag(word, orientation,true);
  },
  // 显示回答并选择是否滚动到最新位置(true为是,false为否)
  addChatWithFlag: function (word, orientation, scrolltopFlag){
    let ch = { 'text': word, 'time': new Date().getTime(), 'orientation': orientation };
    chatListData.push(ch);
    var charlenght = chatListData.length;
    if (scrolltopFlag){
      that.setData({
        chatList: chatListData,
        scrolltop: "roll" + charlenght,
      });
    }else{
      that.setData({
        chatList: chatListData,
      });
    }
  }
})

index.js中的代码是前端的显示逻辑,关于语义处理的部分,因为不同的类型可能返回的数据是不一致的,所以需要针对内容进行特殊处理,就比如“新闻”,因为新闻数据是一个数组,所以需要对其进行循环处理显示。

语义平台配置(index.js中自定义欢迎语)

a.注册并登陆首页,点击账号并进入 应用管理

index

b.新建模块并进入模块

index2

c.新建grammar和定义回答

index3

d.发布
在发布标签页中点击“发布”,使得编写的语法生效

e.配置
应用管理中把新建的模块勾选,即可生效。(NLI模块为自定义,对话系统模块为系统提供)

index4

完成配置之后,就可以更新语法配置来更新欢迎语,不需要更新代码。

最后

总体来说,页面比较简单,但是调整一些内容会比较繁琐。发送按钮的文字目前在开发工具中没有问题,但是在手机上显示会不居中。

源代码地址:https://pan.baidu.com/s/1miT2SXU
公用App Key:80ccf2fdeba243f49c014af42f571e25
公用App Secret:22cd5040b9d347f2b338eeb46f1770be
重要说明:如果需要自定义语法需要按照上面的提示去新建账号

另:写文章不易,感谢打赏!
微信:微信 支付宝:支付宝

其他博客:
自然语言处理-实际开发:用语义开放平台olami写一个翻译的应用

微信小程序IOS端showLoading之后showToast不显示

“欢快”的小程序开发之路


优秀自然语言理解博客文章推荐:

根据OLAMI平台开发的日历Demo

用olami开放语义平台做汇率换算应用

自然语言处理-实际开发:用语义开放平台olami写一个翻译的应用

自定义java.awt.Canvas—趣味聊天

微信小程序+OLAMI自然语言API接口制作智能查询工具–快递、聊天、日历等

热门自然语言理解和语音API开发平台对比

使用OLAMI SDK和讯飞语音合成制作一个语音回复的短信小助手

告诉你如何使用OLAMI自然语言理解开放平台API制作自己的智能对话助手

微信小程序——智能小秘“遥知之”源码分享(语义理解基于olami)

在unity游戏中使用olami实现语音控制


推荐自然语言理解爱好者博客:

http://blog.csdn.net/huangmeimao

http://blog.csdn.net/u011211290

http://blog.csdn.net/u011827504

http://blog.csdn.net/xinfinityx

http://blog.csdn.net/speeds3

http://blog.csdn.net/happycxz

猜你喜欢

转载自blog.csdn.net/u011211290/article/details/77247674