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

Flask流式聊天界面搭建求助:OpenAI助手流输出异常及集成问题

Flask + OpenAI Assistants API 流式聊天解决方案

一、修复流式输出不实时问题

你的代码存在三个核心问题:浏览器缓冲内容、格式不匹配、EventHandler与手动遍历冲突,以下是修复后的后端代码:

from flask import current_app, Response, request, session
from flask.stream_with_context import stream_with_context
import json

@main.route('/chat_stream', methods=['POST'])
def chat_stream():
    # 获取用户输入
    user_input = request.json.get('message', '')
    if not user_input:
        return Response(
            json.dumps({"error": "未提供消息内容"}),
            content_type='application/json',
            status=400
        )

    # 从全局配置获取OpenAI客户端
    chat_session = current_app.config['chat_session']
    client = chat_session.client
    
    # 从Session维护用户对话线程(首次访问自动创建)
    if 'thread_id' not in session:
        thread = client.beta.threads.create()
        session['thread_id'] = thread.id
    thread_id = session['thread_id']

    # 添加用户消息到对话线程
    client.beta.threads.messages.create(
        thread_id=thread_id,
        role='user',
        content=user_input
    )

    @stream_with_context
    def generate():
        try:
            # 流式运行助手,禁用EventHandler避免冲突
            stream = client.beta.threads.runs.stream(
                thread_id=thread_id,
                assistant_id=chat_session.assistant_id,
                instructions='system_prompt'
            )

            for chunk in stream:
                # 仅处理文本增量片段
                if hasattr(chunk, 'data') and chunk.data.type == 'thread.message.delta':
                    delta = chunk.data.delta
                    if delta.content and delta.content[0].type == 'text':
                        text_chunk = delta.content[0].text.value
                        # 按SSE格式返回,强制浏览器实时接收
                        yield f"data: {json.dumps({'content': text_chunk})}\n\n"
        except Exception as e:
            yield f"data: {json.dumps({'error': str(e)})}\n\n"

    # 配置响应头,禁用缓存并指定SSE类型
    return Response(
        generate(),
        content_type='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive'
        }
    )

关键修复点:

  • 改用text/event-stream作为响应类型,这是服务器发送事件(SSE)的标准格式,浏览器会实时处理每个返回块
  • 每个片段按data: {JSON内容}\n\n格式返回,确保前端可解析
  • 移除EventHandler,避免与手动遍历流的逻辑冲突
  • 添加Cache-Control: no-cache头,彻底禁用浏览器缓冲

二、集成多轮聊天界面

以下是配套的前端HTML代码,实现实时流式打字机效果与多轮对话展示:

<!DOCTYPE html>
<html>
<head>
    <title>AI助手聊天</title>
    <style>
        .chat-container {
            max-width: 800px;
            margin: 20px auto;
            border: 1px solid #eee;
            border-radius: 8px;
            padding: 20px;
            height: 600px;
            overflow-y: auto;
        }
        .message {
            margin: 10px 0;
            padding: 12px 16px;
            border-radius: 8px;
            max-width: 75%;
            word-wrap: break-word;
        }
        .user-message {
            background-color: #007bff;
            color: white;
            margin-left: auto;
        }
        .assistant-message {
            background-color: #f8f9fa;
            color: #333;
            margin-right: auto;
        }
        .input-area {
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            gap: 10px;
        }
        #message-input {
            flex: 1;
            padding: 12px;
            border-radius: 8px;
            border: 1px solid #ddd;
            font-size: 16px;
        }
        #send-btn {
            padding: 12px 24px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div class="chat-container" id="chat-container"></div>
    <div class="input-area">
        <input type="text" id="message-input" placeholder="输入消息...">
        <button id="send-btn">发送</button>
    </div>

    <script>
        const chatContainer = document.getElementById('chat-container');
        const messageInput = document.getElementById('message-input');
        const sendBtn = document.getElementById('send-btn');

        // 添加消息到聊天容器
        function addMessage(content, role) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${role}-message`;
            messageDiv.textContent = content;
            chatContainer.appendChild(messageDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;
        }

        // 发送消息并接收流式响应
        async function sendMessage() {
            const message = messageInput.value.trim();
            if (!message) return;

            // 显示用户消息
            addMessage(message, 'user');
            messageInput.value = '';

            // 创建SSE连接接收流式数据
            const eventSource = new EventSource(`/chat_stream`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ message: message })
            });

            // 临时存储助手消息元素,实现实时追加
            let assistantMsgEl = null;

            eventSource.onmessage = function(event) {
                const data = JSON.parse(event.data);
                if (data.error) {
                    addMessage(`错误: ${data.error}`, 'assistant');
                    eventSource.close();
                    return;
                }

                if (!assistantMsgEl) {
                    assistantMsgEl = document.createElement('div');
                    assistantMsgEl.className = 'message assistant-message';
                    chatContainer.appendChild(assistantMsgEl);
                }

                // 追加片段内容
                assistantMsgEl.textContent += data.content;
                chatContainer.scrollTop = chatContainer.scrollHeight;
            };

            // 处理连接错误
            eventSource.onerror = function(error) {
                console.error('连接错误:', error);
                eventSource.close();
                if (assistantMsgEl) assistantMsgEl.textContent += '\n[连接中断]';
            };

            // 流结束时关闭连接
            eventSource.addEventListener('close', () => eventSource.close());
        }

        // 绑定发送事件
        sendBtn.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', e => {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>

前端核心逻辑:

  • 使用EventSource建立SSE连接,实时接收后端返回的文本片段
  • 为助手消息创建临时DOM元素,每次收到片段就追加内容,实现打字机效果
  • 自动滚动到聊天底部,保证用户始终看到最新内容
  • 处理连接错误与中断情况,提升界面友好性

三、多轮对话维护

通过Flask的session存储用户的thread_id,每个用户的对话历史会自动保存在OpenAI的线程中,无需额外维护本地会话数据,实现天然的多轮对话支持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 15:08:12