AI小栈AI小栈
首页
  • 模块概览
  • Transformer 与注意力机制
  • 主流大模型对比
  • Prompt Engineering
  • Token 与成本核算
  • Function Calling
  • 模块概览
  • Agent 范式
  • 多 Agent 协作
  • 记忆系统
  • 规划与推理
  • 安全与可控性
  • 模块概览
  • LangChain / LangGraph
  • Claude Agent SDK
  • OpenAI Agents SDK
  • AutoGen / CrewAI
  • Dify / Coze
  • MCP 协议
  • 模块概览
  • RAG 工程化
  • 工具设计与封装
  • 流式输出与 UX
  • 可观测性
  • 部署与成本优化
  • 测试与评估
  • 模块概览
  • 智能编程助手
  • 知识库问答 Agent
  • 数据分析 Agent
  • 多模态 Agent
  • 运维 Agent
  • 从0到1构建
  • 模块概览
  • 论文解读
  • 开源项目追踪
  • MCP 生态
  • Agent 商业化
  • 职业路径
  • 模块概览
  • 技术面试实战
  • Agent 面试题解析
  • 项目经验包装
  • 流程与心态准备
首页
  • 模块概览
  • Transformer 与注意力机制
  • 主流大模型对比
  • Prompt Engineering
  • Token 与成本核算
  • Function Calling
  • 模块概览
  • Agent 范式
  • 多 Agent 协作
  • 记忆系统
  • 规划与推理
  • 安全与可控性
  • 模块概览
  • LangChain / LangGraph
  • Claude Agent SDK
  • OpenAI Agents SDK
  • AutoGen / CrewAI
  • Dify / Coze
  • MCP 协议
  • 模块概览
  • RAG 工程化
  • 工具设计与封装
  • 流式输出与 UX
  • 可观测性
  • 部署与成本优化
  • 测试与评估
  • 模块概览
  • 智能编程助手
  • 知识库问答 Agent
  • 数据分析 Agent
  • 多模态 Agent
  • 运维 Agent
  • 从0到1构建
  • 模块概览
  • 论文解读
  • 开源项目追踪
  • MCP 生态
  • Agent 商业化
  • 职业路径
  • 模块概览
  • 技术面试实战
  • Agent 面试题解析
  • 项目经验包装
  • 流程与心态准备
  • Agent 工程实践

    • 模块概览
    • RAG 工程化
    • 工具设计与封装
    • 流式输出与用户体验
    • 可观测性
    • 部署与成本优化
    • 测试与评估框架

流式输出与用户体验

Agent 的推理过程往往需要数秒到数十秒,如果用户在等待期间看不到任何反馈,体验会非常糟糕。流式输出不仅让用户"看到 Agent 在思考",更是构建复杂交互(中断控制、进度反馈、增量渲染)的基础。本章讲解 SSE/WebSocket 技术选型、增量渲染策略、中断与取消机制、进度反馈设计,帮你打造流畅的 Agent 交互体验。

SSE 与 WebSocket 技术选型

Server-Sent Events(SSE)

SSE 是基于 HTTP 的单向推送协议,服务端可以持续向客户端发送文本事件流。特点:

  • 单向通信:服务端 → 客户端,客户端无法通过同一连接向服务端发消息。
  • 自动重连:浏览器原生支持断线自动重连,并携带 Last-Event-ID 实现续传。
  • 文本协议:每个事件由 data:、event:、id: 字段组成,不适合传输二进制数据。
  • 轻量简单:实现成本低,一个 HTTP 接口加 text/event-stream 响应即可。

适合场景:Agent 文本输出流、状态推送、进度更新——绝大多数 Agent 流式交互用 SSE 就够了。

WebSocket

WebSocket 是双向持久连接协议,客户端和服务端可互发消息。特点:

  • 双向通信:客户端可以随时向服务端发消息(如中断指令、追加输入)。
  • 低延迟:建立连接后通信不需要 HTTP 请求开销。
  • 二进制支持:可传输非文本数据。
  • 复杂度高:需要自行处理心跳、重连、消息路由。

适合场景:需要客户端实时发指令的交互(如中途追加参数、实时协作编辑、多 Agent 协同场景)。

选型建议

90% 的 Agent 场景用 SSE。因为 Agent 流式交互的模式是"用户发一次请求 → Agent 持续输出",这是典型的单向推送。只有当你需要用户在 Agent 执行过程中频繁发指令(而非只在最后发一条)时,才需要 WebSocket。

一个常见的混合方案:SSE 推送输出 + HTTP POST 发送控制指令(如中断)。这比全 WebSocket 更简单,且避开了 WebSocket 的连接管理复杂度。

增量渲染策略

Markdown 增量渲染

Agent 输出的文本通常包含 Markdown 格式(标题、列表、代码块)。流式接收时,内容逐 token 到达,渲染面临两个挑战:

  1. 未完成的 Markdown 结构:收到 "## 分析结" 时,不知道这是标题还是普通文本(后面可能还有 "果")。
  2. 代码块边界判断:收到三个反引号时,不知道代码块是开始还是结束。

