流式响应API

使用Server-Sent Events实现实时流式输出,让用户立即看到生成内容

SSE实时响应高级功能

什么是流式响应?

流式响应允许模型在生成内容时实时返回结果,而不是等待完整响应后再返回。 这极大提升了用户体验,特别是在生成长文本时。

❌ 传统方式

  • • 等待完整响应(可能需要几秒)
  • • 用户体验延迟
  • • 超时风险

✅ 流式响应

  • • 立即开始显示内容
  • • 实时更新界面
  • • 更好的用户体验

启用流式响应

在请求中设置 stream: true 即可启用流式响应。

请求示例
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {
      "role": "user",
      "content": "写一篇关于人工智能的文章"
    }
  ],
  "stream": true  // 启用流式响应
}

流式响应格式

流式响应使用Server-Sent Events (SSE)格式,每个数据块都以 data: 开头。

SSE数据格式
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":"人工"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":"智能"},"finish_reason":null}]}

data: [DONE]

重要字段说明

  • delta: 包含增量内容
  • finish_reason: 完成原因(stop/length/function_call)
  • [DONE]: 表示流结束

实现示例

Python实现

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.n1n.ai/v1"
)

# 创建流式响应
stream = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": "讲一个故事"}],
    stream=True
)

# 处理流式响应
for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

JavaScript实现

async function streamChat(message) {
  const response = await fetch('https://api.n1n.ai/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_API_KEY'
    },
    body: JSON.stringify({
      model: 'gpt-3.5-turbo',
      messages: [{ role: 'user', content: message }],
      stream: true
    })
  });

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

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        if (data === '[DONE]') {
          console.log('Stream finished');
          return;
        }
        
        try {
          const parsed = JSON.parse(data);
          const content = parsed.choices[0].delta.content;
          if (content) {
            process.stdout.write(content);
          }
        } catch (e) {
          console.error('Error parsing:', e);
        }
      }
    }
  }
}

React组件示例

import { useState } from 'react';

function ChatComponent() {
  const [messages, setMessages] = useState([]);
  const [isStreaming, setIsStreaming] = useState(false);

  async function sendMessage(content) {
    setIsStreaming(true);
    const newMessage = { role: 'assistant', content: '' };
    setMessages(prev => [...prev, { role: 'user', content }, newMessage]);

    const response = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: content, stream: true })
    });

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

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value);
      // 解析并更新消息
      setMessages(prev => {
        const msgs = [...prev];
        msgs[msgs.length - 1].content += chunk;
        return msgs;
      });
    }
    
    setIsStreaming(false);
  }

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i} className={msg.role}>
          {msg.content}
        </div>
      ))}
      {isStreaming && <div>AI正在思考...</div>}
    </div>
  );
}

最佳实践

错误处理

try { // 处理流 } catch (error) { if (error.name === 'AbortError') { console.log('Stream aborted'); } else { console.error('Stream error:', error); } }

取消流

const controller = new AbortController(); // 开始流 fetch(url, { signal: controller.signal, // ...其他选项 }); // 取消流 controller.abort();

缓冲处理

对于高频更新,考虑使用缓冲区批量更新UI,避免性能问题。

注意事项

重要提醒

  • • 流式响应不支持 n > 1 参数
  • • 某些代理或防火墙可能不支持SSE
  • • 流式响应的Token计算在流结束时返回
  • • 确保正确处理连接中断和重连