流式架构设计实践

2026-06-09架构流式SSEWebSocket性能

为什么需要流式

Agent 回答通常需要多步推理和工具调用,用户等待时间可达数秒甚至十秒以上。流式输出让用户边看边等,体验改善显著——首字延迟降低到 300ms 以内,用户感知等待时间减少 60%。

流式 vs 非流式

维度非流式流式
首 Token 延迟完整推理后返回~300ms
用户感知等待期不确定边生成边看
实现复杂度简单中等
中间状态反馈可显示"正在检索..."
中断恢复不支持部分支持

SSE (Server-Sent Events) 方案

Agent 系统最推荐的流式方案。单向、基于 HTTP、天然支持断线重连、兼容性好。

// FastAPI SSE 示例
from sse_starlette.sse import EventSourceResponse

async def agent_stream(query: str):
    async for event in agent.generate_stream(query):
        yield {
            "event": event.type,  // "thinking", "tool_call", "token", "done"
            "data": event.data
        }

return EventSourceResponse(agent_stream(query))
// 前端消费
const evtSource = new EventSource("/api/chat/stream?q=...");
evtSource.addEventListener("token", (e) => {
  appendText(JSON.parse(e.data).text);
});
evtSource.addEventListener("done", () => {
  evtSource.close();
});

事件类型设计

  • thinking:Agent 正在思考(显示推理过程或加载动画)
  • tool_call:正在调用工具,带工具名称和参数
  • tool_result:工具返回结果,可展示给用户
  • token:流式生成的文本片段
  • error:错误信息,带错误码
  • done:流结束,附带最终元数据(Token 数、耗时)

中断与恢复

用户中断:发送 AbortController 信号 → 后端取消 LLM 请求 → 清理未完成的工具调用。
网络断线:SSE 自动重连(EventSource 原生支持),带上 lastEventId 恢复。
服务端异常:发送 error 事件,客户端显示错误状态。

实践建议

  • 流式输出配合打字机效果(逐字渲染)优于整段刷新
  • 思考过程和最终回答分开渲染(思考用灰色背景折叠区域)
  • 工具调用结果流式展示——用户能看到 "正在查询数据库..."
  • 前端缓冲使用 requestAnimationFrame 而非 setInterval 渲染
  • 长回答(超过 5 秒)建议在流中定期发送时间戳心跳包
未标记