解决方案:

  • 乐观渲染:假设未完成的结构会最终闭合,按当前最可能的格式渲染。如果后续 token 证明假设错误,重新渲染修正。大多数 Markdown 流式渲染库(如 markdown-it-streaming)采用此策略。
  • 延迟渲染代码块:检测到代码块开始标记后,暂缓格式化渲染,直到收到完整的代码块内容或超时(如 500ms),再一次性渲染。这避免了代码块闪烁问题。
  • 缓冲窗口:保留最近 N 个 token 不渲染,等结构确定后再输出。N 太大用户感觉延迟,太小仍可能误判。建议 N=3-5。

表格与结构化内容的渲染

表格是流式渲染的难点:一个 Markdown 表格的每一行都需要对齐,流式输出时列宽在变化中。建议:

  • 对表格整体延迟渲染:检测到表格开始(| ... |)后,缓冲整个表格直到检测到表格结束(空行或非表格行),然后一次性渲染为完整表格。
  • 先展示文本摘要:在表格缓冲期间,先渲染表格前的文本内容并显示"正在生成数据表格..."的提示。

代码执行结果的混合渲染

当 Agent 同时输出文本和代码执行结果时,需要区分两种流:

  • 文本流:Markdown 格式,需要增量渲染。
  • 结果流:JSON/表格/图像,通常一次性到达。

建议用 事件类型标记 区分:SSE 事件中 event: text 标记文本 token,event: result 标记完整结果,前端按不同策略渲染。

中断与取消控制

中断的类型

Agent 执行中的中断分为三类:

  • 用户主动取消:用户点击"停止"按钮,要求终止当前推理。
  • 超时取消:执行超过预设时间上限(如 60 秒),系统自动终止。
  • 错误中断:工具调用失败或模型输出异常,需要中途停止。

取消的实现

后端取消:收到取消指令后,后端需要:

  1. 停止向 LLM API 发送后续请求(部分 API 支持 cancel 信号)。
  2. 终止正在执行的工具调用(取消子进程/容器)。
  3. 保存已生成的部分结果(而非直接丢弃)。

前端取消:

  • SSE 场景:前端关闭 EventSource 连接,后端检测连接断开后触发取消逻辑。
  • WebSocket 场景:发送 {"type": "cancel", "request_id": "xxx"} 消息。
  • HTTP POST 场景:调用 POST /cancel/{request_id} 接口。

部分结果的保留与续传

用户取消后,已生成的部分回答不应丢弃。保存策略:

  • 将已生成内容存入会话历史,标注 [部分生成]。
  • 提供"继续生成"按钮,允许用户从断点续传——后端将已生成内容作为上下文,让 LLM 从断点继续。
  • SSE 的 Last-Event-ID 机制天然支持断点续传:重连时携带最后一个事件的 ID,后端从该 ID 之后继续推送。

进度反馈设计

为什么进度反馈很重要

Agent 执行复杂任务(多步推理+多轮工具调用)时,纯文字流式输出不够——用户需要知道"Agent 在做什么、做到哪一步了、还要多久"。

多层反馈体系

层级内容实现方式
Token 级逐字输出的文本内容SSE event: text
步骤级"正在检索知识库..." "正在调用订单查询..."SSE event: step
进度级"步骤 2/5:执行数据库查询"SSE event: progress
状态级思考中/执行中/已完成/出错SSE event: status

步骤级反馈的实现

Agent 的每一步动作(检索、工具调用、推理)都应向前端推送步骤事件:

// SSE event: step
data: {
  "step_name": "检索知识库",
  "step_type": "retrieval",
  "step_index": 1,
  "total_steps": 4,
  "detail": "正在查询与「退款政策」相关的 5 篇文档"
}

前端据此渲染步骤列表,显示当前执行到哪一步,已完成步骤显示结果摘要,未执行步骤显示灰色等待态。

进度估算

精确的"还要多久"很难估算,但可以给出粗略提示:

  • 基于步骤数:已知 Agent 计划执行 4 步,当前在第 2 步,提示"约完成 50%"。
  • 基于历史数据:记录同类任务的平均耗时,估算剩余时间。如"数据库查询通常需要 3-5 秒"。
  • 避免虚假精度:说"大约还需 10-20 秒"比说"还需 12.3 秒"更可信——过高的精度会让用户对延误更不耐烦。

错误反馈

当某一步出错时,不要只显示"出错了"。应该:

  1. 告知用户哪一步出错:"在调用库存查询时遇到问题"。
  2. 说明错误性质:"库存服务暂时不可用"而非"Error 500"。
  3. 说明 Agent 的应对策略:"正在使用缓存数据替代实时查询,结果可能略有延迟"。
  4. 给用户选择:"您可以等待恢复、查看缓存数据,或稍后再试"。

本章小结

UX 维度关键策略
传输技术SSE(单向)vs WebSocket(双向),按场景选择
渲染策略Markdown 增量渲染 + 打字机效果,平衡实时感与可读性
进度反馈状态标签 + 进度条 + 步骤计数,降低用户焦虑
错误处理区分技术错误(静默重试)与业务错误(明确告知)

UX 核心原则:用户不需要知道 Agent 在"思考",只需要知道"进展如何"。


📖 延伸阅读

  1. Server-Sent Events (SSE) —— MDN 官方文档
  2. WebSocket API —— MDN 官方文档
  3. OpenAI Streaming Guide —— 流式输出最佳实践
  4. React Markdown —— Markdown 增量渲染组件
Prev
工具设计与封装
Next
可观测性