模拟类似 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. 添加前端缓存策略

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

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

相关文章

【训练细节解读】文本智能混合分块(Mixtures of Text Chunking,MoC)引领RAG进入多粒度感知智能分块阶段

RAG系统在处理复杂上下文时,传统和语义分块方法的局限性,文本分块的质量限制了检索到的内容,从而影响生成答案的准确性。尽管其他算法组件有所进步,但分块策略中的增量缺陷仍可能在一定程度上降低整体系统性能。如何直接量化分块质量?如何有效利用大型语言模型(LLMs)进行…

IMA+DeepSeekR1+本地知识库撰写NOIP2008普及组T3【传球游戏】题解

目录 一、提问词 二、DeepSeekR1回复 题目描述 解题思路 实现代码 代码说明 三、说明 【IMADeepSeekR1本地知识库】撰写NOIP2008普及组复赛题解系列 1、IMADeepSeekR1本地知识库撰写NOIP2008普及组T1【ISBN 号码】题解-CSDN博客 2、IMADeepSeekR1本地知识库撰写NOIP200…

Nginx正向代理HTTPS配置指南(仅供参考)

要使用Nginx作为正向代理访问HTTPS网站&#xff0c;需通过CONNECT方法建立隧道。以下是操作详细步骤&#xff1a; 1. 安装Nginx及依赖模块 需要模块&#xff1a;ngx_http_proxy_connect_module&#xff08;支持CONNECT方法&#xff09;。 安装方式&#xff1a;需重新编译Nginx…

Python 实现机器学习的 房价预测回归项目

项目目标&#xff1a; 基于房屋特征&#xff08;如房间数、地理位置等&#xff09;预测加州地区的房价中位数。 使用 Python 实现机器学习的 房价预测回归项目&#xff08;使用 California Housing 数据集&#xff09; 环境准备 # 安装必要库&#xff08;若未安装&#xff09…

聚力·突破·共赢|修饰组学服务联盟正式成立,共启协同发展新篇章

2025年3月13日&#xff0c;上海——由中科新生命、杭州微米生物、广科安德、承启生物、派森诺生物、胡珀生物等十余家行业标杆企业共同发起的“修饰组学服务联盟”成立仪式在上海紫竹新兴产业技术研究院隆重举行。联盟以“聚力突破共赢”为主题&#xff0c;致力于整合修饰组学全…

【Docker项目实战】使用Docker部署serverMmon青蛇探针(详细教程)

【Docker项目实战】使用Docker部署serverMmon青蛇探针 一、serverMmon介绍1.1 serverMmon 简介1.2 主要特点二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载serverMmon镜像五、…

力扣刷题(数组篇)

日期类 #pragma once#include <iostream> #include <assert.h> using namespace std;class Date { public:// 构造会频繁调用&#xff0c;所以直接放在类里面&#xff08;类里面的成员函数默认为内联&#xff09;Date(int year 1, int month 1, int day 1)//构…

【通缩螺旋的深度解析与科技破局路径】

通缩螺旋的深度解析与科技破局路径 一、通缩螺旋的形成机制与恶性循环 通缩螺旋&#xff08;Deflationary Spiral&#xff09;是经济学中描述价格持续下跌与经济衰退相互强化的动态过程&#xff0c;其核心逻辑可拆解为以下链条&#xff1a; 需求端萎缩&#xff1a;居民消费信…

单一责任原则在Java设计模式中的深度解析

在软件开发中&#xff0c;设计模式提供了一种解决特定问题的思路。在众多的设计原则中&#xff0c;单一责任原则&#xff08;Single Responsibility Principle&#xff0c;SRP&#xff09;是一个非常重要的概念。它主要强调一个类应该只有一个责任&#xff0c;也就是说&#xf…

开源后台管理系统推荐

前言 在当今数字化时代&#xff0c;企业和组织对于管理和运营资源的需求日益增加。开源后台管理系统应运而生&#xff0c;为用户提供了一个灵活、可定制化的管理平台。本文将介绍开源后台管理系统的概念和优势&#xff0c;探讨常见的开源后台管理系统&#xff0c;以及如何选择…

