通过 API 将Deepseek响应流式内容输出到前端

要实现通过 API 将流式内容输出到前端,可以采用以下技术方案(以 Python 后端 + 前端 JavaScript 为例):

方案一:使用 Server-Sent Events (SSE)

这是浏览器原生支持的流式传输方案,推荐首选

python 复制代码
# Flask 示例
from flask import Response, stream_with_context

@app.route('/stream')
def stream_data():
    def generate():
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=messages,
            stream=True
        )
        
        for chunk in response:
            if chunk.choices:
                content = chunk.choices[0].delta.content or ""
                # SSE 格式要求 data: 前缀和双换行符
                yield f"data: {json.dumps({'content': content})}\n\n"
    
    return Response(stream_with_context(generate()), mimetype='text/event-stream')
javascript 复制代码
// 前端 JavaScript
const eventSource = new EventSource('/stream');

eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    document.getElementById('output').innerHTML += data.content;
};

eventSource.onerror = (err) => {
    console.error('EventSource failed:', err);
    eventSource.close();
};

方案二:使用流式 HTTP 响应(NDJSON)

更通用的流式传输方案,适合非浏览器客户端

python 复制代码
# FastAPI 示例
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
import json

@app.get("/stream")
async def stream_data():
    async def generate():
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=messages,
            stream=True
        )
        
        async for chunk in response:
            if chunk.choices:
                content = chunk.choices[0].delta.content or ""
                yield json.dumps({"content": content}) + "\n"  # NDJSON 格式
    
    return StreamingResponse(generate(), media_type='application/x-ndjson')
javascript 复制代码
// 前端 JavaScript 使用 Fetch API
async function streamData() {
    const response = await fetch('/stream');
    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 data = JSON.parse(chunk);
        document.getElementById('output').innerHTML += data.content;
    }
}

关键配置说明

  1. 响应头设置:

    python 复制代码
    # Flask
    headers = {
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    }
    
    # FastAPI 自动处理
  2. 数据格式选择:

    • SSE (text/event-stream):浏览器原生支持,自动重连
    • NDJSON (application/x-ndjson):更通用的流式 JSON 格式
    • 纯文本流:简单但结构化能力弱
  3. 前端处理建议:

    javascript 复制代码
    // 更健壮的读取方式(处理分块不完整情况)
    let buffer = '';
    
    async function processChunk(chunk) {
        buffer += chunk;
        while(buffer.includes('\n')) {
            const lineEnd = buffer.indexOf('\n');
            const line = buffer.slice(0, lineEnd);
            buffer = buffer.slice(lineEnd + 1);
            
            try {
                const data = JSON.parse(line);
                // 处理数据...
            } catch(e) {
                console.error('解析错误:', e);
            }
        }
    }

完整工作流程示例(FastAPI + React)

后端:

python 复制代码
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/chat")
async def chat_stream(prompt: str):
    async def generate():
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": prompt}],
            stream=True
        )
        
        async for chunk in response:
            if content := chunk.choices[0].delta.content:
                yield json.dumps({"content": content})
    
    return StreamingResponse(generate(), media_type="application/x-ndjson")

前端 React 组件:

javascript 复制代码
// ChatComponent.jsx
import { useState } from 'react';

export default function ChatComponent() {
    const [output, setOutput] = useState('');
    
    const startStream = async () => {
        const response = await fetch('http://api/chat?prompt=你好');
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';
        
        while(true) {
            const { done, value } = await reader.read();
            if(done) break;
            
            buffer += decoder.decode(value);
            while(buffer.includes('}')) {
                const endIndex = buffer.indexOf('}') + 1;
                const chunk = buffer.slice(0, endIndex);
                buffer = buffer.slice(endIndex);
                
                try {
                    const data = JSON.parse(chunk);
                    setOutput(prev => prev + data.content);
                } catch(e) {
                    console.error('解析错误:', e);
                }
            }
        }
    };
    
    return (
        <div>
            <button onClick={startStream}>开始对话</button>
            <div id="output">{output}</div>
        </div>
    );
}

注意事项

  1. 连接管理:

    • 设置合理的超时时间(通常 30-60 秒)
    • 处理客户端提前断开连接的情况
    python 复制代码
    # FastAPI 示例
    try:
        async for chunk in response:
            # ...处理数据
            if await request.is_disconnected():
                break
    finally:
        await client.close()  # 清理资源
  2. 性能优化:

    • 使用异步框架(FastAPI 性能优于 Flask)
    • 启用响应压缩
    python 复制代码
    app = FastAPI()
    @app.middleware("http")
    async def add_compression(request, call_next):
        response = await call_next(request)
        response.headers["Content-Encoding"] = "gzip"
        return response
  3. 安全考虑:

    • 限制最大并发连接数
    • 实施速率限制
    python 复制代码
    from fastapi import Request
    from fastapi.middleware import Middleware
    from slowapi import Limiter
    from slowapi.util import get_remote_address
    
    limiter = Limiter(key_func=get_remote_address)
    app.state.limiter = limiter
    
    @app.get("/chat")
    @limiter.limit("10/minute")
    async def chat_stream(request: Request):
        # ...
  4. 错误处理增强:

    python 复制代码
    async def generate():
        try:
            response = client.chat.completions.create(...)
            async for chunk in response:
                # 处理数据...
        except Exception as e:
            yield json.dumps({"error": str(e)})
        finally:
            await client.close()  # 确保释放资源

这些方案可根据具体需求组合使用,建议优先选择 SSE 方案(浏览器兼容性好),需要支持更复杂场景时可考虑 WebSocket,但后者实现成本较高。

相关推荐
不吃紫菜3 小时前
LangGraph 体系实战(上):建图、状态管理、控制流与持久化
ai
七夜zippoe6 小时前
Agent 优雅降级实战:模型不可用时的后备方案设计
ai·agent·模型·降级·后备方案
七夜zippoe11 小时前
Agent 超时与幂等性:重复请求、副作用控制与状态一致性
ai·agent·超时·控制·幂等性
秋饼11 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
又过二十12 小时前
结合Godot八向图实现网页角色设计器测试
ai·aigc·godot·game·qwen-image·游戏素材
aicat_cn14 小时前
json-render 接入 Chat SDK:Agent 在 Slack 和 Teams 里直接回表格和按钮
ai·大模型
weixin_4684668517 小时前
视觉Transformer的“第二次革命”:ViT³如何用测试时训练重构注意力范式
人工智能·深度学习·机器学习·ai·重构·transformer·vit
謓泽18 小时前
【插件】用写网页的方式做视频?HyperFrames 让我重新理解了“做视频“这件事!
gpt·ai·大模型·音视频·媒体·claude·视频
我才是银古1 天前
视频是算出来的:DeepSeek Harness零素材全代码生成视频插件
ai·视频·插件·deepseek·dsh
又过二十1 天前
macOS 录屏剪辑工具DogSC「逐文件」搬进 Windows —— DogSC Desktop 转写记
ai·aigc·录屏工具