HTMLPAGE 的流式生成技术(Streaming Generation)彻底改变了传统的"请求-等待-响应"模式。通过 Server-Sent Events (SSE) 和增量渲染引擎,我们实现了毫秒级的首字响应(TTFT),让用户能够实时目睹 AI 的思考与构建过程,就像看着一位设计师在眼前现场作画。
🌊 流式传输架构
SSE vs WebSocket
在技术选型上,我们选择了 SSE (Server-Sent Events) 作为主要的流式传输协议,而非 WebSocket。
| 特性 | SSE (Server-Sent Events) | WebSocket | 为什么选择 SSE? |
|---|---|---|---|
| 通讯方向 | 单向 (Server -> Client) | 双向 | 生成过程主要是服务器推送,单向足够 |
| 协议复杂度 | 简单 (基于 HTTP) | 复杂 (自定义协议) | SSE 原生支持 HTTP/2 多路复用和防火墙穿透 |
| 自动重连 | 浏览器原生支持 | 需手动实现 | SSE 内置重连机制,更稳定 |
| 数据格式 | 文本流 (Text Stream) | 二进制/文本 | 适合传输 JSON Patch 或 Markdown 片段 |
流式管道设计
我们的流式生成管道由三个核心阶段组成:
- Token 生成层:LLM 逐个 Token 输出。
- 结构化解析层:将 Token 流实时组装成结构化的 JSON Patch 或 HTML 片段。
- 增量传输层:通过 SSE 通道将差异数据推送到前端。
sequenceDiagram
participant Client
participant Gateway
participant StreamEngine
participant LLM
Client->>Gateway: POST /api/generate (Stream=true)
Gateway->>StreamEngine: Init Generation Task
StreamEngine->>LLM: Prompt Request
loop Token Streaming
LLM-->>StreamEngine: Token "<div>"
StreamEngine-->>StreamEngine: Buffer & Parse
StreamEngine-->>Gateway: SSE Event: { type: "patch", data: "..." }
Gateway-->>Client: SSE Event
Client-->>Client: Incremental Render
end
StreamEngine-->>Gateway: SSE Event: { type: "done" }
Gateway-->>Client: Close Connection
⚡ 增量渲染引擎
虚拟 DOM 补丁策略
前端接收到流式数据后,并非简单地追加 HTML 字符串,而是通过虚拟 DOM 补丁(Virtual DOM Patching)技术,智能地更新页面结构。
增量更新算法示例
class IncrementalRenderer {
constructor(container) {
this.vdom = null;
this.container = container;
}
// 处理 SSE 消息
onPatchReceived(patch) {
// 1. 应用 JSON Patch 到当前状态
const nextState = applyPatch(this.currentState, patch);
// 2. 生成新的虚拟 DOM
const nextVdom = renderToVdom(nextState);
// 3. 计算最小差异 (Diff)
const diff = diffVdom(this.vdom, nextVdom);
// 4. 仅更新变动的 DOM 节点
patchDom(this.container, diff);
// 5. 更新引用
this.vdom = nextVdom;
this.currentState = nextState;
}
}
渐进式水合 (Progressive Hydration)
为了提升交互性能,我们采用了渐进式水合策略。
- 骨架屏 (Skeleton):连接建立瞬间,立即渲染页面骨架。
- 静态内容 (Static Content):优先渲染文本、图片占位符等静态资源。
- 交互逻辑 (Interactivity):当组件结构渲染完成后,异步加载并激活对应的 JavaScript 逻辑。
🛡️ 容错与状态恢复
智能断点续传
网络波动是流式传输的大敌。我们设计了基于 Last-Event-ID 的断点续传机制。
- 服务端缓存:服务端会缓存最近 1 分钟的生成流数据。
- 自动重连:当连接断开时,浏览器会自动发起重连,并带上
Last-Event-ID。 - 无缝恢复:服务端根据 ID 补发丢失的数据包,前端无感知地继续渲染。
乱序处理
虽然 TCP 保证了包序,但在复杂的网络环境下(如 HTTP/3 UDP),应用层仍需处理潜在的数据一致性问题。
- 序列号校验:每个 SSE 事件都带有递增的
seq_id。 - 缓冲队列:前端维护一个缓冲队列,确保严格按
seq_id顺序处理事件。
🚀 性能优化策略
背压控制 (Backpressure)
当生成速度超过前端渲染速度时(例如在低端移动设备上),会导致 UI 卡顿。我们实现了应用层的背压控制。
// 前端流控制器
const streamController = new WritableStream({
write(chunk) {
// 如果渲染队列过长,暂停读取流
if (renderQueue.length > HIGH_WATER_MARK) {
return new Promise(resolve => {
eventBus.once('drain', resolve);
});
}
processChunk(chunk);
}
});
智能分块 (Smart Chunking)
服务端不会每生成一个字符就发送一次 SSE 事件,而是采用智能分块策略,平衡实时性与网络开销。
- 语义分块:尽量在完整的单词、HTML 标签或句子结束时发送数据包。
- 自适应频率:根据网络 RTT 动态调整发送频率(如:50ms - 200ms)。
📊 用户体验设计
视觉平滑处理
为了避免文字逐字跳动带来的视觉疲劳,前端实现了"打字机平滑效果"。
/* CSS 动画平滑过渡 */
.streaming-text-cursor::after {
content: '|';
animation: blink 1s step-end infinite;
}
.new-content-fade-in {
animation: fadeIn 0.3s ease-out;
}
进度预估
虽然流式生成没有确定的结束时间,但我们通过训练一个轻量级的预测模型,根据 Prompt 复杂度和当前生成速度,实时估算剩余时间,缓解用户等待焦虑。
🔗 相关技术文档
流式生成不仅是技术的革新,更是交互范式的升级。HTMLPAGE 让等待不再枯燥,让创作过程本身成为一种享受。