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

那年夏天 2018-08-11

重要更新!!!!

现在“智能聊”小程序支持语音输入了!!!!!

完整代码请参考下面这篇文章!!

微信小程序语音聊天智能对话(源码分享)

======================

本文原地址:http://blog.csdn.net/u011211290/article/details/77247674

源代码地址:https://pan.baidu.com/s/1miT2SXU

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

0.介绍

界面:

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

对话:

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

功能:目前支持闲聊,问时间,问天气,算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();

}

})

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

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

// 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);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

【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 + "" + 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 + "" + 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,

});

}

}

})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154

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

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

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

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

b.新建模块并进入模块

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

c.新建grammar和定义回答

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

d.发布

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

e.配置

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

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

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

最后

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

源代码地址:https://pan.baidu.com/s/1miT2SXU

相关推荐