当前位置: 首页 > article >正文

模拟类似 DeepSeek 的对话

以下是一个完整的 JavaScript 数据流式获取实现方案,模拟类似 DeepSeek 的对话式逐段返回效果。包含前端实现、后端模拟和详细注释:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>流式对话演示</title><style>#output {border: 1px solid #ccc;padding: 20px;margin: 20px;min-height: 200px;font-family: monospace;white-space: pre-wrap;}.loading {display: none;color: #666;}</style>
</head>
<body><button onclick="startStream()">开始对话</button><div id="output"></div><div id="loading" class="loading">思考中...</div><script>// 流式对话核心逻辑class StreamDialog {constructor() {this.output = document.getElementById('output');this.loading = document.getElementById('loading');this.controller = null;  // 用于中止请求}// 开始流式对话async start() {try {this.showLoading();this.output.textContent = '';// 创建中止控制器this.controller = new AbortController();// 发起 fetch 请求const response = await fetch('/stream', {signal: this.controller.signal,headers: {'Content-Type': 'application/json'}});if (!response.ok) throw new Error(response.statusText);if (!response.body) throw new Error("ReadableStream not supported");// 创建读取器const reader = response.body.getReader();const decoder = new TextDecoder();// 持续读取流数据while (true) {const { done, value } = await reader.read();if (done) break;// 解析数据块const chunk = decoder.decode(value);const lines = chunk.split('\n').filter(line => line.trim());for (const line of lines) {try {const event = JSON.parse(line.replace('data: ', ''));this.appendContent(event.content);} catch (e) {console.error('解析错误:', e);}}}} catch (error) {if (error.name !== 'AbortError') {this.appendContent('\n[对话异常终止]');}} finally {this.hideLoading();this.controller = null;}}// 中止对话abort() {if (this.controller) {this.controller.abort();}}// 添加内容到输出区appendContent(text) {// 模拟打字机效果let currentIndex = 0;const animate = () => {if (currentIndex < text.length) {this.output.textContent += text[currentIndex];currentIndex++;requestAnimationFrame(animate);this.output.scrollTop = this.output.scrollHeight; // 自动滚动}};animate();}showLoading() {this.loading.style.display = 'block';}hideLoading() {this.loading.style.display = 'none';}}// 初始化对话实例const dialog = new StreamDialog();// 启动对话function startStream() {dialog.start();}// 中止对话function stopStream() {dialog.abort();}</script>
</body>
</html>

后端 Node.js 实现(Express):

const express = require('express');
const app = express();
const port = 3000;// 允许跨域
app.use((req, res, next) => {res.header('Access-Control-Allow-Origin', '*');next();
});// 流式端点
app.get('/stream', (req, res) => {// 设置流式响应头res.writeHead(200, {'Content-Type': 'text/event-stream','Cache-Control': 'no-cache','Connection': 'keep-alive'});// 模拟的对话数据const responseText = ["你好!我是 DeepSeek 的智能助手。","我正在分析您的问题...","根据现有数据,建议如下:\n","1. 首先检查网络连接\n","2. 验证 API 密钥有效性\n","3. 查看服务状态面板\n","\n需要更详细的帮助吗?"];// 发送流式数据let index = 0;const sendChunk = () => {if (index < responseText.length) {const chunk = {content: responseText[index],finished: index === responseText.length - 1};// SSE 格式要求res.write(`data: ${JSON.stringify(chunk)}\n\n`);index++;setTimeout(sendChunk, 500); // 控制发送速度} else {res.end();}};sendChunk();// 处理客户端断开连接req.on('close', () => {console.log('客户端断开连接');res.end();});
});app.listen(port, () => {console.log(`服务运行在 http://localhost:${port}`);
});

核心实现原理:

  1. 前端实现:
  • 使用 Fetch API + ReadableStream 处理流式数据
  • 支持请求中止(AbortController)
  • 实现打字机动画效果
  • 自动滚动保持内容可见
  • 加载状态指示
  1. 后端实现:
  • 使用 Server-Sent Events (SSE) 协议
  • 分块发送 JSON 格式数据
  • 模拟真实响应延迟(500ms/块)
  • 支持客户端断开检测
  1. 数据格式:
{"content": "当前内容片段","finished": false
}

优化扩展建议:

  1. 性能优化:
