在实时聊天或长文本生成应用中,长时间的等待会损害用户体验。请求处理时间过长也容易触发服务端超时,导致任务失败。流式输出通过持续返回模型生成的文本片段,解决了这两个核心问题。
工作原理
流式输出基于 Server-Sent Events (SSE) 协议。发起流式请求后,服务端与客户端建立持久化 HTTP 连接。模型每生成一个文本块(称为 chunk),立即通过连接推送。全部内容生成后,服务端发送结束信号。
客户端监听事件流,实时接收并处理文本块,例如逐字渲染界面。这与非流式调用(一次性返回所有内容)形成对比。
以上组件仅供您参考,并未真实发送请求。
计费说明
流式输出计费规则与非流式调用完全相同,根据请求的输入Token数和输出Token数计费。
请求中断时,输出 Token 仅计算服务端收到终止请求前已生成的部分。
如何使用
Qwen3 开源版、QwQ 商业版与开源版、QVQ 等模型仅支持流式输出方式调用。
步骤一:配置 API Key
将API Key配置为环境变量(DASHSCOPE_API_KEY
)比在代码中硬编码更安全。
步骤二:发起流式请求
OpenAI兼容
如何开启
设置
stream
为true
即可。查看 Token 消耗
OpenAI 协议默认不返回 Token 消耗量,需设置
stream_options={"include_usage": true}
,使最后一个返回的数据块包含Token消耗信息。
Python
import os
from openai import OpenAI, APIError
# 1. 准备工作:初始化客户端
# 建议通过环境变量配置API Key,避免硬编码。
try:
client = OpenAI(
# 若没有配置环境变量,请将下行替换为:api_key="sk-xxx"
api_key=os.environ["DASHSCOPE_API_KEY"],
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
except KeyError:
raise ValueError("请设置环境变量 DASHSCOPE_API_KEY")
# 2. 发起流式请求
try:
completion = client.chat.completions.create(
model="qwen-plus",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "请介绍一下自己"}
],
stream=True,
# 目的:在最后一个chunk中获取本次请求的Token用量。
stream_options={"include_usage": True}
)
# 3. 处理流式响应
# 使用列表推导式和join()是处理大量文本片段时最高效的方式。
content_parts = []
print("AI: ", end="", flush=True)
for chunk in completion:
# 最后一个chunk不包含choices,但包含usage信息。
if chunk.choices:
# 关键:delta.content可能为None,使用`or ""`避免拼接时出错。
content = chunk.choices[0].delta.content or ""
print(content, end="", flush=True)
content_parts.append(content)
elif chunk.usage:
# 请求结束,打印Token用量。
print("\n--- 请求用量 ---")
print(f"输入 Tokens: {chunk.usage.prompt_tokens}")
print(f"输出 Tokens: {chunk.usage.completion_tokens}")
print(f"总计 Tokens: {chunk.usage.total_tokens}")
full_response = "".join(content_parts)
# print(f"\n--- 完整回复 ---\n{full_response}")
except APIError as e:
print(f"API 请求失败: {e}")
except Exception as e:
print(f"发生未知错误: {e}")
返回结果
AI: 你好!我是Qwen,是阿里巴巴集团旗下的通义实验室自主研发的超大规模语言模型。我能够回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。我支持多种语言,包括但不限于中文、英文、德语、法语、西班牙语等。如果你有任何问题或需要帮助,欢迎随时告诉我!
--- 请求用量 ---
输入 Tokens: 26
输出 Tokens: 87
总计 Tokens: 113
Node.js
import OpenAI from "openai";
async function main() {
// 1. 准备工作:初始化客户端
// 建议通过环境变量配置API Key,避免硬编码。
if (!process.env.DASHSCOPE_API_KEY) {
throw new Error("请设置环境变量 DASHSCOPE_API_KEY");
}
const client = new OpenAI({
// 若没有配置环境变量,请将下行替换为:apiKey:"sk-xxx",
apiKey: process.env.DASHSCOPE_API_KEY,
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
});
try {
// 2. 发起流式请求
const stream = await client.chat.completions.create({
model: "qwen-plus",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "请介绍一下自己" },
],
stream: true,
// 目的:在最后一个chunk中获取本次请求的Token用量。
stream_options: { include_usage: true },
});
// 3. 处理流式响应
const contentParts = [];
process.stdout.write("AI: ");
for await (const chunk of stream) {
// 最后一个chunk不包含choices,但包含usage信息。
if (chunk.choices && chunk.choices.length > 0) {
const content = chunk.choices[0]?.delta?.content || "";
process.stdout.write(content);
contentParts.push(content);
} else if (chunk.usage) {
// 请求结束,打印Token用量。
console.log("\n--- 请求用量 ---");
console.log(`输入 Tokens: ${chunk.usage.prompt_tokens}`);
console.log(`输出 Tokens: ${chunk.usage.completion_tokens}`);
console.log(`总计 Tokens: ${chunk.usage.total_tokens}`);
}
}
const fullResponse = contentParts.join("");
// console.log(`\n--- 完整回复 ---\n${fullResponse}`);
} catch (error) {
console.error("请求失败:", error);
}
}
main();
返回结果
AI: 你好!我是Qwen,是阿里巴巴集团旗下的通义实验室自主研发的超大规模语言模型。我能够回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。我支持多种语言,包括但不限于中文、英文、德语、法语、西班牙语等。如果你有任何问题或需要帮助,欢迎随时向我提问!
--- 请求用量 ---
输入 Tokens: 26
输出 Tokens: 89
总计 Tokens: 115
curl
请求
# 确保已设置环境变量 DASHSCOPE_API_KEY
curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
--no-buffer \
-d '{
"model": "qwen-plus",
"messages": [
{"role": "user", "content": "你是谁?"}
],
"stream": true,
"stream_options": {"include_usage": true}
}'
响应
返回数据为符合 SSE 协议的流式响应。每一行 data:
都代表一个数据块。
data: {"choices":[{"delta":{"content":"","role":"assistant"},"index":0,"logprobs":null,"finish_reason":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"finish_reason":null,"delta":{"content":"我是"},"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"delta":{"content":"来自"},"finish_reason":null,"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"delta":{"content":"阿里"},"finish_reason":null,"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"delta":{"content":"云的超大规模语言"},"finish_reason":null,"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"delta":{"content":"模型,我叫通义千问"},"finish_reason":null,"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"delta":{"content":"。"},"finish_reason":null,"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[{"finish_reason":"stop","delta":{"content":""},"index":0,"logprobs":null}],"object":"chat.completion.chunk","usage":null,"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: {"choices":[],"object":"chat.completion.chunk","usage":{"prompt_tokens":22,"completion_tokens":17,"total_tokens":39},"created":1726132850,"system_fingerprint":null,"model":"qwen-plus","id":"chatcmpl-428b414f-fdd4-94c6-b179-8f576ad653a8"}
data: [DONE]
data:
: 消息的数据负载,通常是一个JSON字符串。[DONE]
: 表示整个流式响应已结束。
DashScope
如何开启
根据使用方式(Python SDK、Java SDK、cURL)不同,开启流式输出的方式不同:
Python SDK:设置
stream
参数为True
;Java SDK:通过
streamCall
接口调用;cURL:设置 Header 参数
X-DashScope-SSE
为enable
。
是否启动增量输出
DashScope 协议支持增量与非增量式流式输出:
增量(推荐):每个数据块仅包含新生成的内容,设置
incremental_output
为true
启动增量式流式输出。示例:["我爱","吃","苹果"]
非增量:每个数据块都包含之前已生成的内容,造成网络带宽浪费和客户端处理压力。设置
incremental_output
为false
启动非增量式流式输出。示例:["我爱","我爱吃","我爱吃苹果"]
查看 Token 消耗
每个数据块都包含实时的 Token 消耗信息。
Python
import os
from http import HTTPStatus
import dashscope
from dashscope import Generation
# 1. 准备工作:配置API Key
# 建议通过环境变量配置API Key,避免硬编码。
try:
dashscope.api_key = os.environ["DASHSCOPE_API_KEY"]
except KeyError:
raise ValueError("请设置环境变量 DASHSCOPE_API_KEY")
# 2. 发起流式请求
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "请介绍一下自己"},
]
try:
responses = Generation.call(
model="qwen-plus",
messages=messages,
result_format="message",
stream=True,
# 关键:设置为True以获取增量输出,性能更佳。
incremental_output=True,
)
# 3. 处理流式响应
content_parts = []
print("AI: ", end="", flush=True)
for resp in responses:
if resp.status_code == HTTPStatus.OK:
content = resp.output.choices[0].message.content
print(content, end="", flush=True)
content_parts.append(content)
# 检查是否是最后一个包
if resp.output.choices[0].finish_reason == "stop":
usage = resp.usage
print("\n--- 请求用量 ---")
print(f"输入 Tokens: {usage.input_tokens}")
print(f"输出 Tokens: {usage.output_tokens}")
print(f"总计 Tokens: {usage.total_tokens}")
else:
# 处理错误情况
print(
f"\n请求失败: request_id={resp.request_id}, code={resp.code}, message={resp.message}"
)
break
full_response = "".join(content_parts)
# print(f"\n--- 完整回复 ---\n{full_response}")
except Exception as e:
print(f"发生未知错误: {e}")
返回结果
AI: 你好!我是Qwen,是阿里巴巴集团旗下的通义实验室自主研发的超大规模语言模型。我能够帮助你回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。我支持多种语言,包括但不限于中文、英文、德语、法语、西班牙语等。如果你有任何问题或需要帮助,欢迎随时向我提问!
--- 请求用量 ---
输入 Tokens: 26
输出 Tokens: 91
总计 Tokens: 117
Java
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) {
// 1. 获取 API Key
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("请设置环境变量 DASHSCOPE_API_KEY");
return;
}
// 2. 初始化 Generation 实例
Generation gen = new Generation();
CountDownLatch latch = new CountDownLatch(1);
// 3. 构建请求参数
GenerationParam param = GenerationParam.builder()
.apiKey(apiKey)
.model("qwen-plus")
.messages(Arrays.asList(
Message.builder()
.role(Role.USER.getValue())
.content("介绍一下自己")
.build()
))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.incrementalOutput(true) // 开启增量输出,流式返回
.build();
// 4. 发起流式调用并处理响应
try {
Flowable<GenerationResult> result = gen.streamCall(param);
StringBuilder fullContent = new StringBuilder();
System.out.print("AI: ");
result
.subscribeOn(Schedulers.io()) // IO线程执行请求
.observeOn(Schedulers.computation()) // 计算线程处理响应
.subscribe(
// onNext: 处理每个响应片段
message -> {
String content = message.getOutput().getChoices().get(0).getMessage().getContent();
String finishReason = message.getOutput().getChoices().get(0).getFinishReason();
// 输出内容
System.out.print(content);
fullContent.append(content);
// 当 finishReason 不为 null 时,表示是最后一个 chunk,输出用量信息
if (finishReason != null && !"null".equals(finishReason)) {
System.out.println("\n--- 请求用量 ---");
System.out.println("输入 Tokens:" + message.getUsage().getInputTokens());
System.out.println("输出 Tokens:" + message.getUsage().getOutputTokens());
System.out.println("总 Tokens:" + message.getUsage().getTotalTokens());
}
System.out.flush(); // 立即刷新输出
},
// onError: 处理错误
error -> {
System.err.println("\n请求失败: " + error.getMessage());
latch.countDown();
},
// onComplete: 完成回调
() -> {
System.out.println(); // 换行
// System.out.println("完整响应: " + fullContent.toString());
latch.countDown();
}
);
// 主线程等待异步任务完成
latch.await();
System.out.println("程序执行完成");
} catch (Exception e) {
System.err.println("请求异常: " + e.getMessage());
e.printStackTrace();
}
}
}
返回结果
AI: 你好!我是Qwen,是阿里巴巴集团旗下的通义实验室自主研发的超大规模语言模型。我能够帮助你回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。我支持多种语言,包括但不限于中文、英文、德语、法语、西班牙语等。如果你有任何问题或需要帮助,欢迎随时向我提问!
--- 请求用量 ---
输入 Tokens: 26
输出 Tokens: 91
总计 Tokens: 117
curl
请求
# 确保已设置环境变量 DASHSCOPE_API_KEY
curl -X POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-DashScope-SSE: enable" \
-d '{
"model": "qwen-plus",
"input":{
"messages":[
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你是谁?"
}
]
},
"parameters": {
"result_format": "message",
"incremental_output":true
}
}'
响应
响应遵循 Server-Sent Events (SSE) 格式,每条消息包含:
id: 数据块编号;
event: 事件类型,固定为result;
HTTP 状态码信息;
data:JSON 数据部分。
id:1
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"我是","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":27,"output_tokens":1,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
id:2
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"通义千","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":30,"output_tokens":4,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
id:3
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"问,阿里巴巴","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":33,"output_tokens":7,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
...
id:13
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"或需要帮助,欢迎随时","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":90,"output_tokens":64,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
id:14
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"告诉我!","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":92,"output_tokens":66,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
id:15
event:result
:HTTP_STATUS/200
data:{"output":{"choices":[{"message":{"content":"","role":"assistant"},"finish_reason":"stop"}]},"usage":{"total_tokens":92,"output_tokens":66,"input_tokens":26,"prompt_tokens_details":{"cached_tokens":0}},"request_id":"d30a9914-ac97-9102-b746-ce0cb35e3fa2"}
应用于生产环境
性能与资源管理:在后端服务中,为每个流式请求维持一个HTTP长连接会消耗资源。确保您的服务配置了合理的连接池大小和超时时间。在高并发场景下,监控服务的文件描述符(file descriptors)使用情况,防止耗尽。
客户端渲染:在Web前端,使用
ReadableStream
和TextDecoderStream
API 可以平滑地处理和渲染SSE事件流,提供最佳的用户体验。关键指标:监控首Token延迟(Time to First Token, TTFT),该指标是衡量流式体验的核心。同时监控请求错误率和平均响应时长。
告警设置:为API错误率(特别是4xx和5xx错误)的异常设置告警。
Nginx代理配置:若使用 Nginx 作为反向代理,其默认的输出缓冲(proxy_buffering)会破坏流式响应的实时性。为确保数据能被即时推送到客户端,务必在Nginx配置文件中设置
proxy_buffering off
以关闭此功能。
错误码
如果模型调用失败并返回报错信息,请参见错误信息进行解决。
常见问题
Q:为什么返回数据中没有 usage 信息?
A:OpenAI 协议默认不返回 usage 信息,设置stream_options
参数使得最后返回的包中包含 usage 信息。
Q:开启流式输出对模型的回复效果是否有影响?
A:无影响,但部分模型仅支持流式输出,且非流式输出可能引发超时错误。建议优先使用流式输出。