AI流式渲染打字机效果抖动?节流方案踩坑实录

上线那天我盯着对话框看了半小时,越看越烦------AI 一吐字,整段文字就抖一下,像在打摆子。产品同学截图甩群里:「这是抖屏 bug 吗?」不是 bug,是我自己写的打字机效果,把性能问题写进去了。

记一下我怎么从「一秒重排 60 次」调到「肉眼丝滑」的,中间踩的坑都挺典型。

先搞清楚抖动从哪来

SSE 推过来的 token 是一小块一小块的,有时一次来俩字,有时半个字(UTF-8 多字节被切了)。我最早的写法很朴素:

ini 复制代码
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let full = '';
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  full += decoder.decode(value, { stream: true }); // stream:true 很关键
  setText(full); // 罪魁祸首
}

decoder.decode 不加 stream: true 那次,我吃了个大亏------中文经常显示成「�」,因为一个汉字的三个字节被拆到两次 chunk 里了。加上 stream: true,解码器会缓住不完整的字节,等下一块拼齐再吐。这个坑排查了我一下午。

抖动的真凶是 setText(full)。后端快的时候一秒能 push 三四十次,每次 setState 都触发 React 重渲染整段 Markdown,长回答里有代码块、列表、表格,重排成本极高。FPS 直接掉到 20 出头。

节流,但别用现成的 throttle

第一反应是 lodash throttle,但它有个问题:尾部那次更新可能被丢掉,导致最后几个字显示不全。我改成用 requestAnimationFrame 攒一帧渲染一次:

ini 复制代码
let pending = '';
let rafId: number | null = null;

function pushChunk(chunk: string) {
  pending += chunk;
  if (rafId != null) return;
  rafId = requestAnimationFrame(() => {
    setText(prev => prev + pending);
    pending = '';
    rafId = null;
  });
}

一帧最多渲染一次,天然贴着屏幕刷新率走,60Hz 屏就是 16.6ms 一次,重排次数从一秒 40 次压到 60 次封顶但每次增量极小。结束时记得把残留的 pending flush 一次,别漏字。

打字机的「匀速」是装出来的

光攒帧还不够顺,因为后端吐字本身忽快忽慢------卡 800ms 不动,然后哗一下来 50 个字。用户体感是「一顿一顿」。我加了个本地队列做缓冲,让字符以恒定速度出来:

ini 复制代码
const queue: string[] = [];
function enqueue(s: string) { queue.push(...s); }

function tick() {
  // 队列堆积多就一帧多吐几个,别让缓冲无限膨胀
  const step = Math.max(1, Math.floor(queue.length / 8));
  const out = queue.splice(0, step).join('');
  if (out) setText(prev => prev + out);
  requestAnimationFrame(tick);
}

step 那行是我调了好几轮的------固定一帧吐一个字,遇到长回答能拖到几十秒还没吐完,用户早走了;固定吐十个又太快没了打字感。按积压量动态调速,积压越多吐越快,体验最自然。

一个没解决干净的取舍

Markdown 边流边渲染,代码块在没闭合前 ``````````` 是残缺的,highlight.js 会闪一下未高亮的纯文本再变色。我试过等代码块闭合再渲染,但那样代码块会整块「啪」地出现,破坏打字感。最后选了「先纯文本流出、闭合后再上色」,接受那一下轻微闪烁------两害相权,闪一下比卡顿强。这块到现在我也没找到完美解,有更优雅的思路欢迎评论区拍我。

模型那头我没自己折腾,对话和工具编排直接挂在讯飞 这类 MaaS 上,现成的模型 API 调就完事,我把精力全砸在前端这点丝滑上。

你们做流式渲染抖动是怎么治的?评论区交个底。

相关推荐
笨鸟先飞,勤能补拙28 分钟前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
2601_963749101 小时前
标题:越华环保集团|面向美丽河湖项目的数字化污水治理云边协同采集架构设计
人工智能
沐籽李1 小时前
从溶剂可及表面积SASA理解抗体结构与工程改造
人工智能·药物设计·aidd·sasa
智慧物业老杨1 小时前
物业如何做好预算管理?落地架构逻辑
人工智能·架构
微学AI1 小时前
一根针指向所有方向:挂谷猜想对 LLM Agent 技能-记忆架构的启示
开发语言·人工智能·架构·挂谷猜想
城管不管1 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
boppu1 小时前
布草特殊污渍去渍剂的种类及作用
大数据·人工智能
AIsoft_86882 小时前
会议录音转文字与AI纪要工具推荐:免费额度与核心功能对比指南
人工智能
sphw2 小时前
nixnb: Jupyter Notebook 优雅分享
ide·人工智能·jupyter
mingo_敏2 小时前
DeepAgents : 权限(Permissions)
人工智能·深度学习·langchain