AI大语言模型LLM学习-基于Vue3的AI问答页面

程序员老司机 2024-09-15 13:01:01 阅读 78

系列文章


1. AI大语言模型LLM学习-入门篇

2. AI大语言模型LLM学习-Token及流式响应

3. AI大语言模型LLM学习-WebAPI搭建


前言


在上一篇博文中,我们使用Flask这一Web框架结合LLM模型实现了后端流式WebAPI接口,本篇将基于Vue3实现AI问答页面,本人习惯使用HBuilder进行前端页面的开发,当然各位网友可以选择自己喜欢的前端开发IDE,比如VS Code。


一、设计思路

打开一个主流的AI对话页面,比如我注册的是阿里的通义千问

可以看到页面的效果如图所示:

在这里插入图片描述

根据页面效果,可以大致把内容分为如下3部分:

标题问答对话,右侧为用户输入的问题,左侧为AI的回答;此部分需要自定义组件,左右布局,可复用,采用循环实现底部输入区域,包含输入框及发送按钮


二、编码实现


1.项目新建

在这里插入图片描述

2.项目结构

在这里插入图片描述

3.代码部分

3.1 安装并引入element-plus

<code>npm install element-plus --save

关于element-plus相关组件的使用,参考element-plus官网

mian.js配置

import { createApp } from 'vue'

import App from './App.vue'

import ElementPlus from 'element-plus'

import 'element-plus/dist/index.css'

const app = createApp(App)

//引入element-plus

app.use(ElementPlus)

app.mount('#app')

3.2 api接口进行代理

注意:api接口不能直接在html页面中进行调用,存在跨域访问的问题,需要在vite.config.js添加代理配置。

生产环境部署时可以采用nginx对api接口进行反向代理解决跨域问题。

import { defineConfig } from 'vite'

import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/

export default defineConfig({

plugins: [vue()],

server:{

proxy:{

'/chat':{

target:"http://127.0.0.1:2024/",

changeOrigin: true,

},

}

}

})

3.3对话组件

chat.vue代码如下:

<script setup>

import { ref } from 'vue'

defineProps({

msg: Object

})

const count = ref(0)

</script>

<template>

<div class="chat">code>

<!--问题-->

<div style="text-align: right;">code>

<div class="el-card chat-right" >code>

{ -- -->{msg.question}}

</div>

</div>

<!--AI回答-->

<div style="text-align: left;">code>

<div class="el-card chat-left">code>

{ -- -->{msg.answer}}

</div>

</div>

</div>

</template>

<style scoped>

.chat{

max-width: 1000px;

margin: 0 auto;

padding-top: 10px;

padding-bottom: 10px;

}

.ai-img

{

height: 36px;

width: 36px;

}

.chat-left

{

background-color: #f5f6f7!important;

display: inline-block;

box-sizing: border-box;

width: auto;

text-align: left;

border-radius: 12px;

line-height: 24px;

max-width: 100%;

padding: 12px 16px;

white-space: pre-wrap;

}

.chat-right

{

background-color: #e0dfff;

display: inline-block;

box-sizing: border-box;

width: auto;

color: #3f3f3f;

border-radius: 12px;

line-height: 24px;

max-width: 100%;

padding: 12px 16px;

white-space: pre-wrap;

}

</style>

3.4主体页面

代码如下:

<template>

<div class="common-layout">code>

<el-container style="height:100%;width:100%;margin: 0 auto;">code>

<el-header style="height: 50px; width: 100% ;backgroundColor:rgba(0,102,255,.06)">code>

<p class="centered-text">AI-历史人物</p>code>

</el-header>

<el-main id="chat">code>

<chat v-for="item in form.msgList" :msg=item></chat>code>

</el-main>

<el-row style="margin: 0 auto;padding-left: 20px;padding-right: 20px;">code>

<div style="width: 100%;">code>

<el-input style="float: left;width: 90%;" @keyup.enter="sendMsg" v-model="form.input"></el-input>code>

<el-button @click="sendMsg" style="float: right; height: 42px;line-height: 42px;" >发送</el-button>code>

</div>

<div style="margin: 0 auto;">code>

<p style="color: red;font-size: 11px;">code>

服务生成的所有内容均由人工智能模型生成,其生成内容的准确性和完整性无法保证,不代表我们的态度或观点

</p>

</div>

</el-row>

</el-container>

</div>

</template>

<script setup>

import { -- --> reactive,nextTick, ref } from 'vue'

import chat from './components/chat.vue'

const form = reactive({

input: '',//输入

msgList:[] //消息列表

});

async function sendMsg()

{

var keyword=form.input;

if(form.input.length>0)

{

var msg={

question:keyword,

answer:"AI生成中..."

};

form.msgList.push(msg);

form.input="";code>

setScrollToBottom();

const response=await fetch('/chat',{ -- -->

method:"post",

headers:{ 'Content-Type':'application/json'},

body:JSON.stringify({

question:keyword

})

});

if (!response.ok) {

throw new Error(`HTTP error! status: ${ response.status}`);

}

const reader = response.body.getReader();

let decoder = new TextDecoder();

let resultData = '';

var str="";code>

msg={ -- -->

question:keyword,

answer:str

};

form.msgList.pop();

form.msgList.push(msg);

while (true) {

const { done, value } = await reader.read();

if (done) break;

resultData = decoder.decode(value);

console.log(resultData);

str+=resultData;

msg={

question:keyword,

answer:str

};

form.msgList.pop();

form.msgList.push(msg);

setScrollToBottom();

}

}

}

/*内容显示过多时自动滑动*/

async function setScrollToBottom() {

await nextTick()

let chat = document.querySelector("#chat")

chat.scrollTop = chat.scrollHeight

}

</script>

<style>

html,body{

width: 100%;

height: 100%;

margin: 0;

padding: 0;

border: 0;

}

#app {

font-family: Avenir, Helvetica, Arial, sans-serif;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

text-align: center;

color: #2c3e50;

height:100%;min-width: 380px;

}

.common-layout{

height: 100%;

}

#chat{

height: calc(100vh - 150px);

}

.el-input{

height: 45px;

border-radius: 12px;

box-sizing: border-box;

}

</style>


运行效果展示


在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。