进阶第 6 章:前端流式运行时
业务问题
流式 token 高频到达,如果每次 token 都触发 React 整树更新,页面会卡。前端需要一个稳定、可恢复、可测试的运行时。
交互图解:React-free 前端流式运行时 · 查看图解导读
核心原理
把业务状态和 React 分离:
text
业务对象层
-> 不可变快照
-> 通知合批
-> React useSyncExternalStore业务对象层负责:
- 连接。
- 接收事件。
- 累积文本。
- 工具调用树。
- 会话恢复。
React 只负责投影。
真实项目中的映射
AI 编码助手前端需要:
- 流式显示代码。
- 展示工具调用和 diff。
- 取消当前生成。
- 断线后恢复。
- 保持消息顺序。
DeepSeek Harness 对应实现
DeepSeek Harness Web Client 使用:
SessionManagerSessionConversationNodeAssemblerNotifieruseSyncExternalStore
业务对象层没有 React import。
进一步阅读:
.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.mdpackages/client/runtime/src/client/sessions/
代码示例
ts
type Listener = () => void
class StreamingSession {
private text = ''
private snapshot = { text: '', status: 'idle' }
private listeners = new Set<Listener>()
append(delta: string): void {
this.text += delta
this.snapshot = { text: this.text, status: 'streaming' }
this.emit()
}
complete(): void {
this.snapshot = { text: this.text, status: 'completed' }
this.emit()
}
getSnapshot() {
return this.snapshot
}
subscribe(listener: Listener) {
this.listeners.add(listener)
return () => this.listeners.delete(listener)
}
private emit() {
for (const listener of this.listeners) listener()
}
}真实实现还需要重连游标、消息去重、断流恢复和工具状态。
面试追问
问:为什么业务对象层要脱离 React?
回答要点:
- 便于测试。
- 避免高频更新影响 React。
- 可替换 UI 框架。
- 更清楚表达状态机。
问:如何防止重复消息?
回答要点:
- 服务端给事件分配单调序号。
- 前端记录最后收到的序号。
- 重连后从游标继续。
- 对相同序号做幂等处理。
问:断流后如何恢复?
回答要点:
- 保留事件游标。
- 重连时请求缺失区间。
- 与本地已有状态合并。
- 标记断流前后状态。
实践任务
实现一个简化 StreamingSession,支持 append、complete、subscribe 和 getSnapshot,再用 React 组件订阅。
验收标准
- 能解释 React-free object layer 的价值。
- 能实现最小 observable snapshot。
- 能说明重连去重的基本方案。