【玩转全栈】—— Django+vue3+讯飞星火API 实现前端页面实时AI答复

技术栈:vue3 + element-plus + axios + pinia + router + Django5 + websocket + 讯飞星火API

本文将实现一个 AI 聊天对话功能,将前端用户输入问题以及之前对话发送给后端,通过 api 访问大模型,返回前端实时对话数据。

调用 讯飞星火API 大家可以看这篇(文A):创作中心-CSDN

前端 vue3 +后端 Django5 连接可以看这篇(文B):【玩转全栈】—— Django 连接 vue3 保姆级教程,前后端分离式项目2025年4月最新!!!_django vue3 前后端分离-CSDN博客

Django5 配置 websocket(文C):【全栈开发】---- 一文掌握 Websocket 原理,并用 Django 框架实现_django websocket-CSDN博客
【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)_django websocket聊天室-CSDN博客

目录

效果预览:

前期准备

代码实现

后端

前端

资源获取


效果预览:

Django连接vue3,接入ai

前期准备

文A 已讲解如何在 Django 调用免费的讯飞星火API 。

文B 已讲解如何连接前端 vue3 、后端 Django5,配置 vite.config 文件代理,后端解决跨域等等。还有如何在前端获取 token ,并在前端发送 Post 请求时以携带该 token 以越过安全验证,使得后端 Django 能接收到数据,这里不过多赘述,结尾也有相关资源可以下载。

文C 以讲解如何在 Django 中配置 websocket 环境,以及如何实现聊天室功能。

没实现的可以先回去实现。

代码实现

后端

Django 配置好 websocket  ,定义 AI 消费者及其路径:

routings.py:

from django.urls import re_path
from . import consumerswebsocket_urlpatterns = [re_path(r'ws/chat/', consumers.ChatConsumer.as_asgi()),
]

consumers.py:

import json
import asyncio
import httpx
from channels.generic.websocket import AsyncWebsocketConsumer
import reclass ChatConsumer(AsyncWebsocketConsumer):async def connect(self):print("AI消费者已连接")await self.accept()async def disconnect(self, close_code):print("AI消费者已断开")passasync def receive(self, text_data):print("获取到的text_data:", text_data)try:text_data_json = json.loads(text_data)print("text_data_json:",text_data_json)if not text_data_json:await self.send(text_data=json.dumps({'error': '问题不能为空',}))returntry:async for chunk in self.call_spark_ai(text_data_json):if chunk == '[DONE]':await self.send(text_data=json.dumps({'done': True,}))else:# 将 AI 的答复推送给前端await self.send(text_data=json.dumps({'message': chunk,}))await asyncio.sleep(0.1)  # 增加延迟,降低推送频率except Exception as e:await self.send(text_data=json.dumps({'error': f'调用 AI 接口失败: {str(e)}',}))except json.JSONDecodeError:await self.send(text_data=json.dumps({'error': '无效的 JSON 数据',}))async def call_spark_ai(self, question):print("已调用 Spark_ai函数")# 讯飞星火 AI API 的 URL 和认证信息url = "https://spark-api-open.xf-yun.com/v1/chat/completions"headers = {"Authorization": "Bearer 你的密钥","Content-Type": "application/json",}data = {"max_tokens": 4096,"top_k": 4,"temperature": 0.5,"messages": question,"model": "4.0Ultra","stream": True,}async with httpx.AsyncClient() as client:async with client.stream("POST", url, headers=headers, json=data) as response:if response.status_code != 200:raise Exception(f"AI 接口返回错误: {response.status_code} {await response.text()}")# 定义正则表达式pattern = r'"content":"(.*?)"'async for line in response.aiter_lines():print(line.strip())if line.strip():try:# 使用正则表达式提取 contentmatch = re.search(pattern, line)if match:content = match.group(1)print("content:", content)if content:yield content  # 只推送 content 部分except Exception as e:print(f"处理消息时出错: {e}")continue

消费者涉及到的内容比较多,下面我将一一解释:

首先注意!由于连的 websocket ,需要频繁地接收客户端发送的消息、向客户端发送消息并保持连接状态。这些操作本质上是 I/O 密集型任务,涉及到网络请求和响应。如果使用同步代码来处理这些任务,线程会阻塞,导致性能瓶颈。而异步代码可以高效地处理大量并发连接,避免线程阻塞。

text_data_json 获取到前端的对话数据,并添加空数据判断。

定义 call_spark_ai() 函数,传入参数是对话列表,通过调用讯飞星火 API ,得到流式数据,通过正则获取到 content 数据,通过 yield 并发式返回。

