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

Google Colab React+Flask文本摘要项目:Backend URL未设置问题解决

问题:点击Summarize按钮出现‘backend URL not set’错误

我正在开发一个基于Google Colab的React前端与Flask后端集成的文本摘要项目,但点击Summarize按钮时出现‘backend URL not set’错误,点击按钮无反应,浏览器控制台显示该错误。希望实现输入长文本后获取4种不同模型生成的摘要,求错误原因及解决方法。


Flask后端(Google Colab Notebook代码)

安装依赖

!pip install flask
!pip install transformers
!pip install pyngrok
!pip install flask-ngrok
!pip install python-dotenv
!pip install flask-cors

核心代码

import os
import shutil
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from transformers import pipeline
from pyngrok import ngrok, conf
from dotenv import load_dotenv
import threading
import time
import subprocess

# Load environment variables from .env file
load_dotenv()

# Initialize Flask app
app = Flask(__name__, static_folder='build/build')
CORS(app)  # Enable CORS for all origins

# Delete the existing 'build' directory if it exists
build_dir = 'build/build'
if os.path.exists(build_dir):
    shutil.rmtree(build_dir)
    print("Deleted the existing 'build' directory.")

# Unzip the build.zip file
!unzip -o build.zip -d build/

# Set up ngrok with authentication token (if using a paid plan)
ngrok.set_auth_token('2hkd7J7tIDvMWx0hPImWMrQfoIi_7tcxWdqFTNucPfD2p9oSM')

# Function to kill all existing ngrok processes
def kill_existing_ngrok():
    try:
        subprocess.run(['pkill', '-f', 'ngrok'], check=True)
        print("Killed existing ngrok processes.")
    except subprocess.CalledProcessError:
        print("No existing ngrok processes found.")

# Kill existing ngrok processes before starting a new one
kill_existing_ngrok()

# Start ngrok tunnel to expose the Flask app on port 8000
ngrok_tunnel = ngrok.connect(8000) 
print(f' * Tunnel URL: {ngrok_tunnel.public_url}')

# Define function to generate TL;DR summary
def generate_tldr(full_text):
    # Take the first 2000 characters as TL;DR summary
    tldr = full_text[:2000]
    return tldr.strip()

# Initialize models
models = {
    "GPT-2": {"type": "text-generation", "model": "gpt2-medium"},
    "T5": {"type": "summarization", "model": "t5-base"},
    "BART": {"type": "summarization", "model": "facebook/bart-large-cnn"},
    "PEGASUS": {"type": "summarization", "model": "google/pegasus-cnn_dailymail"}
}

# Define route for serving the React app
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve(path):
    if path != "" and os.path.exists(os.path.join(app.static_folder, path)):
        return send_from_directory(app.static_folder, path)
    else:
        return send_from_directory(app.static_folder, 'index.html')

# Define route for generating summaries
@app.route('/generate_summary', methods=['POST'])
def generate_summary():
    try:
        # Get input text from the request
        text = request.json.get('text', '')[:2000]  # Take first 2000 characters as input

        if not text:
            return jsonify({'error': 'No text provided'}), 400

        # Initialize dictionary to store summaries
        summaries = {}

        # Iterate through each model
        for model_name, model_config in models.items():
            # Initialize pipeline for the model
            pipe = pipeline(model_config["type"], model=model_config["model"])

            # Process input text based on model type
            if model_config["type"] == "text-generation":
                # For text generation models like GPT-2
                pipe_out = pipe(text, max_length=512, clean_up_tokenization_spaces=True)
                generated_text = pipe_out[0]['generated_text']
                tldr = generate_tldr(generated_text)
                summaries[model_name] = {"summary": generated_text, "tldr": tldr}
            else:
                # For summarization models like T5, BART, and PEGASUS
                pipe_out = pipe(text)
                summary_text = pipe_out[0]['summary_text']
                tldr = generate_tldr(summary_text)
                summaries[model_name] = {"summary": summary_text, "tldr": tldr}

        return jsonify(summaries)
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# Add endpoint to serve ngrok URL
@app.route('/ngrok_url', methods=['GET'])
def get_ngrok_url():
    return jsonify({"ngrok_url": ngrok_tunnel.public_url})

# Function to run Flask app without debug mode
def run_flask():
    app.run(port=8000)

# Run Flask app in a separate thread
thread = threading.Thread(target=run_flask)
thread.start()

React前端(App.js代码)

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';

