基于vue2 + flask实现对话大模型

该文章已生成可运行项目,

基于vue2 + flask实现对话大模型


前言

这篇文章主要讲使用vue2 + flask实现对话大模型的实现历程。在实现过程中,我尝试了两种数据处理方式:
第一种方式是使用axios发送post请求,这种请求方式相对比较简单,前端只需要调用post接口,后端处理完毕后直接返回,前端拿到数据直接显示。
这种实现方式有一种弊端:在后台处理数据比较久时,前端一直会处于一个等待的状态,给用户带来不好的体验。为解决这个弊端,我又尝试了第二种方式。
第二种方式是使用fetch API进行流式返回和调用。这种方式就可以实现模型生成文本的同时逐步将结果发送给客户端,实现实时的交互体验。
注:查资料显示:Axios 是一个流行的 JavaScript HTTP 客户端,它本身并不直接支持流式数据处理

以下将介绍两种方式的实现方法:


一、通过axios请求的方式实现对话

后端

接口:

from flask import Flask
app = Flask(__name__)
app.secret_key = b'_dsay2L"F4Q8z\n\xec]/'

@app.route('/openAiDeekSeek', methods=['post'])
def getAisDataToOpenAi():
	requestParams = request.get_json()
	requestContext = requestParams.get('requestContext')
	openAiResponse = openAi_entry(requestContext)
	return jsonify(code=200, data=openAiResponse)

处理函数:

from openai import OpenAI
client = OpenAI(api_key="xxx", base_url="xxx")
def openAi_entry(requestContext):
	response = client.chat.completions.create(
		model="deepseek-chat",
   		messages=requestContext,
		stream=False
	)
	# 正常输出
    return response.choices[0].message.content

前端

<template>
<div class="diologMain" ref="diologMain">
	<div v-for="(msgItem, index) in message" :key="index">
        <div v-if="msgItem.role === 'user'" class="userContent">
            <div class="msgContent">{{ msgItem.content }}</div>
        </div>
        <div v-else-if="msgItem.role === 'assistant'" class="assistantContent">
            <div class="assisitantItem">
                <img class="assisstantIcon" src="@/assets/img/ui/ai.png" alt="">
                <span class="asssmsg">{{ msgItem.content }}</span>
            </div>
        </div>
    </div>
</div>
</template>

<script>
import { conversationByOnce } from '@/request/modules/dataApi.js'

export default{ 
	name: 'dialogIndex',
    data() {
        return {
            message: []
        }
    },
    methods: {
    	// 调用openai接口 - 一次性请求
        sendToAiRequest(requestData) {
            const that = this
            conversationByOnce(requestData).then(res => {
                if (res.status === 200) {
                    const responseData = res.data.data
                    const assistantMag = { 'role': 'assistant', content: responseData }
                    that.addChatMsgTo(assistantMag)
                } else {
                    that.isDiologLoading = false
                    const errorMsg = { 'role': 'assistant', content: '请求失败,请重试' }
                    that.addChatMsgTo(errorMsg)
                }
            })
        },
        // 增加对话记录
        addChatMsgTo(chatMsg) {
            this.message.push(chatMsg)
            this.userInput = ''
            this.$nextTick(() => {
                this.scrollToBottom()
            })
        },
        // 自动跳转到对话框底部
        scrollToBottom() {
            const diologMainContainer = this.$refs.diologMain
            diologMainContainer.scrollTop = diologMainContainer.scrollHeight
        },
    }
}
</script>

二、通过fetch API流式输出方式实现对话

后端

接口

from flask import Flask
app = Flask(__name__)
app.secret_key = b'_dsay2L"F4Q8z\n\xec]/'

@app.route('/openAiDeekSeek', methods=['post'])
def getAisDataToOpenAi():
	requestParams = request.get_json()
	requestContext = requestParams.get('requestContext')
	return openAi_entry_by_stream(requestContext)

处理函数

from openai import OpenAI
client = OpenAI(api_key="xxx", base_url="xxx")

def openAi_entry(requestType, jsonData, shipInfo):
    #  流式输出
	response = client.chat.completions.create(
		model="deepseek-chat",
		messages=jsonData,
		stream=True
	)
    def generate():
        for chunk in response:
            content = chunk.choices[0].delta.content
            if content:
                print("content:", content, end='', flush=True)
                yield content
    return Response(generate(), content_type="text/plain")

前端 - JavaScript

// 流式发送请求
sendToAiRequest(userMsg, requestData) {
    const that = this
    fetch('/local/openAiDeekSeek', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestData)
    }).then(response => {
			if (response.ok) return response.body
            else throw new Error('Network response was not ok');
        })
        .then(stream => {
            const reader = stream.getReader()
            const decoder = new TextDecoder()
            const assistantMsgIndex = that.message.push({ role: 'assistant', content: '' }) - 1;
            async function readAndLog() {
                const { value, done } = await reader.read();
                if (done) return
                const chunk = decoder.decode(value, { stream: true });
                that.updateMessageAndScroll(chunk, assistantMsgIndex)
                await readAndLog();
            }
            readAndLog().catch(err => console.error('Failed to read stream: ', err));
        }).catch(error => console.error('Failed to fetch stream: ', error))
},

// 判断对话框是否在最底下
isScrolledToBottom(container) {
	return container.scrollTop + container.clientHeight >= container.scrollHeight - 5
},
// 更新数据并滚动
updateMessageAndScroll(chunk, assistantMsgIndex) {
    // 更新现有的对话对象的内容
    this.$set(this.message, assistantMsgIndex, {
        ...this.message[assistantMsgIndex],
        content: this.message[assistantMsgIndex].content + chunk
    });
    // 跳转到最下面
    const diologMainContainer = this.$refs.diologMain
    if (this.isScrolledToBottom(diologMainContainer)) {
        this.$nextTick(() => {
            diologMainContainer.scrollTop = diologMainContainer.scrollHeight;
        });
    }
},

总结

以上仅仅是部分代码作为参考,具体实现逻辑还需根据个人情况完善。总体来说,流式调用更加符合大模型对话中的用户体验,希望读者可以从中得到一些启发。如有问题欢迎各位大佬讨论。


2025年1月22日补充

记录一下部署到nginx服务器时,流式输出效果消失:
个人理解:nginx服务器默认并不支持流式接收数据,需要修改配置手动打开流式接收数据模式。
在服务器中,容器中的nginx配置项如果没有映射到宿主机上,则nginx配置项一般在容器中的路径/etc/nginx/nginx.conf,如果映射到宿主机上,在对应的代理处添加配置即可。
在配置中的代理添加以下配置:

		proxy_buffering     off; # 关键:关闭代理缓冲    
        proxy_http_version  1.1; # 使用 HTTP/1.1 支持分块传输
        proxy_set_header    Connection ""; # 清除 Connection 头,避免 close 导致问题
        proxy_set_header    Host $host;
  1. 配置入口文件:/etc/nginx/nginx.conf:
http {                       
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;                  
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '            
                      '"$http_user_agent" "$http_x_forwarded_for"';                                         
    access_log  /var/log/nginx/access.log  main;                                                                                     
    sendfile        on;                                                      
    #tcp_nopush     on;                                                                                            
    keepalive_timeout  65;                                                   
    #gzip  on;               
    include /etc/nginx/conf.d/default.conf; # 默认的配置路径
} 
  1. 默认配置文件:/etc/nginx/conf.d/default.conf
 location /openApi/ {            
        proxy_pass http://xxx/;      
                                
        proxy_buffering     off;     
        proxy_http_version  1.1;      
        proxy_set_header    Connection "";
        proxy_set_header    Host $host;
    } 
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值