然后在消费者中异步使用该函数,将返回值返回给前端。

记得在 headers 中添加自己的密钥。

前端

新建一个 Ai_store 用于存储对话数据:

// stores/Ai_store.js
import { defineStore } from 'pinia';export const useAiStore = defineStore('ai', {state: () => ({messages: [],}),actions: {// 添加对话addMessage(role, content) {this.messages.push({ "role":role, "content":content });},// 清空对话列表clearMessages() {this.messages = [];},},
});

定义了一个 messages 用于存储对话,addMessage() 添加对话对话和内容,clearMessage() 使messages 清空,即新建对话。

导入、初始化 pinia ,并定义一些变量:

import { useAiStore } from '../stores/Ai_store';// 初始化 pinia store
const Ai_store = useAiStore();// 定义消息类型
type Message = {role: string;content: string;
};
const messages = ref<Message[]>([]); // 存储当前一轮对话(用户提问和 AI 回答)
const question = ref(''); // 用户输入的问题
const aiResponse = ref<string[]>([]); // AI 的响应数据
let csrfToken: string | null = null; // CSRF Token
let socket: WebSocket | null = null; // WebSocket 连接(全局变量)
let currentAiResponse = ''; // 当前问题的实时回复内容

初始化 websocket 连接:

function initWebSocket() {if (socket) {socket.close(); // 关闭之前的连接}socket = new WebSocket(`ws://localhost:8080/ws/chat/`);// 监听 WebSocket 打开事件socket.onopen = () => {console.log("WebSocket connection opened");};// 监听 WebSocket 消息事件socket.onmessage = (event: MessageEvent) => {console.log("到达websocket消息事件");const data = JSON.parse(event.data);console.log("data:", data);if (data.message) {// 将消息添加到当前响应中currentAiResponse += data.message;// 更新AI回复内容if (messages.value.length > 0) {messages.value[messages.value.length - 1].content = currentAiResponse;}} else if (data.error) {console.error("Error from backend:", data.error);}};// 监听 WebSocket 关闭事件socket.onclose = () => {console.log("WebSocket connection closed");setTimeout(initWebSocket, 5000); // 自动重连,间隔 5 秒};}

socket 路径 ws://localhost:8080/ws/chat/ 要和后端对应起来,保证连接顺利。

onmessage 接受后端返回的消息流,将 message.data 动态加入到 currentAiResponse ,currentAiResponse 动态更新消息。

发送消息:

async function sendQuestion() {// csrfToken验证if (!csrfToken) {console.error("CSRF Token is not available");return;}if (!question.value.trim()) {alert("请输入有效的问题!");return;}try {// 如果有上一轮对话,将其存入 Ai_storeif (messages.value.length > 0) {console.log("messages:", messages);messages.value.forEach(msg => {Ai_store.addMessage(msg.role, msg.content);});}// 清空 messages 并存储新的用户问题messages.value = [];messages.value.push({ role: 'user', content: question.value });// 清空 AI 的响应数据和完整字符串currentAiResponse = "";// 通过 WebSocket 发送问题const join_messages = ref<Message[]>([]);join_messages.value = [...Ai_store.messages];join_messages.value.push({ role: "user", content: question.value })console.log("join_messages:", join_messages)const message = JSON.stringify(join_messages.value);if (socket) {socket.send(message);}console.log("Sent question to WebSocket:", message);// 清空问题输入框question.value = '';// 初始化 AI 回复占位符messages.value.push({ role: 'system', content: '' });} catch (error) {console.error("Error sending question:", error.response?.data || error.message);}}

如何 messages 中有对话数据,则添加至 pinia 中,当作历史对话数据,以在页面上展示之前对话数据,通过 join_messages 构造历史对话数据和当前对话数据,即当前对话中所有对话数据,然后传给后端,后端解析后,传给 讯飞星火,如此形成循环。

组件生命周期:

onMounted(() => {fetchCsrfToken();initWebSocket();});onUnmounted(() => {if (socket) {socket.close();}});

组件挂载则初始化,卸载则断开 socket 连接。

新建对话:

// 新建对话函数function newConversation() {try {// 清空数据Ai_store.clearMessages();messages.value = [];currentAiResponse = '';// 清空用户输入框question.value = '';console.log("新建对话:所有数据已清空");} catch (error) {console.error("Error creating new conversation:", error.message);}}

用户点击按钮调用此函数,所有数据清空,重新开始对话。

计算 html :

// 计算属性:实时拼接并格式化对话记录const formattedResponse = computed(() => {// 合并历史记录和当前问题的实时回复const allMessages = [...Ai_store.messages, ...messages.value];// 格式化消息const formattedMessages = allMessages.map(msg => {return `<strong style="color: ${msg.role === 'user' ? 'blue' : 'green'};">${msg.role === 'user' ? '您:' : 'AI:'}</strong><br>${String(msg.content || '').replace(/(\\n)+/g, '<br>').replace(/\t+/g, '&emsp;')}`;});// 拼接最终的HTML字符串return formattedMessages.join('<br><br>');});

合并所有对话数据,制造格式化消息,返回给页面,用于展示。

页面:

<el-drawerv-model="drawerVisible"direction="ltr":modal="true":close-on-click-modal="true"custom-class="custom-drawer":with-header="false"><div class="drawer-content"><div class="header-not"><h1>AI 对话界面</h1><button @click="newConversation">新建对话</button></div><!-- 输入框 --><div class="fixed-container"><textarea v-model="question" placeholder="请输入问题"></textarea><button @click="sendQuestion" class="send_button">发送问题</button></div><!-- 对话记录 --><div class="response" v-html="formattedResponse"></div></div></el-drawer>

资源获取

本次分享结束,源码也已放入资源:

https://download.csdn.net/download/2403_83182682/90626683

感谢您的观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/77110.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

广东广州一家IPO资产重组疑点重重,信息披露真实性存疑

作者&#xff1a;Eric 来源&#xff1a;IPO魔女 4月18日&#xff0c;广州瑞立科密汽车电子股份有限公司&#xff08;简称“瑞立科密”&#xff09;将接受深交所主板IPO上会审核。公司保荐机构为中信证券&#xff0c;拟募集资金为15.2162亿元。 瑞立科密过往资产重组疑点重重&a…

银河麒麟(Kylin) - V10 SP1桌面操作系统ARM64编译QT-5.15.12版本

银河麒麟(Kylin) - V10 SP1桌面操作系统ARM64编译QT-5.15.12版本 原因 测试Kylin-Desktop-V10-SP1-General-Release-2303-arm64系统下&#xff0c;编译QT-5.15.12版本已做测试。 测试环境 测试板配置 型号&#xff1a;LM-D2000-NONE-1w-V01-pc_A2150 CPU&#xff1a;飞腾D20…

查看前端项目依赖树型结构关系图的详细方法,涵盖 命令行工具 和 可视化工

以下是查看前端项目依赖树型结构关系图的详细方法&#xff0c;涵盖 命令行工具 和 可视化工具&#xff1a; 一、命令行工具生成依赖树 1. npm # 查看项目依赖树&#xff08;文本形式&#xff09; npm ls# 查看指定包的依赖树 npm ls <package-name># 生成JSON格式的依…

Ollama高并发测试

本文主要来测试一下ollama的高并发能力。 具体配置如下&#xff1a; 一、Ollama默认参数执行 我们打开4个窗口&#xff0c;然后分别让DeepSeek “给我讲一个笑话” &#xff0c;看下不同窗口的答题顺序。 通过答题顺序可以看到&#xff0c;在不进行参数设置时&#xff0c;模…

资源管理与HPA:让Kubernetes应用自动伸缩

引言&#xff1a;从“手动挡”到“自动驾驶” 想象我们驾驶一辆汽车&#xff0c;手动调节油门和换挡不仅费力&#xff0c;还难以应对突发状况。我们的应用服务也一样&#xff0c;在面对突然的流量增长&#xff0c;内存使用暴涨该如何应对。HPA&#xff08;Horizontal Pod Auto…

Windows 下 MongoDB ZIP 版本安装指南

在开发和生产环境中&#xff0c;MongoDB 是一种非常流行的 NoSQL 数据库&#xff0c;以其灵活性和高性能而受到开发者的青睐。对于 Windows 用户来说&#xff0c;MongoDB 提供了多种安装方式&#xff0c;其中 ZIP 版本因其灵活性和轻量级的特点&#xff0c;成为很多开发者的首选…

【Linux网络与网络编程】11.数据链路层mac帧协议ARP协议

前面在介绍网络层时我们提出来过一个问题&#xff1a;主机是怎么把数据交给路由器的&#xff1f;那里我们说这是由数据链路层来做的。 网络上的报文在物理结构上是以mac帧的形式流动的&#xff0c;但在逻辑上是以IP流动的&#xff0c;IP的流动是需要mac帧支持的。 数据链路层解…

多模态思维链AI医疗编程:从计算可持续性到开放域推理的系统性解决方案

多模态思维链AI医疗编程:从计算可持续性到开放域推理的系统性解决方案 医疗AI领域的多模态思维链技术正在重塑临床决策支持、医学影像分析和医疗流程优化的范式。本指南从计算可持续性、错误传播控制、伦理安全防护和通用性扩展四大维度,系统解析医疗大模型落地落地的关键要…

代理模式深度解析

目录 一 静态代理 1.1 优点 1.2 缺点 1.3 适用场景 二 JDK动态代理 1 JDK动态代理的工作原理 1.1 创建代理类 1.2 加载代理类 1.3 实现方法调用 2. Proxy.newProxyInstance() 的核心工作流程 方法签名 工作步骤 3. 代理类的生成与加载 3.1 代理类生成的关键方法 …

Spring Cache与Redis集成原理

一、核心架构图解 #mermaid-svg-aiWGQLhmWx7kOfLz {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aiWGQLhmWx7kOfLz .error-icon{fill:#552222;}#mermaid-svg-aiWGQLhmWx7kOfLz .error-text{fill:#552222;stroke:#5…

编程技能:调试02,设置断点与删除断点

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;调试01&#xff0c;调试介绍 回到目录 下…

flink写doris时的优化

1.概念 doris并不擅长高频、小量数据的导入&#xff1b; 因为doris每一次数据导入都会在be节点上生成数据文件&#xff1b;如果高频导入小量数据&#xff0c;就会在存储层产生大量的小文件&#xff08;必然会影响到后续的查询效率&#xff0c;也会对系统产生更多的compaction…

ElementNotInteractableException原因及解决办法

在自动化测试中,ElementNotInteractableException是一个常见的异常,它通常发生在尝试与网页上的某个元素进行交互(例如点击、输入等操作)时,但由于该元素当前不可交互。这可能由多种原因引起,以下是一些常见的原因及其解决方法: 元素未完全加载 如果尝试与页面上的元素交…

如何从 GitHub 镜像仓库到极狐GitLab?

最近 GitHub 封禁中国用户的事情闹得沸沸扬扬,虽然官方发布的报道说中国用户被限制登录是因为配置错误导致,已经撤回了更新,中国用户已经可以正常使用。但是这就像横在国内开发者和企业头上的“达摩克利斯之剑”。为了避免 GitHub 不可用而带来的影响,国内开发者和企业可以…

服务器安装nacos

1.下载依赖 docker pull nacos/nacos-server:v2.4.3安装 docker run -d --name nacos-server -p 8848:8848 -e MODEstandalone nacos/nacos-server:v2.4.3把nacos中的data 文件和conf 文件copy到自己服务的文件夹 docker cp nacos-server:/home/nacos/data /home/admin1/…

Matter协议暗战:苹果、谷歌、亚马逊的智能家居霸权争夺

原文地址&#xff1a;Matter协议暗战&#xff1a;苹果、谷歌、亚马逊的智能家居霸权争夺 一、Matter 协议&#xff1a;巨头联手打造的 “智能家居联合国” 1.1 从 CHIP 到 Matter&#xff1a;标准统一的十年长跑 智能家居发展多年&#xff0c;却始终被 “孤岛效应” 困扰。各…

软件设计师2009-2022历年真题与答案解析(附pdf下载)

软考在即&#xff0c;现在给大家分享一下软件设计师2009-2022真题与答案解析 pdf全套&#xff0c;文末提供大家免费下载&#xff0c;大家都知道在软考备考过程中&#xff0c;拥有一套全面且实用的考试资料对于考生来说至关重要。目录如下&#xff1a; 历年真题及详解2004-2019 …

基于EasyX库开发的球球大作战游戏

目录 球球大作战 一、开发环境 二、流程图预览 三、代码逻辑 1、初始化时间 2、设置开始界面大小 3、设置开始界面 4、让玩家选择速度 5、设置玩家小球、人机小球、食物的属性 6、一次性把图绘制到界面里 7、进入死循环 8、移动玩家小球 9、移动人机 10、食物刷新…

aslist和list的区别

‌Arrays.asList和List的主要区别在于它们的固定长度和不可变性、与原始数组的关系、性能以及使用场景。 一、固定长度和不可变性 ‌Arrays.asList‌&#xff1a;通过Arrays.asList方法创建的List是一个固定长度的List&#xff0c;其长度与原始数组相同。这意味着你不能通过添…

大模型预标注和自动化标注在OCR标注场景的应用

OCR&#xff0c;即光学字符识别&#xff0c;简单来说就是利用光学设备去捕获图像并识别文字&#xff0c;最终将图片中的文字转换为可编辑和可搜索的文本。在数字化时代&#xff0c;OCR&#xff08;光学字符识别&#xff09;技术作为处理图像中文字信息的关键手段&#xff0c;其…