function App() {
  const [inputText, setInputText] = useState('');
  const [summaries, setSummaries] = useState(null);
  const [backendUrl, setBackendUrl] = useState('');
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchNgrokUrl = async () => {
      try {
        const response = await axios.get('/ngrok_url'); // Assuming the React app and Flask are served from the same origin
        setBackendUrl(response.data.ngrok_url);
      } catch (error) {
        console.error('Error fetching ngrok URL:', error);
        setError('Failed to fetch backend URL');
      } finally {
        setLoading(false);
      }
    };

    if (!backendUrl) {
      fetchNgrokUrl();
    } else {
      setLoading(false); // If backendUrl is already set, setLoading to false
    }
  }, [backendUrl]); // Only run this effect once on mount or if backendUrl changes

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!backendUrl) {
      console.error('Backend URL not set');
      setError('Backend URL not set');
      return;
    }
    try {
      const response = await axios.post(`${backendUrl}/generate_summary`, { text: inputText });
      setSummaries(response.data);
      setError(null); // Clear any previous errors
    } catch (error) {
      console.error('Error fetching summaries:', error);
      setError('Failed to fetch summaries');
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <h1>Text Summarization</h1>
        <form onSubmit={handleSubmit}>
          <textarea
            value={inputText}
            onChange={(e) => setInputText(e.target.value)}
            placeholder="Enter text to summarize..."
            rows="10"
            cols="50"
          />
          <button type="submit" disabled={loading}>Summarize</button>
        </form>
        {loading && <p>Loading...</p>}
        {error && <p className="error">{error}</p>}
        {summaries && (
          <div className="summaries">
            {Object.keys(summaries).map((model) => (
              <div className="summary" key={model}>
                <h2>{`${model} Summary`}</h2>
                <p>{summaries[model].summary}</p>
                <h3>TL;DR:</h3>
                <p>{summaries[model].tldr}</p>
              </div>
            ))}
          </div>
        )}
      </header>
    </div>
  );
}

export default App;

错误原因及解决方法

核心问题

  1. 前端请求逻辑冗余且错误:React由Flask直接托管,属于同域服务,无需额外获取ngrok URL。但当前代码拿到ngrok URL后又用它发起跨域请求,不仅没必要,还可能因请求失败导致backendUrl一直为空。
  2. 状态更新逻辑异常:useEffect依赖backendUrl,若/ngrok_url请求失败,backendUrl始终为空,会触发useEffect反复执行,进一步加剧状态异常。

解决步骤

步骤1:简化前端API请求逻辑

删除backendUrl相关逻辑,直接用相对路径请求后端接口:

import React, { useState } from 'react';
import axios from 'axios';
import './App.css';

function App() {
  const [inputText, setInputText] = useState('');
  const [summaries, setSummaries] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!inputText.trim()) {
      setError('请输入要摘要的文本');
      return;
    }
    setLoading(true);
    setError(null);
    try {
      // 直接用相对路径请求,无需ngrok URL
      const response = await axios.post('/generate_summary', { text: inputText });
      setSummaries(response.data);
    } catch (error) {
      console.error('获取摘要失败:', error);
      setError('获取摘要失败,请重试');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <h1>文本摘要工具</h1>
        <form onSubmit={handleSubmit}>
          <textarea
            value={inputText}
            onChange={(e) => setInputText(e.target.value)}
            placeholder="请输入要摘要的文本..."
            rows="10"
            cols="50"
          />
          <button type="submit" disabled={loading || !inputText.trim()}>
            {loading ? '生成中...' : '生成摘要'}
          </button>
        </form>
        {error && <p className="error">{error}</p>}
        {summaries && (
          <div className="summaries">
            {Object.keys(summaries).map((model) => (
              <div className="summary" key={model}>
                <h2>{`${model} 摘要`}</h2>
                <p>{summaries[model].summary}</p>
                <h3>精简版:</h3>
                <p>{summaries[model].tldr}</p>
              </div>
            ))}
          </div>
        )}
      </header>
    </div>
  );
}

export default App;

步骤2:优化Flask后端性能与稳定性

  • 提前加载模型:避免每次请求重新初始化模型,大幅提升响应速度:
# 提前加载所有模型,避免重复初始化
loaded_models = {}
for model_name, model_config in models.items():
    loaded_models[model_name] = pipeline(model_config["type"], model=model_config["model"])
    print(f"Loaded {model_name} model successfully.")

# 修改generate_summary路由
@app.route('/generate_summary', methods=['POST'])
def generate_summary():
    try:
        text = request.json.get('text', '')[:2000]
        if not text:
            return jsonify({'error': 'No text provided'}), 400
        summaries = {}
        for model_name, pipe in loaded_models.items():
            model_config = models[model_name]
            if model_config["type"] == "text-generation":
                pipe_out = pipe(text, max_length=512, clean_up_tokenization_spaces=True)
                generated_text = pipe_out[0]['generated_text']
                tldr = generate_tldr(generated_text)
                summaries[model_name] = {"summary": generated_text, "tldr": tldr}
            else:
                pipe_out = pipe(text)
                summary_text = pipe_out[0]['summary_text']
                tldr = generate_tldr(summary_text)
                summaries[model_name] = {"summary": summary_text, "tldr": tldr}
        return jsonify(summaries)
    except Exception as e:
        return jsonify({'error': str(e)}), 500
  • 修复Flask启动参数:添加use_reloader=False避免线程重复启动:
def run_flask():
    app.run(port=8000, debug=False, use_reloader=False)

步骤3:验证流程

  1. 在Colab中运行Flask后端代码,等待所有模型加载完成,复制ngrok公网URL。
  2. 在浏览器中打开该URL,即可使用前端页面输入文本生成摘要。

内容的提问来源于stack exchange,提问作者Amit Bin Tariqul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:07:04