切换日光/暗黑模式
042. AI 流式更新表单实现
学习目标
这一节把上一节的流式更新原理写成可运行逻辑。
学完后,你应该能理解:
- 如何收集 AI 每次返回的 chunk;
- 为什么要从
data_start之后才开始处理; bufferText为什么必须放在循环外;- 如何只解析已经完整的行;
- 为什么流式更新时要暂时隐藏预览区;
useBufferStreamHandler如何把复杂逻辑封装起来。
从 chunk 开始理解
流式响应会把内容拆成很多小片段。
前端先监听 AI 消息更新事件,把每次返回的文本片段收集起来。
这一步不是为了马上展示所有内容,而是为了观察真实返回格式:哪些内容是思考、哪些内容是标记、哪些内容才是表单字段和值。
只处理 data_start 之后的内容
AI 返回的文本前面可能包含说明、思考或其他非结构化内容。
真正要写入表单的数据,从约定标记之后开始。
所以解析逻辑需要先判断是否进入数据区域。
还没有进入数据区域时,chunk 只继续累积,不写表单。
bufferText
bufferText 用来保存还没有处理完的文本。
每次收到新的 chunk,都要追加到同一个变量里:
ts
bufferText += chunkText;这个变量必须放在循环或回调外面。
如果每次收到 chunk 都重新创建一个空字符串,前面的未完成内容就会丢失,后续解析自然不会稳定。
找最后一个换行符
一条字段更新通常按行分隔。
但 chunk 可能只返回半行。
所以处理时要找最后一个换行符:
- 换行符前面的内容,可以认为是完整行;
- 换行符后面的内容,可能还没返回完,要继续留在
bufferText。
这样就不会把半截字段误解析成完整数据。
解析完整行
完整行再按等号拆成路径和值。
例如:
txt
basicInfo.0.value=13800000000解析后得到:
- path:
basicInfo.0.value - value:
13800000000
然后把 value 写入 path 对应的表单位置。
为什么不要重复解析全部内容
如果每次收到 chunk 都从头解析全部文本,数据越多越慢。
更合理的方式是:
- 只处理已经完整的新行;
- 处理完就从 buffer 里移除;
- 保留最后那段未完成文本。
这也是流式更新能保持顺滑的关键。
常见分隔选择
当前设计用换行符分隔字段。
它的限制是:字段值里不适合再包含换行。
如果后续需要支持多行文本,可以让 AI 在每个字段末尾输出一个特殊结束标记。
分隔符没有绝对标准,重点是前后端和提示词要保持同一套协议。
预览区要暂时隐藏
流式更新时,表单数据处于半成品状态。
例如某个列表项刚生成标题,后续内容还没生成。
如果这时预览组件马上渲染,可能会因为字段缺失报错。
即使不报错,预览区也会频繁重绘,性能会变差。
更稳的做法是:
- AI 更新开始时,切到表单数据视图;
- 预览区显示加载状态;
- 更新完成后,再切回预览视图。
useBufferStreamHandler
useBufferStreamHandler 封装了这套流程。
它负责:
- 接收 chunk;
- 维护 buffer;
- 判断是否进入数据区域;
- 提取完整行;
- 解析路径和值;
- 调用表单更新函数;
- 在完成时触发收尾逻辑。
这样后续做发票识别、简历翻译、表单生成时,都可以复用同一套流式字段更新能力。
这一节的重点
这一节的核心不是某个 UI,而是流式结构化数据的处理方法。
你需要记住:
- chunk 不是完整数据;
- buffer 负责拼接不完整内容;
- 只处理完整行;
- 已处理内容要移除;
- 更新期间不要让复杂预览组件吃半成品数据。