一、为什么我们需要“流式输出”?

传统的 HTTP 请求-响应模式是“一问一答”式的。当我们向像 MiMo 这样的大模型发送一个复杂问题时,服务器需要较长的时间生成完整的回答。在此期间,用户界面会一直显示“加载中”,体验就像是在看一个卡住的进度条,这无疑会让人焦虑。

流式输出 解决了这个“等待黑洞”问题。它的核心思想是:服务器不等全部内容生成完毕,而是“边生成边发送”。模型每生成一小块文本(例如一个词或一个句子),就立刻通过网络推送给客户端。客户端收到一小块就立刻渲染一小块,让用户能看到文字像“打字机”一样逐字输出。

这种模式带来了革命性的用户体验提升:

二、SSE:流式传输的“轻量级”协议

要实现服务器向客户端的持续推送,我们有 WebSocket、HTTP/2 Server Push 等多种技术。而 SSE 以其独特的简单性,在需要单向服务器推送的场景(如大模型流式输出)中脱颖而出。

SSE 全称为 Server-Sent Events,顾名思义,它建立在标准的 HTTP 协议之上。客户端发起一个普通的 HTTP 请求,但服务器不会立即关闭连接,而是将响应头的 Content-Type 设置为 text/event-stream,并保持连接打开,后续可以多次发送数据。

它的核心特点可以概括为:

三、MiMo 如何使用 SSE 进行流式输出

当我们调用 MiMo 的 API 并将 stream 参数设置为 true 时,就激活了其流式模式。此时,API 的响应不再是单个的 JSON 对象,而是一个 text/event-stream 类型的数据流。

这个数据流由多个事件组成。每一个事件(或消息块)在 SSE 规范中通常包含以下几个部分(用换行符 \n 分隔):

  1. event: 字段(可选):定义事件类型,对于大模型文本流,通常可以忽略或固定。
  2. data: 字段:核心负载,承载实际的文本片段。一个事件中可以包含多个 data 行,它们会被拼接。
  3. id: 字段(可选):事件的唯一标识符,可用于在断线重连时告知服务器从哪里开始续传。
  4. retry: 字段(可选):建议客户端重连的时间间隔(毫秒)。

对于 MiMo 的流式输出,我们通常关注的是 data: 行里的内容,它包含了模型本次生成的一个小文本片段(例如几个词)。客户端程序需要持续读取并解析这些事件,将 data 内容拼接起来,形成最终的完整回答。

四、代码实践:用 Python 捕捉 MiMo 的流

下面是一个使用 Python 的 requests 库来与流式 MiMo API 交互的简化示例。关键在于使用 stream=True 参数来建立流式连接。

import requests
import json

# MiMo API 端点和包含 `stream: true` 的请求体
url = "https://api.mimo.example.com/v1/chat/completions"
headers = {"Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json"}
payload = {
    "model": "MiMo-Pro",
    "messages": [{"role": "user", "content": "用简单的语言解释量子纠缠"}],
    "stream": True  # 关键参数,开启流式
}

# 发起流式请求
response = requests.post(url, headers=headers, json=payload, stream=True)
response.raise_for_status()  # 检查请求是否成功

print("开始接收流式响应...")
full_response = []

# 逐行读取流式数据
for line in response.iter_lines():
    if line:
        # SSE 数据以 "data: " 开头,我们需要跳过行首并解析 JSON
        decoded_line = line.decode('utf-8')
        if decoded_line.startswith('data: '):
            # 去掉 'data: ' 前缀,解析 JSON 负载
            json_str = decoded_line[6:]  # 切片去掉前6个字符
            if json_str.strip() == '[DONE]':
                print("\n\n[流式传输完成]")
                break
            try:
                data = json.loads(json_str)
                # 从数据中提取生成的文本片段
                content = data.get('choices', [{}])[0].get('delta', {}).get('content', '')
                if content:
                    print(content, end='', flush=True)  # 逐字打印,模拟打字效果
                    full_response.append(content)
            except json.JSONDecodeError:
                # 忽略无法解析的行(可能是空行或心跳)
                continue

final_text = ''.join(full_response)
print(f"\n\n完整回答已拼接,长度:{len(final_text)} 字符")

五、解析流式数据时的实用技巧

处理 SSE 流时,会遇到一些实际问题,这里有几个技巧:

提示:在前端 JavaScript 中处理 SSE 非常简单,因为现代浏览器都内置了 EventSource API。而在 Python、Java 等后端语言中,通常需要手动处理底层的 HTTP 流和文本解析,如上面的示例所示。

六、流式输出的局限与适用场景

流式输出虽好,但并非万能。它主要适用于:

但它也存在局限:

七、总结:从技术到体验

回顾一下,流式输出是提升用户体验的关键技术,而 SSE 为我们提供了实现它的一种轻量、标准的 协议。像 MiMo 这样的模型通过暴露一个 stream 参数,将复杂的流式处理封装在简单的 API 接口之后,极大地降低了开发者的接入门槛。

作为一名开发者,理解这背后的原理——从 HTTP 长连接、数据格式解析到客户端渲染逻辑——将帮助你构建出响应更迅捷、交互更自然的应用。下次当你看到聊天窗口里文字在“跳舞”时,你便能知晓这背后是 SSE 协议在默默支撑着数据的流动。