Skip to content
签到签到

进阶第 6 章:前端流式运行时

业务问题

流式 token 高频到达,如果每次 token 都触发 React 整树更新,页面会卡。前端需要一个稳定、可恢复、可测试的运行时。

交互图解:React-free 前端流式运行时 · 查看图解导读

核心原理

把业务状态和 React 分离:

text
业务对象层
-> 不可变快照
-> 通知合批
-> React useSyncExternalStore

业务对象层负责:

  • 连接。
  • 接收事件。
  • 累积文本。
  • 工具调用树。
  • 会话恢复。

React 只负责投影。

真实项目中的映射

AI 编码助手前端需要:

  • 流式显示代码。
  • 展示工具调用和 diff。
  • 取消当前生成。
  • 断线后恢复。
  • 保持消息顺序。

DeepSeek Harness 对应实现

DeepSeek Harness Web Client 使用:

  • SessionManager
  • Session
  • ConversationNodeAssembler
  • Notifier
  • useSyncExternalStore

业务对象层没有 React import。

进一步阅读:

  • .agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md
  • packages/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。
  • 能说明重连去重的基本方案。