你平时用ChatGPT或者DeepSeek网页版,对方回消息是一个字一个字蹦出来的,像有人在键盘上现场敲给你看。那种体验就是流式输出。API默认是一次性把整段回答吐给你,像等一封长邮件。差别在哪?流式不用等全部算完,生成一个字你就能看到一个字。
接口层面怎么开流式
OpenAI风格的SDK里,就多一个参数的事。调用的时候把stream设成True:
python
response = client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "写一篇200字的短文"}],
stream=True
)默认是False,不写就是非流式,憋着等全部结果回来再给你。
处理返回数据的方式彻底变了
非流式的时候,你直接从response.choices[0].message.content拿内容,完事。
开了流式之后,返回的response变成了一个生成器(generator),你得用迭代的方式去拿每一块数据:
python
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")注意这里不再是message,换成了delta。每块content只是这次生成的一小段增量文本,不是完整回答。你需要自己把这些碎片拼起来,才能得到最终全文。
有些厂商的SDK对流式做了封装,比如OpenAI官方库会自动帮你拼好,你只需要遍历chunk就行。但有些简易封装或者直接用requests发原始请求的,就得自己解析SSE(Server-Sent Events)格式的数据流——每个chunk以data:开头,读到data: [DONE]结束。
两个最容易踩的坑
第一个坑:超时设置要调大。流式输出是个长连接,生成时间取决于内容长度。你设个30秒超时,写一封邮件够用,但让人家写一篇800字的文章,可能刚写一半连接就被你掐断了。把超时拉到120秒甚至180秒,别在这上面抠门。
第二个坑:中断之后怎么处理。用户没耐心看完,点了个取消,或者网页关了,你的后端还在默默地接收剩余的数据流,白白浪费token和带宽。合理的做法是,当检测到客户端断开连接(比如WebSocket的close事件),立刻在代码里调用response.close(),把底层的HTTP连接关掉。厂商那边收到连接关闭的信号,会停止继续生成,替你省钱。
流式输出到底好在哪
用户体验的差别是天壤之别的。你站在用户的角度想一想:点击发送后,界面卡住转圈,等上5秒突然蹦出几百字的大段文字。和点击发送后,1秒内开始出现第一个字,然后像有人在打字一样持续刷新。前者你会觉得系统卡、慢、不靠谱。后者你会觉得响应快、流畅、甚至有点爽。
核心原因是大模型的生成时间是跟输出长度成正比的。非流式要等全部生成完才返回,如果写了500字,耗时可能就是5秒,用户全程干瞪眼。流式把首字延迟压缩到几百毫秒,虽然总生成时间没变,但体感快了好几倍。
流式跟非流式,token计费有差别吗
没有。同一个问题,无论流式还是非流式,输出的token总数是一样的,计费标准完全一致。所以纯从省钱角度,选哪个都一样。
但在一个场景下流式能帮你省钱——超长回答。比如你要生成一篇5000字的报告,如果非流式,生成到第3000字的时候发现prompt里有个指令理解错了,想中断掉,但非流式没法半路叫停,你得等全部跑完。流式的话,只要连接还开着,你随时可以断开,后面的2000字不生成就不收费。
一个实战例子:从非流式改成流式
很多新手写web服务的时候,习惯等拿到完整结果再返回。像Flask里这样写:
python
@app.route('/chat', methods=['POST'])
def chat():
response = client.chat.completions.create(...)
return jsonify({"content": response.choices[0].message.content})改成流式的话,要换成流式响应(StreamingResponse),把每个chunk的content通过SSE或者WebSocket推给前端。前端监听每次收到的数据片段,实时刷新到页面上。
说起来就是换一种返回方式,但整个交互模式的架构要往前端推数据,逻辑比简单的请求-响应复杂一截。值不值得?但凡你的产品是面向真实用户,不是纯后端调用的批处理任务,我觉得值。用户满意度差的不止一个档次。
我自己的选择标准
新项目开发阶段,我永远先用非流式调试——log里看得清楚完整返回,方便排查prompt效果。等逻辑跑通了,上线之前再改成流式。
批处理任务、定时脚本、后台数据处理——非流式足够,省得处理那些碎片拼接的麻烦。
面向用户的聊天、写作辅助、代码生成——必须流式。别犹豫,这点开发成本值得花。
如果前端开发资源有限,流式SSE比WebSocket简单得多,兼容性也不错。没有双向通信的需求,就别上WebSocket,徒增复杂度。
最核心的一句话:流式输出不改变业务逻辑,只改变交付体验。你的prompt该怎么写还怎么写,模型该怎么算还怎么算,只是把“端上来一桌菜”换成了“边炒边上菜”。用户吃得更热乎,你说呢。