原生微信小程序实现导航漫游(Tour)

效果&#xff1a; 小程序实现导航漫游 1、组件 miniprogram/components/tour/index.wxml <!--wxml--> <view class"guide" wx:if"{{showGuide}}"><view style"{{guideStyle}}" class"guide-box"><view class&quo…

Docker容器命令速查表

这是 Docker 的快速参考备忘单。 你可以在这里找到最常见的 Docker 命令。 安装 curl -sSL https://get.docker.com/ | sh sudo chmod 777 /var/run/docker.sock在后台创建和运行容器 $ docker run -d -p 80:80 docker/getting-started-d - 以分离&#xff08;后台&#xff0…

Qt QML实现鼠标自由选择不规则区域进行截图

背景 不规则区域进行截图是一种常见的应用场景&#xff0c;通常用于程序截图工具或者图像处理软件中。主要是为了让用户可以自由选择任意形状的区域进行截图&#xff0c;而不仅仅局限于矩形区域。这样用户可以更灵活地进行截图操作&#xff0c;特别是对于需要截取特定形状区域…

SpringDataRedis存储Redis的数据序列化

在使用Spring Data Redis存储数据至Redis时&#xff0c;选择合适的序列化策略至关重要。它不仅影响数据存储的效率和空间利用率&#xff0c;还关系到跨语言兼容性和系统的扩展性。适当的序列化方式可以确保数据正确无误地被存储和读取&#xff0c;提升系统的稳定性和维护性&…

交易系统【三】网关

第二章本来是要讲消息总线&#xff0c;审核说是过度宣传&#xff0c;就放弃了&#xff0c;不纠结&#xff0c;先跳过。 网关和消息总线的底层技术都和网络相关&#xff0c;两者也有很重要的差别。消息总线主要用于内网&#xff0c;受交换机和网卡影响比较大&#xff0c;网络状…

eNSP中路由器的CON/AUX接口、GE Combo接口、Mini USB接口、USB接口、WAN侧uplink接口、FE接口、GE接口介绍

路由器常见接口的详细介绍及其应用示例&#xff1a; 1. CON/AUX 接口 全称&#xff1a;Console/Auxiliary&#xff08;控制台/辅助接口&#xff09;作用&#xff1a; CON&#xff08;Console&#xff09;&#xff1a;通过命令行界面&#xff08;CLI&#xff09;直接配置路由器…

iOS底层原理系列04-并发编程

在移动应用开发中&#xff0c;流畅的用户体验至关重要&#xff0c;而并发编程是实现这一目标的关键技术。本文将深入探讨iOS平台上的并发编程和多线程架构&#xff0c;帮助你构建高性能、响应迅速的应用程序。 1. iOS线程调度机制 1.1 线程本质和iOS线程调度机制 线程是操作…

Vmware下的openEuler

1.下载openEuler操作系统镜像 https://repo.openeuler.org/openEuler-20.03-LTS/ISO/ 2.在VM新建虚拟机 3.虚拟机联网 我是出现了没有网络&#xff0c;ping不通的问题 参考&#xff1a;https://blog.csdn.net/FHY26828/article/details/140941234 修改文件&#xff1a; 在…

带宽管理配置实验

一、实验拓扑 配置流程&#xff1a; 1、带宽通道&#xff1a;整体带宽、每个用户带宽、连接数、优先级信息 2、带宽策略 3、策略通道&#xff0c;引用 4、配置接口出入带宽 二、实验需求和配置 1、基础配置 接口配置 [dianxin]interface GigabitEthernet 0/0/0 [dianxin-G…

【STM32】从新建一个工程开始:STM32 新建工程的详细步骤

STM32 开发通常使用 Keil MDK、STM32CubeMX、IAR 等工具来创建和管理工程。此处是 使用 Keil MDK5 STM32CubeMX 创建 STM32 工程的详细步骤。 新建的标准库工程文件已上传至资源中&#xff0c;下载后即可直接使用。 标准库新建 STM32 工程的基本目录结构&#xff1a;STD_STM…