本篇文章首发于算法工程笔记,更多内容,欢迎关注。
注:本文章提到的 openai
,均指 python 语言的一个库,OpenAI
指发明 GPT 系列模型的公司。
相信使用过大模型接口的开发者,对 python 的 openai 库都不陌生。因为几乎所有的大模型,都有兼容 OpenAI 定义的 API,而 openai 库,则是 python 语言使用大模型 API 最方便的工具。
关于 openai 库的接口定义,详见 OpenAI API Reference 。而在绝大多数使用到大模型进行实时问答的应用中,使用到最多的就是流式输出的功能了。
这里就用两篇文章,详细介绍下 openai 库的流式输出原理以及在实际应用中的注意点。
第一篇文章,着重介绍 openai 库流式输出的基本原理,以及当我们使用这个库的流式输出功能时,具体发生了什么。
openai库的流式输出原理
在 openai 库的 github 页面上,有一句关于 streaming response 的原理介绍:
We provide support for streaming responses using Server Side Events (SSE) .
也就是说,流式输出是使用 Server Side Events (SSE) 实现的。
那么具体什么是 SSE 呢?
SSE 是什么
关于 SSE 的具体定义,这里选取阮一峰老师《Server-Sent Events 教程》中的一段话来解释:
严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming) 。
也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。
SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。
所以,可以理解 SSE 是一种基于 HTTP 协议的流式信息推送机制。
注意这里的两个关键词:
- 基于 HTTP 协议:说明 SSE 本质上还是需要建立 HTTP 连接
- 流式信息推送:说明是服务端主动向客户端发送消息
理解了这两点,就能后更好的理解下一节中,网络包的传输过程。
流式输出时,网络包是如何传递的
这里使用 tcpdump
命令抓取网络包并利用 wireshark
工具进行可视化。
关于这两个工具的具体用法可以参考《实战!我用 Wireshark 让你“看见“ TCP》
使用到的具体抓包命令如下:
tcpdump -i any tcp and port 8700 -w ./1.pcap
具体参数的含义如下:
-i any
: 指定所有可用的网络接口tcp and port 8700
: 表示只捕获 TCP 协议的数据包,并且目标或源端口为 8700 的数据包-w ./1.pcap
: 将捕获的数据包写入文件,文件名为1.pcap
,并保存在当前目录 (./
) 下
综上所述,这条命令的作用是从所有网络接口中捕获 TCP 协议且端口为 8700 的数据包,并将这些数据包保存到名为 1.pcap
的文件中。
这里 8700 是大模型服务所在的端口。
而使用 openai 库进行一次流式请求的方式如下:
import os
from openai import OpenAI
base_url, api_key, model = BASE_URL, API_KEY, MODEL
client = OpenAI(api_key=api_key, base_url=base_url)
def test(stream=True):
query = f"please introduce yourself briefly, no more than ten words."
messages = [{"role": "user", "content": query}]
req_dic = {"model": model, "messages": messages, "stream": stream}
response = client.chat.completions.create(**req_dic)
if stream:
res = ""
for chunk in response:
if chunk.choices[0].delta.content is not None:
res += chunk.choices[0].delta.content
pass
test()
运行上述代码,便可以抓取对应的数据包。
然后通过 wireshark ,可以显示这次请求的 数据传输图:
注:上图中10开头的为客户端的IP,49开头的为服务端的IP
结合第一部分的 SSE 是一种基于 HTTP 协议的流式信息推送机制,可以看出:
- client创建时,客户端和服务端进行了 TCP 的连接(传输图中的前三帧)
- 客户端发起请求后,实际上是在TCP连接中发送了一个 HTTP 请求(传输图中的第4、5帧),详见下图
- 响应传输完毕后,客户端和服务端进行了 TCP 连接的断开,并且是由客户端发起的挥手请求(传输图中的最后三帧,这里的4次挥手,变成了3次,是因为服务端的 ACK 和 FIN 消息合并为了一帧)
- 在响应过程中,服务端有了一部分的 chunk 结果,便会推送给客户端,而客户端收到 chunk 结果后,也会给客户端以回复(这里注意,不管客户端有没有 for 循环的遍历,甚至我们可以在for循环之前,添加一句sleep命令,服务端也始终会在生成结果的下一刻立马将结果推送给客户端,这也就是流式信息推送机制)
整体请求的TCP流量图如下:
以上就是一个正常的流式请求时,网络包传输的过程。
下一篇文章,介绍几种异常流式请求时,网络连接情况的分析。