目录
一、前端编写
我的队友在服务器上部署了大模型,并进行了微调。现在需要做的事我把前端写好和她编写的后端代码进行交互,把用户在小程序内输入文字信息和上传的图像传递到服务器,然后喂给大模型,得到大模型的返回结果后再将结果返回前端显示给用户。
需要往后端传递的信息有 文本 和 图像。上一篇博客写到了我们的图像上传之后暂存在前端,然后我们需要把前端输入的文字信息也暂存前端,通过发送按钮一键发送。
结构的代码如下:
<!--下方的输入框-->
<view class="input" style="{{'bottom:'+KeyboardHeight+'px; position: fixed;'}}">
<!--上传图片的内容-->
<view class="input-picture" bind:tap="open_camera">
<image src="/images/dialogue/camera.png"></image>
</view>
<!--语音识别-->
<view class='input-voice' bind:longpress="startRecord" bind:touchend="stopRecord">
<image src="/images/dialogue/voice.png"></image>
</view>
<!--输入条-->
<scroll-view class="input-textarea">
<textarea placeholder="请输入内容" auto-height bindinput="bindInput" value="{{msg}}"></textarea>
</scroll-view>
<!--发送按钮-->
<button class="input-button" bind:tap="sendMsg">发送</button>
</view>
我们通过bindinput这个函数将输入框的内容传递给msg。
当我们点击button的时候,会执行四个事:
一是把图片、文字封装上flag和type(分别代表是用户或ai发送的信息和是否含有图像信息)暂存到前端,并显示在上方的对话框中。
二是将图像信息和文字信息传递到后端,喂给大模型。
三是把大模型的信息返回到前端。
四是对大模型的信息进行封装然后显示在上方的对话框上。
这一部分只讲第一件事儿,下一部分详细将后三部分。
封装上传的代码如下所示:
console.log(this.data.tempImagePath)
if(this.data.msg.length==0){ //如果啥也没写
Notify({ type: 'success', message: '输入内容不能为空' });
}else{ //有有效信息
if(this.data.tempImagePath.length > 0 ) {
// 如果有图片
this.data.dialogue.push({"flag": 1, "type": "0", "content": this.data.msg, "url": this.data.tempImagePath})
}
else {
this.data.dialogue.push({"flag": 1, "type":"1", "content": this.data.msg})
}
这里会判断文字信息是否为空,和是否有图像存在,以便进行封装。
如果没有文字信息,会报错。
二、与后端连接
我们需要信息传递给后端。通过Process函数将文本信息和图像信息传递给后端。
我们的函数会判断是否含有图像信息,然后通过两种传递方式与后端进行交互。
数据传递到后端后,喂给大模型后返回信息,我们会把它显示到对话框中。
代码如下所示:
/*与后端大模型进行交互*/
Process: function() {
const _this = this;
// 检查数据有效性
if (!this.data.msg && !this.data.tempImagePath) {
console.log("Invalid data");
return;
}
if (this.data.tempImagePath) {
// 如果有图片,使用 wx.uploadFile
console.log("Uploading file from path:", this.data.tempImagePath[0]); // 打印文件路径
wx.uploadFile({
url: 'https://www.aiguider666.asia/process', // 后端服务器地址
filePath: this.data.tempImagePath[0], // 确保这是一个字符串
name: 'image', // 与后端字段名一致
formData: {
text: this.data.msg,
temperature: '0.8',
top_p: '0.4'
},
success: (res) => {
try {
const data = JSON.parse(res.data); // 确保解析响应数据
if (data.result != '') {
_this.setData({
dialogue: _this.data.dialogue.concat({"flag": 0, "type": "1", "content": data.result })
});
var len = this.data.dialogue.length //遍历的数组的长度
this.setData({
dialogue: this.data.dialogue,
scroll_top: 1000 * len,
})
} else {
console.log("Error", data);
}
} catch (e) {
console.log("Response parse error", e);
}
},
fail: (err) => {
console.log("Internet error", err);
}
});
} else {
// 如果没有图片,使用 wx.request
wx.request({
url: 'https://www.aiguider666.asia/qanda', // 后端服务器地址
method: 'POST',
data: {
user_id: this.data.user,
text: this.data.msg,
temperature: '0.8',
top_p: '0.4'
},
header: {
'Content-Type': 'application/x-www-form-urlencoded' // 确保发送正确的内容类型
},
success: (res) => {
if (res.data.result != '') {
_this.setData({
dialogue: _this.data.dialogue.concat({"flag": 0, "type": "1", "content": res.data.result })
});
var len = this.data.dialogue.length //遍历的数组的长度
this.setData({
dialogue: this.data.dialogue,
scroll_top: 1000 * len,
msg:""
})
} else {
console.log("Error", res.data);
}
},
fail: (err) => {
console.log("Internet error", err);
}
});
}
},
三、布局调整
我们在每次对话更新后,对将对话内容滚动到最下方。采取的方式是读取数组长度,然后乘以一个大的数,让其显示保持在最下方。
此外每次输入结束后,我们会清空输入框中的文字和上传的图像,以便进行下一次输入和上传。
代码如下:
this.setData({
tempImagePath: "",
isImage: 0,
})
var len = this.data.dialogue.length //遍历的数组的长度
this.setData({
dialogue: this.data.dialogue,
scroll_top: 1000 * len,
msg:""
})
}
此外,我们发现,真机调试的时候,由于键盘的升起,会挡住我们下方的输入条,因此我们读取键盘的长度,然后将输入框的position设置成fixed,然后计算计算其应该在的位置。
父组件代码如下:
<view class="input" style="{{'bottom:'+KeyboardHeight+'px; position: fixed;'}}">