
流式输出的工程选型与性能对比SSE与WebSocket在Agent产品中的深度评测一、Token到屏幕的最后一公里Agent产品的流式交互焦虑Agent产品的核心竞争力往往不在于推理能力本身而在于最终用户感知到的响应速度。当用户输入一个指令后Agent在后台完成意图理解、工具调用、结果归因等一系列动作。如果这一过程被黑盒化为一段漫长的Loading动画用户的流失率将呈指数级上升。SSEServer-Sent Events与WebSocket是当前实现流式输出的两种主流传输协议。两者的技术本质差异决定了它们在Agent场景下截然不同的性能表现与工程代价。从字节级的数据帧结构到浏览器端的渲染策略每一个决策都在影响终端用户的体感延迟。实测数据显示在5000并发连接的压测下SSE的连接建立开销仅为WebSocket的23%。但在需要双向高频信令交互的Agent多轮对话场景中WebSocket的往返延迟比SSEHTTP轮询低了62%。选型的核心矛盾在于——你的Agent是单向生成为主还是需要高频的状态同步二、协议握手与数据帧的底层博弈两种协议在传输层的核心差异体现在三个维度。第一连接初始化SSE复用标准HTTP连接无需协议升级握手冷启动延迟更低。第二多路复用WebSocket天然支持全双工Agent可在生成过程中接收用户的中断指令而SSE需要额外建立一条POST通道。第三代理兼容性HTTP/2下SSE可能受连接复用限制需要通过显式禁用多路复用来规避。三、生产级Agent流式网关的实现以下代码实现了一个支持SSE与WebSocket双协议的Agent流式网关核心设计在于协议适配层的抽象与连接生命周期的管理。// stream_gateway.go - Agent流式网关核心实现 package gateway import ( context encoding/json sync time ) // StreamProtocol 流式协议抽象 type StreamProtocol int const ( ProtoSSE StreamProtocol iota ProtoWebSocket ) // AgentStreamRequest 统一的流式请求模型 type AgentStreamRequest struct { SessionID string json:session_id Message string json:message Protocol StreamProtocol json:protocol MaxTokens int json:max_tokens Callbacks []ToolCallback json:callbacks,omitempty } // StreamHub 管理Agent会话的流式连接池 type StreamHub struct { mu sync.RWMutex sessions map[string]*AgentSession // 熔断器——WebSocket连接数超过阈值时触发降级 wsConns int32 maxWS int32 // SSE的兜底心跳防止代理层静默断开 sseHeartbeat time.Duration } // AgentSession 单个Agent会话的流式状态 type AgentSession struct { ID string Protocol StreamProtocol CancelFunc context.CancelFunc // 双通道设计Agent消费LLM流同时监听用户中断 llmStream -chan TokenChunk interrupt chan struct{} // SSE场景下的补偿通道——用户中断通过独立HTTP POST投递 sseAbort chan string } // DispatchStream 协议感知的流式分发入口 func (h *StreamHub) DispatchStream( ctx context.Context, req *AgentStreamRequest, writer StreamWriter, ) error { session, err : h.getOrCreateSession(ctx, req) if err ! nil { return err } defer h.cleanupSession(session.ID) ctx, cancel : context.WithCancel(ctx) session.CancelFunc cancel switch req.Protocol { case ProtoWebSocket: return h.handleWebSocketStream(ctx, session, writer) case ProtoSSE: return h.handleSSEStream(ctx, session, writer) default: return ErrUnsupportedProtocol } } // handleWebSocketStream 处理WebSocket双向流 // 关键设计select同时监听LLM流与用户中断通道 func (h *StreamHub) handleWebSocketStream( ctx context.Context, session *AgentSession, writer StreamWriter, ) error { for { select { case token, ok : -session.llmStream: if !ok { // LLM流正常结束 return writer.Write(StreamEvent{ Type: done, }) } if err : writer.Write(StreamEvent{ Type: token, Data: token, }); err ! nil { return err } case -session.interrupt: // 用户主动中断——调用Cancel通知LLM协程停止 session.CancelFunc() return writer.Write(StreamEvent{ Type: cancelled, Data: user_interrupted, }) case -ctx.Done(): return ctx.Err() } } } // handleSSEStream 处理SSE单向流 // 补偿设计通过独立goroutine监听sseAbort通道实现伪双向 func (h *StreamHub) handleSSEStream( ctx context.Context, session *AgentSession, writer StreamWriter, ) error { // 启动心跳goroutine防止代理层超时断开 go h.sseHeartbeatLoop(ctx, writer) for { select { case token, ok : -session.llmStream: if !ok { return writer.Write(StreamEvent{ Type: done, }) } if err : writer.WriteSSE(token, token); err ! nil { return err } case abortReason : -session.sseAbort: // SSE的中断补偿通过独立HTTP POST通道接收 session.CancelFunc() return writer.WriteSSE(cancelled, abortReason) case -ctx.Done(): return ctx.Err() } } } // sseHeartbeatLoop SSE心跳保活 // 每15秒发送注释行避免Nginx proxy_read_timeout断开 func (h *StreamHub) sseHeartbeatLoop( ctx context.Context, writer StreamWriter, ) { ticker : time.NewTicker(h.sseHeartbeat) defer ticker.Stop() for { select { case -ticker.C: // SSE注释行: heartbeat\n\n浏览器不触发事件 writer.WriteHeartbeat() case -ctx.Done(): return } } }上述实现中三个设计决策值得关注。其一双通道架构WebSocket利用全双工特性通过单连接同时承载LLM流与中断信号。SSE则需要补偿一条独立的HTTP POST通道实现中断投递。其二心跳策略SSE场景下代理层的静默断开是高频故障源15秒间隔的注释行心跳可以有效规避。其三熔断保护WebSocket的有状态连接消耗更多内存需要通过连接计数触发降级。四、性能边界的实测对比与选型决策框架基于16核64G的云服务器环境对两种协议进行5000并发连接的压测对比指标SSEWebSocket连接建立耗时(P50)12ms53ms首Token延迟(P50)180ms175ms内存占用/连接1.2KB4.8KB中断响应延迟320ms(补偿通道)8ms(同通道)代理兼容性HTTP/1.1兼容需显式Upgrade配置SSE在以下场景是更优选择Agent以单向内容生成为主如文档续写、代码补全、报告生成。此时SSE的低成本连接和更广的代理兼容性优势突出。WebSocket在以下场景不可替代需要高频双向交互的Agent如实时协作编辑、多步工具调用中的用户干预。中断响应的8ms延迟对比320ms在体感上是即时与卡顿的区别。一个常见的架构误区是在SSE基础上通过HTTP轮询模拟双向通信。这会导致连接数翻倍且中断延迟不可控。正确的做法是如果你的Agent有超过20%的会话涉及用户中途干预直接选择WebSocket。五、总结Agent的流式输出选型本质上是在连接成本与交互灵活性之间做权衡。SSE的优势在于极低的连接开销和无需协议升级的部署便利性。在Nginx、CDN等基础设施层面几乎无感知。其核心局限是单向性需要额外设计中断补偿通道。WebSocket的全双工特性天然适合多轮交互式Agent。但每个连接占用4倍于SSE的内存在万级并发下需要考虑水平扩展策略。工程落地的建议路径初期优先使用SSE快速验证Agent的核心生成能力。当用户中断率超过预设阈值后在StreamHub中增加WebSocket协议分支实现按Session动态切换。最终达成一份协议适配层、两种传输通道的架构收敛。选型决策的核心不是技术优劣而是你的Agent在多大比例的用户会话中需要被打断。