为什么用 SSE
大模型生成一段话需要几秒到几十秒。如果等全部生成完再返回,用户会盯着空白屏幕。SSE(Server-Sent Events)让服务端边生成边推送,首字延迟能降到几百毫秒。
相比 WebSocket,SSE 更简单:单向推送、纯 HTTP、自动重连。
协议格式
data: {"choices":[{"delta":{"content":"你"}}]}
data: {"choices":[{"delta":{"content":"好"}}]}
data: [DONE]
每行以 data: 开头,两个换行表示一条消息结束,[DONE] 表示流结束。
Python 实现
from openai import OpenAI
client = OpenAI(api_key="你的KEY", base_url="https://api.ns666.cfd/v1")
stream = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "讲个故事"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content if chunk.choices else None
if delta:
print(delta, end="", flush=True)
Node.js 实现
const resp = await fetch("https://api.ns666.cfd/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.API_KEY}`,
},
body: JSON.stringify({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "讲个故事" }],
stream: true,
}),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("n")) {
if (!line.startsWith("data: ")) continue;
const p = line.slice(6);
if (p === "[DONE]") continue;
try {
const d = JSON.parse(p).choices[0].delta.content;
if (d) process.stdout.write(d);
} catch (e) {}
}
}
浏览器端
const resp = await fetch("https://api.ns666.cfd/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "你好" }],
stream: true,
}),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("n")) {
if (!line.startsWith("data: ")) continue;
const p = line.slice(6).trim();
if (p === "[DONE]") continue;
try {
const delta = JSON.parse(p).choices[0].delta.content;
if (delta) document.body.innerText += delta;
} catch (e) {}
}
}
注意:浏览器直连会暴露 Key
前端代码里的 Key 任何人都能看到。生产环境必须经由你自己的后端中转,后端持 Key 调随性 API,前端只连你的后端。