大模型流式输出评测:别只看首字快慢,测清卡顿与完整性
大模型流式输出评测真正要回答的问题不是“谁的首字快”,而是“在相同输入和参数下,输出是否连续、完整、可预期”。
如果只记录第一个 token 的时间,很容易漏掉中途卡顿、块大小抖动或连接提前关闭。
下面按可复现的采集流程说明怎么做。
先固定评测条件,再比较流式表现
流式输出的时间指标受输入长度、采样参数、网络位置和服务端负载影响很大。
评测前至少固定这几项:
- 同一批 prompt,长度和语言分布一致,避免长输入只出现在某一组。
- 相同的
max_tokens、temperature、top_p,关闭随机采样或固定种子(如果接口支持)。 - 从同一台机器、同一网络出口发起请求,记录请求时间窗口。
- 每个模型或接口重复请求多次,取分位数而不是单次值。
接口是否支持 stream: true、返回格式是 SSE 还是 chunked JSON,要以你实际调用的接口文档为准。
下面用兼容 OpenAI 风格的 /v1/chat/completions 举例,其他接口只需调整解析字段。
用时间戳记录每个分块,而不是只记总耗时
评测流式输出,核心是拿到“每个数据块到达的时间”和“块内容”。
用 Python 的 requests 逐行读取响应即可,不需要复杂框架:
import json, time, requests
url = "https://your-api-host/v1/chat/completions"
headers = {"Authorization": "Bearer YOUR_KEY"}
payload = {
"model": "your-model",
"messages": [{"role": "user", "content": "用三句话说明流式输出的特点"}],
"stream": True,
"max_tokens": 200,
"temperature": 0
}
start = time.perf_counter()
first_chunk_at = None
chunks = []
with requests.post(url, headers=headers, json=payload, stream=True, timeout=60) as r:
r.raise_for_status()
for line in r.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
data = line[5:].strip()
if data == "[DONE]":
break
now = time.perf_counter()
obj = json.loads(data)
delta = obj["choices"][0].get("delta", {}).get("content", "")
if delta and first_chunk_at is None:
first_chunk_at = now
chunks.append({"t": now - start, "text": delta})
print("首块到达(秒):", first_chunk_at)
print("总块数:", len(chunks))
print("末块到达(秒):", chunks[-1]["t"] if chunks else None)
运行后你会得到每个块的相对时间。
注意 iter_lines 按行读取,SSE 的 data: 前缀和空行处理要保留,否则会把多个事件拼错。
从原始时间戳算出三类指标
拿到 chunks 后,不要只看总时长。
建议计算:
- 首字延迟:
first_chunk_at,从请求发出到第一个非空内容块到达。 - 块间间隔分布:相邻块时间差的中位数和 P95。P95 明显高于中位数,说明存在卡顿。
- 断流与完整性:是否收到
[DONE]、输出字符数是否达到预期、连接是否在max_tokens前关闭。
块大小也要看。
同样总时长下,如果服务端把多个 token 合并成一个大块一次性下发,首字延迟可能好看,但用户感知的“逐字输出”并不连续。
记录每块 len(text),对比块大小是否稳定。
常见坑:这些情况会让评测结果失真
- 只测一次:流式输出受瞬时负载影响,单次结果不能代表稳定表现,至少重复 5 次看分布。
- 没处理重试和超时:连接中断后自动重试会把两次请求混在一起,时间戳必须按单次请求隔离。
- 忽略代理和 CDN:中间层可能缓冲 SSE 数据,导致块到达时间被批量推迟,评测前确认请求链路。
- 把
[DONE]当成内容:结束标记不是模型输出,统计字符数时要排除。 - 不同接口的块定义不同:有的按 token,有的按句子,跨模型比较前先确认分块粒度。
验证评测是否可复现
完成采集后,用同一批 prompt 再跑一轮,检查首字延迟和块间间隔的中位数是否在同一量级。
如果差异很大,先排查网络和服务端负载,而不是直接下结论。
把原始时间戳、请求参数和响应内容一起保存,后续换模型或换版本时才能按同一口径对比。
流式输出评测的价值在于暴露“看起来能用”和“实际连续可用”之间的差距。
固定条件、记录分块时间、检查完整性,比单纯比较一个首字延迟数字更可靠。