OpenClaw 实时流式输出与WebSocket通信

更新时间:2026-04-24 | 预计阅读:11分钟

世界上有一种等待叫做"AI在思考"——光标闪了30秒,然后啪地一下吐出500字。你盯着屏幕,像一个等外卖的人,不知道它是在精心烹饪,还是已经忘了你的订单。流式输出解决这个问题:让AI一个字一个字地说,像人一样。

流式输出(Streaming)原理

传统请求-响应模式下,LLM需要完整生成全部内容后才返回。流式输出改变了这个模式——模型每生成一个token(词元),就立即推送给客户端。

为什么流式输出重要

  • 感知速度:第一个字符1秒内出现,用户感觉"有响应了"
  • 用户体验:逐字显示更像真人打字,减少焦虑
  • 提前中断:看到方向不对可以立刻停止,不用等完整生成
  • 实时展示工具调用:用户能看到Agent正在调用哪个工具

OpenClaw 流式架构

事件流(Event Stream)

// OpenClaw 流式事件类型
enum StreamEvent {
  TEXT_DELTA,       // 文本增量(一个token)
  TOOL_START,       // 工具调用开始
  TOOL_RESULT,      // 工具调用结果
  THINKING,         // 推理过程(如果开启)
  ERROR,            // 错误信息
  DONE              // 生成完成
}

// 事件格式
{
  "type": "TEXT_DELTA",
  "delta": "世界上",
  "timestamp": 1713900000123
}
{
  "type": "TOOL_START",
  "tool": "web_search",
  "input": { "query": "OpenClaw教程" }
}

SSE (Server-Sent Events) 模式

OpenClaw默认使用SSE推送流式事件:

// 客户端接收SSE流
const eventSource = new EventSource(
  '/api/stream?session=abc123'
);

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);

  switch (data.type) {
    case 'TEXT_DELTA':
      appendToChat(data.delta);  // 追加文本
      break;
    case 'TOOL_START':
      showToolStatus(data.tool);  // 显示工具状态
      break;
    case 'DONE':
      eventSource.close();       // 关闭连接
      break;
  }
};

WebSocket 双向通信

需要双向实时交互时,使用WebSocket:

// WebSocket连接
const ws = new WebSocket('wss://api.openclaw.com/ws');

ws.onopen = () => {
  // 发送消息
  ws.send(JSON.stringify({
    type: 'message',
    content: '帮我分析今天的AI新闻',
    stream: true
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.type === 'stream') {
    // 流式输出
    document.getElementById('output').textContent += data.delta;
  } else if (data.type === 'tool_call') {
    // 工具调用进度
    updateProgress(data.tool, data.status);
  }
};

// 中断生成
function stopGeneration() {
  ws.send(JSON.stringify({ type: 'stop' }));
}

实战:构建实时聊天界面

前端实现

// 完整的流式聊天组件
class StreamChat {
  constructor(containerId) {
    this.container = document.getElementById(containerId);
    this.currentMessage = '';
  }

  async sendMessage(text) {
    // 显示用户消息
    this.addMessage('user', text);

    // 创建AI回复容器
    const aiDiv = this.addMessage('ai', '', true);

    // 开始流式接收
    const response = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: text, stream: true })
    });

    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value);
      const events = chunk.split('\n').filter(Boolean);

      for (const event of events) {
        const data = JSON.parse(event);
        if (data.type === 'delta') {
          this.currentMessage += data.text;
          aiDiv.innerHTML = this.renderMarkdown(this.currentMessage);
        }
      }
    }
  }
}

后端配置

# openclaw.config.yaml
streaming:
  enabled: true
  mode: sse              # sse 或 websocket
  heartbeat_interval: 30  # 心跳间隔(秒)
  max_stream_duration: 300 # 最大流式时长(秒)
  buffer_size: 1          # 缓冲大小(越小越实时)

工具调用的流式展示

OpenClaw的一个杀手级功能:流式展示Agent的工具调用过程。

// 工具调用事件的流式展示
// 用户看到的效果:

// [🔍 正在搜索...] OpenClaw 教程
// [📄 找到 15 条结果]
// [🌐 正在获取...] https://miaoquai.com/tools/openclaw-guide.html
// [✅ 内容获取完成]
// [🔄 正在生成...] 基于搜索结果生成回答...

// 代码实现
function renderToolCall(event) {
  switch (event.status) {
    case 'start':
      return `⏳ [${event.tool}] ${event.input || ''}`;
    case 'progress':
      return `🔄 [${event.tool}] ${event.message}`;
    case 'done':
      return `✅ [${event.tool}] 完成`;
    case 'error':
      return `❌ [${event.tool}] ${event.error}`;
  }
}

性能优化

  • 缓冲调优:buffer_size=1 最实时,但网络开销大;设为5-10平衡性能
  • 心跳保活:长文本生成时发送心跳,防止连接超时
  • 断线重连:WebSocket断开后自动重连,恢复上下文
  • 增量渲染:不要每收到一个token就重新渲染整个DOM,用增量追加
  • 背压控制:客户端处理速度跟不上的时候,暂停服务端发送

最佳实践

  1. 始终显示"正在思考"状态——用户的耐心有限
  2. 工具调用过程透明化——让用户知道Agent在做什么
  3. 提供中断按钮——用户有权叫停生成
  4. 错误不静默处理——流中遇到错误要明确告知用户
  5. Markdown实时渲染——流式输出配合实时Markdown解析

相关资源