// 添加节流控制
const throttle = (func, limit) => {let lastFunc;let lastRan;return function(...args) {if (!lastRan) {func.apply(this, args);lastRan = Date.now();} else {clearTimeout(lastFunc);lastFunc = setTimeout(() => {if ((Date.now() - lastRan) >= limit) {func.apply(this, args);lastRan = Date.now();}}, limit - (Date.now() - lastRan));}};
};// 在 appendContent 中使用
this.appendContent = throttle(this.appendContent, 50);
  1. 错误处理增强:
// 前端添加错误处理
async start() {try {// ...原有逻辑...} catch (error) {if (error.name === 'AbortError') {this.appendContent('\n[对话已中止]');} else {this.appendContent(`\n[错误: ${error.message}]`);console.error('Stream Error:', error);}}
}// 后端添加错误模拟
app.get('/stream', (req, res) => {// 10% 概率模拟错误if (Math.random() < 0.1) {res.writeHead(500);res.end();return;}// ...原有逻辑...
});
  1. 功能扩展:
// 添加 Markdown 支持
appendContent(text) {// 简单 Markdown 解析const parsed = text.replace(/#{3}/g, '<h3>').replace(/#{2}/g, '<h2>').replace(/#{1}/g, '<h1>').replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');// 使用 DocumentFragment 优化渲染const fragment = document.createDocumentFragment();const span = document.createElement('span');span.innerHTML = parsed;fragment.appendChild(span);this.output.appendChild(fragment);
}

这个实现方案完整展示了:

  1. 前后端流式通信的全流程
  2. 实时内容渲染优化技巧
  3. 完整的错误处理机制
  4. 可扩展的架构设计
  5. 用户体验优化细节

实际部署时,建议:

  1. 使用 WebSocket 替代 SSE 实现双向通信
  2. 添加 JWT 鉴权
  3. 实现速率限制(Rate Limiting)
  4. 部署到支持 HTTP/2 的服务器
  5. 添加前端缓存策略

相关文章:

模拟类似 DeepSeek 的对话

以下是一个完整的 JavaScript 数据流式获取实现方案&#xff0c;模拟类似 DeepSeek 的对话式逐段返回效果。包含前端实现、后端模拟和详细注释&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><titl…...

鸿蒙(OpenHarmony)开发实现 息屏/亮屏 详情

官网参考链接 实现点击关闭屏幕&#xff0c;定时5秒后唤醒屏幕 权限 {"name": "ohos.permission.POWER_OPTIMIZATION"}代码实现 import power from ohos.power;Entry Component struct Page3 {private timeoutID: number | null null; // 初始化 timeout…...

Flutter PopScope对于iOS设置canPop为false无效问题

这个问题应该出现很久了&#xff0c;之前的组件WillPopScope用的好好的&#xff0c;flutter做优化打算“软性”处理禁用返回手势&#xff0c;出了PopScope&#xff0c;这个组件也能处理在安卓设备上的左滑返回事件。但是iOS上面左滑返回手势禁用&#xff0c;一直无效。 当然之…...

SpringBoot + ResponseBodyEmitter 实时异步流式推送,优雅!

ChatGPT 的火爆&#xff0c;让流式输出技术迅速走进大众视野。在那段时间里&#xff0c;许多热爱钻研技术的小伙伴纷纷开始学习和实践 SSE 异步处理。 我当时也写过相关文章&#xff0c;今天&#xff0c;咱们换一种更为简便的方式来实现流式输出&#xff0c;那就是 ​​Respon…...

网络运维学习笔记(DeepSeek优化版) 016 HCIA-Datacom综合实验01

文章目录 综合实验1实验需求总部特性 分支8分支9 配置一、 基本配置&#xff08;IP二层VLAN链路聚合&#xff09;ACC_SWSW-S1SW-S2SW-Ser1SW-CoreSW8SW9DHCPISPGW 二、 单臂路由GW 三、 vlanifSW8SW9 四、 OSPFSW8SW9GW 五、 DHCPDHCPGW 六、 NAT缺省路由GW 七、 HTTPGW 综合实…...

02 windows qt配置ffmpeg开发环境搭建

版本说明 首先我使用ffmpeg版本是4.2.1 QT使用版本5.14.2 我选择是c编译 在02Day.pro⾥⾯添加ffmpeg头⽂件和库⽂件路径 win32 { INCLUDEPATH $$PWD/ffmpeg-4.2.1-win32-dev/include LIBS $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib \$$PWD/ffmpeg-4.2.1-win32-dev/l…...

vue-treeselect 【单选/多选】的时候只选择最后一层(绑定的值只绑定最后一层)

欢迎访问我的个人博客 &#xff5c;snows_ls BLOGhttp://snows-l.site 一、单选 1、问题&#xff1a; vue-treeselect 单选的时候只选择最后一层&#xff08;绑定的值只绑定最后一层&#xff09; 2、方法 1、只需要加上 :disable-branch-nodes"true" 就行&#xff0…...

焊接机器人与线激光视觉系统搭配的详细教程

以下是关于焊接机器人与线激光视觉系统搭配的详细教程&#xff0c;包含核心程序框架、调参方法及源码实现思路。本文综合了多个技术文档与专利内容&#xff0c;结合工业应用场景进行系统化总结。 一、系统硬件配置与视觉系统搭建 1. 硬件组成 焊接机器人系统通常由以下模块构…...

微信小程序实现根据不同的用户角色显示不同的tabbar并且可以完整的切换tabbar

直接上图上代码吧 // login/login.js const app getApp() Page({/*** 页面的初始数据*/data: {},/*** 生命周期函数--监听页面加载*/onLoad(options) {},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函…...

Git 本地常见快捷操作

Git 本地常见快捷操作 &#x1f4cc; 1. 基本操作 操作命令初始化 Git 仓库git init查看 Git 状态git status添加所有文件到暂存区git add .添加指定文件git add <file>提交更改git commit -m "提交信息"修改最后一次提交信息git commit --amend -m "新…...

Elastic Stack 8.16.0 日志收集平台的搭建

简介 1.1 ELK 介绍 ELK 是 ‌Elasticsearch‌、‌Logstash‌、‌Kibana‌ 三款开源工具的首字母缩写&#xff0c;构成了一套完整的日志管理解决方案&#xff0c;主要用于日志的采集、存储、分析与可视化‌。 1&#xff09;Logstash&#xff1a;数据管道工具&#xff0c;负责从…...

江科大51单片机笔记【16】AD/DA转换(下)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论知识…...

Podman 运行redis 报错

Podman 运行redis 报错 一、报错内容 find: .: Permission denied chown: changing ownership of .: Permission denied二、问题分析 SELinux 模式 SELinux(Security-Enhanced Linux)是一种安全模块,旨在通过强制访问控制(MAC)来增强 Linux 系统的安全性。SELinux 具有…...

基于深度学习的多模态人脸情绪识别研究与实现(视频+图像+语音)

这是一个结合图像和音频的情绪识别系统&#xff0c;从架构、数据准备、模型实现、训练等。包括数据收集、预处理、模型训练、融合方法、部署优化等全流程。确定完整系统的组成部分&#xff1a;数据收集与处理、模型设计与训练、多模态融合、系统集成、部署优化、用户界面等。详…...

WordPress the_category与single_cat_title的区别

在wordpress网站主题开发用常会用到调用分类目录的名称&#xff0c;the_category与single_cat_title都可以调用出分类目录的名称。 <?php single_cat_title(); ?> <?php the_category(); ?>但是&#xff0c;不少人搞不清楚二者有什么区别&#xff0c;其实很简…...

以太坊AI代理与PoS升级点燃3月市场热情,2025年能否再创新高?

币热网深度报道&#xff1a;以太坊AI代理与PoS升级引爆3月热潮&#xff0c;2025年能否再攀历史新高&#xff1f; 原文来源&#xff1a;币热网 - 区块链信息资讯平台 以太坊升级&#xff0c;市场热情高涨 近期&#xff0c;以太坊市场犹如被一股神秘力量点燃&#xff0c;掀起了…...

Go语言入门基础详解

一、语言历史背景 Go语言由Google工程师Robert Griesemer、Rob Pike和Ken Thompson于2007年设计&#xff0c;2009年正式开源。设计目标&#xff1a; 兼具Python的开发效率与C的执行性能内置并发支持&#xff08;goroutine/channel&#xff09;简洁的类型系统现代化的包管理跨…...

Web网页制作(静态网页):千年之恋

一、是用的PyCharm来写的代码 二、代码中所用到的知识点&#xff08;无 js&#xff09; 这段HTML代码展示了一个简单的注册页面&#xff0c;包含了多个HTML元素和CSS样式的应用。 这段HTML代码展示了一个典型的注册页面&#xff0c;包含了常见的HTML元素和表单控件。通过CSS样…...

JVM 的不同组成部分分别有什么作用?

JVM&#xff08;Java Virtual Machine&#xff09;主要由以下几个核心组成部分构成&#xff1a; 1. 类加载器子系统 (Class Loader Subsystem): 作用&#xff1a; 加载类&#xff1a; 负责查找并加载 Java 类文件&#xff08;.class 文件&#xff09;到 JVM 中。 类文件可以来…...

正则表达式 - 修饰符

正则表达式 - 修饰符 正则表达式是一种强大的文本处理工具,它广泛应用于数据验证、文本搜索、替换、匹配等场景。在正则表达式中,修饰符是用于调整正则表达式行为的特殊字符。本文将详细解析正则表达式中的常用修饰符,帮助读者更好地理解和运用正则表达式。 1. 字符串定位…...

K8s 1.27.1 实战系列(十二)Ingress

一、Ingress介绍 1、Ingress 的核心概念 Ingress 是 Kubernetes 中管理外部流量访问集群内服务的 API 对象,通过 7 层(HTTP/HTTPS)协议实现流量路由、负载均衡和 SSL 终止。它弥补了传统 Service(如 NodePort 和 LoadBalancer)的不足,例如端口占用过多、不支持路径路由…...

tomcat应用的作用以及安装,以及tomcat软件的开机自启动。

一.tomcat介绍 1.作用 tomcat是一款用来部署网站服务器的一款软件。 动态网站主流语言&#xff1a; PHP, lamp/lnmp平台 Java语言&#xff0c;运行在tomcat平台。【只要这个网站或者软件是Java语言写的&#xff0c;我们都可以在tomcat平台上去运行这个java程序。】 网站是…...

Unity中WolrdSpace下的UI展示在上层

一、问题描述 Unity 中 Canvas使用World Space布局的UI&#xff0c;想让它不被3d物体遮挡&#xff0c;始终显示在上层。 二、解决方案 使用shader解决 在 UI 的材质中禁用深度测试&#xff08;ZTest&#xff09;&#xff0c;强制 UI 始终渲染在最上层。 Shader "Custo…...

postgresql 数据库使用

目录 索引 查看索引 创建 删除索引 修改数据库时区 索引 查看索引 select * from pg_indexes where tablenamet_table_data; 或者 select * from pg_statio_all_indexes where relnamet_table_data; 创建 CREATE INDEX ix_table_data_time ON t_table_data (id, crea…...

Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级

一、缓存雪崩&#xff1a; 1、什么是缓存雪崩&#xff1a; 如果缓在某一个时刻出现大规模的key失效&#xff0c;那么就会导致大量的请求打在了数据库上面&#xff0c;导致数据库压力巨大&#xff0c;如果在高并发的情况下&#xff0c;可能瞬间就会导致数据库宕机。这时候如果…...

【SpringMVC】常用注解:@RequestParam

1.作用 如果控制器标注的方法的参数名称与前端传递过来的参数名称不一致&#xff0c;使得SpringMVC无法自动启动&#xff0c;那么我们可以使用该注解实现前后端参数的绑定。 2.相关属性 value/name&#xff1a;这两个属性都是一个作用&#xff0c;都是描述参数的名称&#x…...

JVM垃圾收集器相关面试题(1)

垃圾收集与内存管理摘要 一.核心垃圾收集算法对比 算法原理优点缺点适用场景标记-清除两次遍历&#xff08;标记存活对象→清除未标记对象&#xff09;实现简单内存碎片化、双遍历效率低老年代&#xff08;结合整理&#xff09;标记-复制内存对半分&#xff0c;存活对象复制到…...

详解SQL数据查询功能

数据查询 一、 单表查询1. 选择表中的若干列2. 选择表中的若干元组3. ORDER BY 子句4. 聚合函数5. GROUP BY 子句6. LIMIT 子句综合示例&#xff1a; 二、 多表查询1. 等值连接查询 (Equi-Join)2. 非等值连接查询 (Non-Equi Join)3. 自然连接查询 (Natural Join)4. 复合条件连接…...

车载以太网测试-11【网络层-ICMP协议】

目录 1 摘要2 ICMP协议帧结构2.1 IP头部2.2 ICMP头部2.3 ICMP数据部分2.4 示例&#xff1a;ICMP回显请求&#xff08;Ping请求&#xff09; 3 ICMP协议在车载以太网的应用3.1 为什么需要ICMP&#xff1f;3.1.1 网络连通性测试3.1.2 错误报告3.1.3 网络性能监测3.1.4 路径MTU发现…...

leetcode:728. 自除数(python3解法)

难度&#xff1a;简单 自除数 是指可以被它包含的每一位数整除的数。 例如&#xff0c;128 是一个 自除数 &#xff0c;因为 128 % 1 0&#xff0c;128 % 2 0&#xff0c;128 % 8 0。 自除数 不允许包含 0 。 给定两个整数 left 和 right &#xff0c;返回一个列表&#xff…...