为什么需要流式输出

非流式(stream=false)需要等待完整响应后才返回,用户看到的是长时间的空白等待。流式输出(stream=true)将生成内容以 token 为单位逐步返回,用户可以实时看到文字逐个出现,大幅提升体验。DeepSeek V4 支持 OpenAI 兼容的 SSE(Server-Sent Events)流式协议。

Python 流式调用

from openai import OpenAI
import os

client = OpenAI(
    api_key=os.environ.get('DEEPSEEK_API_KEY'),
    base_url='https://api.deepseek.com'
)

stream = client.chat.completions.create(
    model='deepseek-v4-flash',
    messages=[{'role': 'user', 'content': '写一篇 500 字的文章介绍 Python'}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end='', flush=True)

Node.js 流式调用

import OpenAI from 'openai';

const openai = new OpenAI({
  baseURL: 'https://api.deepseek.com',
  apiKey: process.env.DEEPSEEK_API_KEY,
});

const stream = await openai.chat.completions.create({
  model: 'deepseek-v4-flash',
  messages: [{ role: 'user', content: '解释什么是机器学习' }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content || '';
  process.stdout.write(content);
}

前端逐字渲染

通过 fetch + ReadableStream 在前端实现打字机效果:

async function streamChat(prompt) {
  const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + apiKey
    },
    body: JSON.stringify({
      model: 'deepseek-v4-flash',
      messages: [{ role: 'user', content: prompt }],
      stream: true
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let fullText = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
    for (const line of lines) {
      if (line === 'data: [DONE]') break;
      const data = JSON.parse(line.slice(6));
      const content = data.choices[0]?.delta?.content;
      if (content) {
        fullText += content;
        document.getElementById('output').textContent = fullText;
      }
    }
  }
}

中断流式输出

使用 AbortController 实现用户停止生成:

const controller = new AbortController();
// 绑定中断信号到 fetch
const response = await fetch(url, { signal: controller.signal, ... });
// 用户点击停止
document.getElementById('stopBtn').onclick = () => controller.abort();

思考模式的流式处理

启用了思考模式后,流式输出中会交替出现 reasoning_content 和 content,可以分别处理:思考过程折叠为可展开区域,正式回答直接展示。

最佳实践

  • 默认使用流式:除非需要完整 JSON 解析,否则 stream=true
  • 添加重连机制:网络中断时自动重试
  • 节流渲染:前端使用 requestAnimationFrame 批量更新 DOM
  • 错误处理:捕获流式错误并提示用户
  • 超时控制:设置合理的超时时间,避免无限等待