You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Rest Framework中使用OpenAI stream=true并保存返回内容

解决OpenAI流式响应返回React前端及数据保存问题

后端(Django)实现

1. 用StreamingHttpResponse结合生成器处理流式响应

直接通过生成器迭代OpenAI的流式结果,同时收集完整内容用于后续保存。推荐用SSE(Server-Sent Events)格式返回数据,方便前端解析:

from django.http import StreamingHttpResponse
import openai
from your_app.models import GeneratedContent  # 替换为你的数据模型

def stream_openai_response(request):
    prompt = "Write me a story about dogs."
    MAX_TOKENS = 1000
    openai.api_key = "your-api-key"  # 或全局配置
    
    full_response = []  # 收集完整输出用于保存

    def generate_stream():
        nonlocal full_response
        # 调用OpenAI流式接口
        completion = openai.Completion.create(
            model="text-davinci-003",
            prompt=prompt,
            temperature=0.7,
            max_tokens=MAX_TOKENS,
            frequency_penalty=1.0,
            presence_penalty=1.0,
            stream=True,
        )
        # 迭代每个流式chunk
        for chunk in completion:
            text_chunk = chunk.choices[0].text
            full_response.append(text_chunk)
            # 按SSE格式返回,必须以\n\n结尾
            yield f"data: {text_chunk}\n\n"
        
        # 流式传输完成后,保存完整内容到数据库
        GeneratedContent.objects.create(
            prompt=prompt,
            content=''.join(full_response)
        )

    # 返回流式响应,设置正确的响应头
    return StreamingHttpResponse(
        generate_stream(),
        content_type='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
        }
    )

核心要点

  • 用nonlocal让生成器能修改外部变量,收集所有文本chunk
  • SSE格式要求每个数据块以data: 开头、\n\n结尾,前端才能正确识别
  • 响应头Cache-Control: no-cache避免浏览器缓存,Connection: keep-alive维持长连接

前端(React)实现

用EventSource接收SSE流式数据,实时更新页面内容:

import { useState, useEffect } from 'react';

function DogStoryGenerator() {
    const [story, setStory] = useState('');
    const [isGenerating, setIsGenerating] = useState(false);

    useEffect(() => {
        let eventSource;
        if (isGenerating) {
            // 建立SSE连接
            eventSource = new EventSource('/api/stream-openai-response/');
            
            // 接收每个文本块
            eventSource.onmessage = (event) => {
                setStory(prev => prev + event.data);
            };

            // 处理错误或连接关闭
            eventSource.onerror = (err) => {
                console.error('SSE连接异常:', err);
                eventSource.close();
                setIsGenerating(false);
            };

            eventSource.onclose = () => {
                setIsGenerating(false);
            };
        }

        // 组件卸载时关闭连接
        return () => {
            eventSource?.close();
        };
    }, [isGenerating]);

    const startGenerate = () => {
        setStory('');
        setIsGenerating(true);
    };

    return (
        <div>
            <button onClick={startGenerate} disabled={isGenerating}>
                {isGenerating ? '生成中...' : '生成狗狗故事'}
            </button>
            <div className="story-content" style={{ marginTop: '16px', whiteSpace: 'pre-wrap' }}>
                {story}
            </div>
        </div>
    );
}

export default DogStoryGenerator;

替代方案:用Fetch API的ReadableStream

如果不想用SSE,也可以直接用Fetch处理原始文本流:

const startGenerate = async () => {
    setStory('');
    setIsGenerating(true);
    try {
        const res = await fetch('/api/stream-openai-response/', { method: 'GET' });
        const reader = res.body.getReader();
        const decoder = new TextDecoder();

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            const text = decoder.decode(value, { stream: true });
            setStory(prev => prev + text);
        }
    } catch (err) {
        console.error('请求失败:', err);
    } finally {
        setIsGenerating(false);
    }
};

此时后端生成器不需要SSE包装,直接yield原始文本即可:

def generate_stream():
    nonlocal full_response
    completion = openai.Completion.create(...)
    for chunk in completion:
        text_chunk = chunk.choices[0].text
        full_response.append(text_chunk)
        yield text_chunk
    # 保存数据...

数据保存注意事项

  • 保存逻辑放在生成器的最后,确保所有流式chunk都已收集完成
  • 保存操作不会影响前端的流式体验,因为此时所有数据已发送给前端
  • 可根据需求修改保存目标,比如文件、Redis或其他存储系统

内容的提问来源于stack exchange,提问作者rd360

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 15:25:22