随着 AI 大模型和 MCP 相关开发的流行,服务器推送事件(Server-sent Events,SSE),这个相对小众的技术又一次出现在大家的面前。大模型的流式输出,通常使用 SSE 推送生成的内容。MCP 服务器使用 SSE 来推送响应和通知。
在使用 HTTP 协议时,客户端与服务器的交互,大部分是请求和响应模式的。客户端发送请求,服务器对请求进行处理并发送响应。服务器通常不会主动发送数据给客户端。
随着应用的发展,产生了服务器主动发送数据给客户端的需求,比如,最典型的场景是服务器主动推送通知给客户端。
为了满足服务器主动发送数据的需求,曾经出现过不同的技术,比如长轮询。目前来说,有两种标准的技术,可以实现服务器主动发送数据。这两种技术分别是 WebSocket 和 SSE。
WebSocket 是复杂的双向传输协议,可以使用文本和二进制数据。而 SSE 是简单的单向文本传输协议。
WebSocket 在浏览器和服务器之间打开一个 TCP 连接。连接的双方都可以往对方发送消息。WebSocket 的优势在于,客户端和服务器之间,可以根据特定的协议自由发送消息,适合于客户端和服务器交互流程比较复杂的场景。
与 WebSocket 相比,正如名字所描述的那样,SSE 只能由服务器推送数据给客户端。SSE 协议本身很简单,只是一个事件的流。每个事件可以有不同的属性,包括事件的 id、类型、数据等。id 和 类型是可选的,只有数据是必须的。SSE 最大的优势在于,构建在已有的 HTTP 基础设施之上,实现和维护的成本低,学习的成本也低。
在 AI 应用开发中,SSE 用来推送大模型的流式输出到客户端。在 MCP 协议中,服务器使用 SSE 来推送消息和通知。
由于 SSE 只能从服务器单方面推送数据到客户端,客户端仍然需要使用普通的 HTTP 请求来发送数据。
在应用中使用 SSE,通常有两种模式。
第一种模式是使用固定的 SSE endpoint 来接收服务器推送的消息。这种方式适合于服务器推送全局的消息。用户与应用的一般交互,仍然使用 HTTP 请求和响应的方式来完成。SSE 用来推送事件通知,比如异步操作的处理结果,服务器主动发现的异常事件等。这些事件在客户端通常以相同的方式来处理,比如在界面上展示一条消息提示框,只需要对 SSE 添加统一的事件处理即可。
第二种模式是把 SSE 作为 HTTP 请求的响应格式。大模型的流式输出和 MCP 采用的是这种方式。通常使用 HTTP POST 请求。GET 和 POST 请求在浏览器端使用时,存在较大差异。如果是 GET 请求,可以直接使用浏览器内置的 EventSource,实现起来更简单。如果是 POST 请求,则不能使用 EventSource,而是要使用 fetch 或 XHR 发起 POST 请求,读取返回的文本流,手动解析 SSE 格式。当然了,我们也不用自己编写代码,使用诸如 eventsource 这样的库即可。
下面具体讨论一下大模型流式输出和 MCP 中 SSE 的使用。
在目前的聊天式 AI 助手的界面中,流式输出已经是标配。这种一边生成一边输出的方式,可以提供更好的用户体验。应用发送 POST 请求给大模型的 API,其中包含发送给大模型的消息。在发送的请求中,可以选择是否启用流式输出。如果启用了流式输出,则大模型 API 响应的格式是 SSE。应用的服务器接收到模型 API 返回的流式输出之后,再以 SSE 流的形式返回给浏览器端。服务器的实现,通常使用 AI 开发框架来简化实现。这是因为大模型的流式输出,虽然看似简单,实际上需要处理的细节很多。比如,如果大模型的响应中,包含了调用自定义函数的名称和参数。参数的值也以流的形式,分成多个事件来传输。仅有部分的参数值,显然无法用来调用实际的函数。这就要求响应的处理方,缓存已经接收到的部分参数值,聚合成完整的参数值之后,再进行实际的函数调用。诸如这些的处理细节,由 AI 开发框架处理。
服务器和浏览器的交互相对复杂一些。其中最关键的一点是 SSE 中事件的数据格式。最简单的实现方式是,事件中仅包含大模型输出的文本内容。这种方式虽然简单,但是不够灵活。因为在大模型的输出中,除了文本内容,还有其他有价值的信息,比如输入和输出所花费的 token 数量。通常的实现方式是以 JSON 内容作为事件的数据。JSON 内容的格式由应用根据需要来确定。事件的类型也可以利用起来。
下图展示了 ChatGPT 使用的 SSE 事件流。其中事件类型 message 表示服务器所推送的消息,其 JSON 内容中的 type 字段,定义了消息的类型。比如 title_generation 类型的消息,表示服务器给出的当前会话的标题。事件类型 delta 表示增量式的大模型输出,其 JSON 内容的 v 字段,表示大模型输出的文本内容。在开发自己的 Chat 类型的助手应用时,可以参考这样的 SSE 格式。
客户端的实现涉及到对 POST 请求返回的 SSE 流的处理。已经有很多开源的库可以进行处理。如果开发 AI 助手类型的应用,可以使用诸如 assistant-ui 这样的库。
在 MCP 中,SSE 的使用更为广泛,主要是在 HTTP 传输方式中。
MCP 协议 2024-11-05 版本的 HTTP 传输方式使用 SSE,采用了传统的 POST 和 GET 请求相结合的方式。客户端发送 POST 请求到服务器,GET 请求用来读取 SSE 流,服务器通过 SSE 推送消息给客户端。使用 SSE 的原因在于 MCP 中定义了通知。通知由服务器主动发送。服务器发送通知的场景包括,提示模板、资源和工具的列表发生变化,日志消息,长时间任务的进度等。这些通知都通过 SSE 来发送。除了通知之外,客户端请求的响应,也通过 SSE 来发送。
2025-03-26 版本改成了 Streamable HTTP 方式,仍然支持 SSE。不过把响应和通知的传输分开了。GET 请求获取到的 SSE 流用来推送通知。POST 请求既可以返回单个响应,也可以使用 SSE 返回多个响应。POST 请求的这种方式,与之前提到的大模型流式输出的处理是相同的。