大模型对话前端第一版:先跑通流式响应和取消
大模型对话前端第一版先跑通流式响应和取消第一版大模型对话界面先验证一条主链路接收流式响应、正确解码、可取消地渲染并在异常时给出可恢复的提示。本文以流式 Markdown 的前端实现为例讨论这条链路的最小边界。fetch与TextDecoder足以搭起原型但还要处理不完整的 UTF-8 字节、被截断的事件以及高频更新带来的重复渲染。状态机、Worker 等机制应由测得的瓶颈决定而不是一开始全部引入。MVP 核心链路拆解在第一版交付中前端最核心的链路只有一条接收服务端 Server-Sent Events (SSE) 字节流逐块解码后通过平滑打字机队列推入状态同时控制 Markdown DOM 树的渲染频率。flowchart TD A[SSEResponse 字节流] -- B(ReadableStream DefaultReader) B -- C{TextDecoder Chunk 解码} C -- D[打字机缓冲区队列 Queue] D -- E{requestAnimationFrame 调度器} E -- F[更新 React/Vue State] F -- G[DOM 增量渲染与自动滚动] G -- H{用户触发 AbortController?} H -- 是 -- I[CancelToken 中断底层 HTTP 链接] H -- 否 -- J[流传输结束释放 Worker/Timer]整个链路涉及三个主要瓶颈点网络层EventSourceAPI 不支持 POST 请求与自定义 Header必须改用原生fetch结合ReadableStream。缓冲层后端返回的 Chunk 尺寸不均单次推送可能包含半个 UTF-8 字符或 incomplete JSON直接赋值给 UI 会导致乱码闪烁。渲染层高频setState触发 React Re-render导致 CPU 占用率飙升至 100%。关键代码取舍与实现为了在最小代码量下解决高频渲染卡顿第一版不需要引入复杂的 Web Worker而是采用基于requestAnimationFrame的缓冲队列Buffer Queue。// sse_client.ts export interface SSEOptions { url: string; headers: Recordstring, string; body: object; onChunk: (text: string) void; onError: (err: Error) void; signal?: AbortSignal; } export async function fetchSSEResponse(options: SSEOptions): Promisevoid { const { url, headers, body, onChunk, onError, signal } options; try { const response await fetch(url, { method: POST, headers: { Content-Type: application/json, ...headers, }, body: JSON.stringify(body), signal, }); if (!response.ok || !response.body) { throw new Error(HTTP 异常: ${response.status} ${response.statusText}); } const reader response.body.getReader(); const decoder new TextDecoder(utf-8); let buffer ; while (true) { const { done, value } await reader.read(); if (done) break; buffer decoder.decode(value, { stream: true }); const lines buffer.split(\n); // 保留未完成的最后一行 buffer lines.pop() || ; for (const line of lines) { const trimmed line.trim(); if (!trimmed || trimmed.startsWith(:)) continue; if (trimmed data: [DONE]) return; if (trimmed.startsWith(data: )) { try { const parsed JSON.parse(trimmed.slice(6)); const delta parsed.choices?.[0]?.delta?.content || ; if (delta) onChunk(delta); } catch (e) { // 避免单个解析失败阻断后续流处理 console.warn(Chunk 解析不完整:, trimmed); } } } } } catch (err: any) { if (err.name AbortError) { console.log(请求已被用户主动中断); } else { onError(err); } } }在 UI 层直接将onChunk的回调关联到组件 State 是灾难性的。按照 50ms 一次 Chunk 的频率一秒钟触发 20 次组件树重绘。我们可以通过平滑渲染器截断频率。// useSmoothStream.ts import { useState, useRef, useEffect } from react; export function useSmoothStream() { const [displayText, setDisplayText] useState(); const queueRef useRefstring[]([]); const frameRef useRefnumber | null(null); const pushChunk (chunk: string) { queueRef.current.push(...chunk.split()); if (!frameRef.current) { scheduleFlush(); } }; const scheduleFlush () { frameRef.current requestAnimationFrame(() { if (queueRef.current.length 0) { // 每次渲染取出最多 3 个字符保持流畅打字感 const batch queueRef.current.splice(0, 3).join(); setDisplayText((prev) prev batch); scheduleFlush(); } else { frameRef.current null; } }); }; const reset () { if (frameRef.current) cancelAnimationFrame(frameRef.current); frameRef.current null; queueRef.current []; setDisplayText(); }; return { displayText, pushChunk, reset }; }第一版中果断放弃的技术点离线 VectorDB 本地检索完全在服务端处理前端只负责呈现。自定义 Markdown AST 增量解析引擎直接选用轻量marked或markdown-it加防抖不写自定义 Parser。复杂的多会话并行 WorkerMVP 阶段单次窗口仅支持单条活跃 Request。现场诊断与优化验证线上验证时不能凭感觉判断“卡不卡”。用 Chrome DevTools 的 Performance 面板录制流式输出过程中的 CPU Profile 才是标准动作。打开 Chrome DevTools切到Performance标签页点击 Record开始一次 1000 字的大模型文本生成录制 15 秒后停止。在没有优化前Profile 显示大量的Recalculate Style和Layout频次高达 45 次/秒Task列表中长任务Long Task 50ms占比超过 35%。使用requestAnimationFrame批处理后Layout频次降至 16 次/秒以内。JS 执行线程的火焰图中setDisplayText导致的重绘耗时从 320ms 降至 42ms。内存占用在整个流式传输期间稳定在 38MB 左右没有任何内存泄漏曲线。做 MVP 的核心思维是给网络层加终止器AbortController给渲染层加缓冲闸门rAF给异常加降级保护。三者具备第一版就立住了。

相关新闻

PostgreSQL动态分区裁剪技术:查询性能优化解析

PostgreSQL动态分区裁剪技术:查询性能优化解析

摘要在海量时序数据、业务流水日志、订单交易记录等大表业务场景中,PostgreSQL分区表是解决单表数据量过大、查询延迟攀升、运维成本过高的核心方案。分区裁剪作为分区表查询的核心优化技术,可精准过滤无关分区、避免全分区扫描,大幅缩减IO开…

2026/10/6 19:12:40 阅读更多 →
UTS集群调度系统架构与数据库任务优化实践

UTS集群调度系统架构与数据库任务优化实践

1. UTS集群调度系统的架构演进 UTS(Unified Task Scheduler)作为新一代分布式任务调度平台,其核心架构经历了三次重大迭代。最新版本采用混合调度策略,将中心化调度与分布式调度有机结合,形成了独特的双层调度体系。 …

2026/10/11 12:37:45 阅读更多 →
Unity3D RPG开发实战:从Dungeon Breaker Starter Kit学习商业级游戏架构

Unity3D RPG开发实战:从Dungeon Breaker Starter Kit学习商业级游戏架构

1. 项目概述与核心价值如果你正在寻找一个能让你快速上手Unity3D RPG游戏开发,并且希望深入理解一个商业级项目是如何从零到一构建起来的,那么Dungeon Breaker Starter Kit(以下简称DBSK)绝对是一个不可多得的宝藏。这不仅仅是一套…

2026/10/10 21:09:06 阅读更多 →

最新新闻

如何让AI写出的文字听起来像你本人?ai-copywriter文风校准功能完全指南

如何让AI写出的文字听起来像你本人?ai-copywriter文风校准功能完全指南

【免费下载链接】ai-copywriter An AI copywriter that uses real copywriting skills real marketing knowledge with human tone. 项目地址: https://gitcode.com/gh_mirrors/aic/ai-copywriter 点击查看 免费下载 ai-copywriter 是一款开源 AI 文案技能&#x…

2026/10/14 15:22:16 阅读更多 →
涌痕事态论:有神论是无限倒退,却不肯抛弃“前置预设”

涌痕事态论:有神论是无限倒退,却不肯抛弃“前置预设”

涌痕事态论:有神论是无限倒退,却不肯抛弃“前置预设”岐金兰(余溪)2026年10月14日 共计16112字摘要:本文以任梓远对罗素批评莱布尼茨的反驳为触媒,从涌痕事态论的“事态一元论”出发,重述有神论…

2026/10/14 15:22:16 阅读更多 →
Unity实现时钟指针式拖动旋转:从射线平面法到角度限位

Unity实现时钟指针式拖动旋转:从射线平面法到角度限位

做Unity交互的都知道,鼠标拖拽旋转模型是个高频需求。但如果你搜“Unity 旋转模型”,拿到的多半是自由旋转——鼠标往哪拖模型就往哪转,用来观察物体或者相机环绕,实现很简单。今天我想聊的是另一类:把模型当成时钟指针…

2026/10/14 15:22:16 阅读更多 →
碰碰好评卡功能开发实战指南 从需求梳理到上线全流程经验总结

碰碰好评卡功能开发实战指南 从需求梳理到上线全流程经验总结

# 碰碰好评卡功能开发实战指南 从需求梳理到上线全流程经验总结## 需求分析与功能设计在开发“碰碰好评卡”功能之前,首先需要明确其核心目标。该功能通常用于提升用户互动性、增强平台活跃度,并通过用户间的互评机制增加信任感。根据知识库中提供的多个…

2026/10/14 15:22:16 阅读更多 →
Bootstrap 5.3 手机商城:专为 WebView 优化的轻量级 H5 电商前端

Bootstrap 5.3 手机商城:专为 WebView 优化的轻量级 H5 电商前端

简介:这是一份基于Bootstrap框架开发的轻量级手机商城前端项目,面向Web初学者与中级前端开发者,用于快速掌握响应式移动电商界面开发与前后端联调实践。资源包含69个文件,以45个HTML页面为核心(覆盖首页、商品详情、购…

2026/10/14 15:22:16 阅读更多 →
AutoPlait时序分段Python实现:HMM与MDL参数调优避坑指南

AutoPlait时序分段Python实现:HMM与MDL参数调优避坑指南

简介:这份资源是AutoPlait(SIGMOD14)的Python实现,面向从事时间序列挖掘、模式发现与变化点检测的研究者和工程师。AutoPlait针对大量共同演化时间序列,能在无参数、无需预先训练与调参的前提下,自动找出不…

2026/10/14 15:21:15 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →