1. 背景与核心概念:B站AI创造公开赛的技术价值
近期,B站低调上线了一项名为“AI创造公开赛”的活动,在技术社区引发了不小的关注。这并非一次简单的营销活动,而是一个面向广大开发者、内容创作者和AI爱好者的综合性技术实战平台。对于从事AI应用开发、内容生成算法研究,或是希望将AI能力融入创意项目的工程师而言,这是一个绝佳的“练手场”和“灵感库”。
简单来说,B站AI创造公开赛是一个以竞赛形式,鼓励参与者利用各类AI技术(如大语言模型、文生图/视频、语音合成、智能剪辑等)进行内容创作和工具开发的系列活动。其核心价值在于:
- 提供真实场景与数据:相比个人闭门造车,公开赛通常会提供B站生态内的特定场景(如视频摘要生成、弹幕智能过滤、AI辅助剪辑模板)、部分脱敏数据或明确的评价指标,让开发者的项目能直面真实业务需求。
- 降低AI应用门槛:活动往往会整合或推荐一些易用的AI开发平台和工具链,例如百度的文心大模型、阿里云的通义千问等国内主流模型的API,帮助开发者快速搭建原型,聚焦于创意和业务逻辑的实现。
- 连接创意与技术:它打破了“技术”与“内容”的壁垒,鼓励开发者思考如何用AI提升创作效率、丰富内容形式(如AI生成虚拟UP主、自动生成视频字幕和章节),这正是当前AIGC领域最核心的命题。
对于开发者而言,参与此类比赛,其意义远超比赛本身。它是一个系统性的学习路径:从理解赛题、技术选型、环境搭建、模型调用/微调、前后端集成,到最终的项目部署与演示。接下来,我们将以一个典型的参赛项目为例,完整走通一个“AI视频智能摘要生成器”的开发全流程。
2. 环境准备与版本说明
在开始一个AI应用项目前,清晰的环境定义是成功的基石。本次实战我们将构建一个Web应用,后端处理AI逻辑,前端展示结果。以下是我们推荐的环境配置,请根据你的实际情况进行调整。
核心环境栈:
- 操作系统:Ubuntu 20.04 LTS / Windows 10+ WSL2 / macOS Monterey 及以上。本文示例基于 Ubuntu 22.04。
- 编程语言:Python 3.8 - 3.10(AI模型生态支持最广泛的版本范围)。
- 后端框架:FastAPI 0.104+。选择FastAPI因其异步特性适合AI推理的I/O密集型操作,且能自动生成API文档。
- 前端框架:Vue 3 + Element Plus。用于快速构建交互界面。
- AI模型服务:百度千帆大模型平台(ERNIE系列)、OpenAI-Compatible API(或使用本地模型如ChatGLM3-6B)。
- 开发工具:VS Code 或 PyCharm。
- 版本控制:Git。
项目结构预览:在开始编码前,我们先规划好项目目录,这有助于管理复杂的依赖和模块。
ai-video-summarizer/ ├── backend/ # 后端FastAPI服务 │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py # FastAPI应用主入口 │ │ ├── api/ # 路由模块 │ │ │ └── endpoints.py │ │ ├── core/ # 核心配置 │ │ │ └── config.py │ │ ├── models/ # Pydantic数据模型 │ │ │ └── schemas.py │ │ └── services/ # 业务逻辑层,含AI调用 │ │ └── ai_service.py │ ├── requirements.txt # Python依赖 │ └── Dockerfile ├── frontend/ # 前端Vue应用 │ ├── public/ │ ├── src/ │ │ ├── views/ │ │ │ └── HomeView.vue │ │ ├── components/ │ │ ├── api/ # 前端API调用封装 │ │ │ └── backend.js │ │ └── main.js │ ├── package.json │ └── vite.config.js # 构建配置 └── docker-compose.yml # 容器编排3. 核心原理与技术选型拆解
我们的目标是实现“视频智能摘要”。其核心流程可以拆解为以下几个关键技术环节,每个环节都有多种技术方案可选。
3.1 视频内容理解(关键信息提取)AI不能直接“看”视频,我们需要先将视频转化为AI可以处理的文本信息。
- 方案一:语音识别(ASR):提取视频中的旁白、对话。可选用:
- 开源方案:OpenAI Whisper(精度高,支持多语言,但推理稍慢)。
- 云服务API:阿里云、腾讯云、百度云的语音识别服务(稳定,有免费额度)。
- 方案二:关键帧分析与OCR:提取视频中的字幕、标题、图表文字。可选用:
- 开源方案:OpenCV(抽帧) + PaddleOCR / EasyOCR(识别文字)。
- 方案三:直接使用视频理解大模型:如Video-LLaMA、InternVideo等,可直接输出视频描述,但部署门槛较高。
对于公开赛快速原型,推荐方案一(ASR)结合方案二(OCR抽字幕),能覆盖大部分信息型视频。
3.2 文本摘要生成(核心AI能力)获得视频文本稿后,需要生成简洁摘要。
- 方案一:调用大语言模型(LLM)API:这是最快捷、效果通常最好的方式。
- 国内平台:百度文心千帆(ERNIE-Bot)、阿里通义千问、智谱AI(ChatGLM)、月之暗面(Kimi)。它们提供了丰富的上下文窗口和强大的指令跟随能力。
- 提示词工程:这是决定摘要质量的关键。你需要设计一个清晰的“系统提示词”(System Prompt)来引导模型。
- 方案二:使用本地摘要模型:如BART、T5等经过微调的文本摘要模型。优点是数据隐私性好、无网络延迟,但需要一定的机器学习部署能力。
对于公开赛,强烈推荐方案一。我们以百度千帆的ERNIE-Bot API为例进行演示。
3.3 系统架构设计一个可用的应用还需要前后端架构。
- 异步处理:视频转文本和AI摘要都是耗时操作,必须采用异步任务,避免HTTP请求阻塞。可以使用Celery + Redis,或者利用FastAPI的
BackgroundTasks简单处理。 - API设计:遵循RESTful风格,设计清晰的任务提交、状态查询、结果获取接口。
- 前端交互:需要提供文件上传、任务进度显示、结果展示(摘要文本、高亮关键词、时间戳片段)的界面。
4. 完整实战案例:构建视频摘要生成Web应用
下面我们开始分步实现。我们将先搭建后端AI服务,再构建一个简单的前端界面。
4.1 后端服务搭建(FastAPI + 千帆大模型)
第一步:创建虚拟环境并安装依赖
# 进入后端目录 cd backend # 创建虚拟环境(以venv为例) python -m venv venv # 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate # 安装核心依赖 pip install fastapi uvicorn httpx python-multipart pydantic-settings # 安装ASR依赖(以Whisper为例,需确保已安装ffmpeg) pip install openai-whisper # 安装OCR依赖(以PaddleOCR为例) pip install paddlepaddle paddleocr第二步:编写配置文件创建backend/app/core/config.py,管理敏感信息和设置。
# backend/app/core/config.py from pydantic_settings import BaseSettings from typing import Optional class Settings(BaseSettings): # 百度千帆API配置 (从环境变量读取,避免硬编码) qianfan_ak: Optional[str] = None # API Key qianfan_sk: Optional[str] = None # Secret Key qianfan_api_base: str = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions" # 模型选择 qianfan_model: str = "ernie-3.5-8k" # 或 "ernie-4.0-8k-preview" # 应用配置 project_name: str = "B站AI创造赛-视频摘要器" api_v1_prefix: str = "/api/v1" # 文件上传设置 max_upload_size: int = 200 * 1024 * 1024 # 200MB allowed_extensions: set = {".mp4", ".avi", ".mov", ".mkv"} class Config: env_file = ".env" # 从.env文件加载配置 settings = Settings()创建.env文件(切记加入.gitignore):
QIANFAN_AK=your_actual_api_key_here QIANFAN_SK=your_actual_secret_key_here第三步:实现AI服务层创建backend/app/services/ai_service.py,封装所有AI能力。
# backend/app/services/ai_service.py import httpx import whisper from paddleocr import PaddleOCR import asyncio from typing import List, Tuple import logging from ..core.config import settings logger = logging.getLogger(__name__) class AIService: def __init__(self): self.ocr_engine = PaddleOCR(use_angle_cls=True, lang='ch') # Whisper模型加载较慢,可延迟加载或单例管理 self.asr_model = None async def extract_text_from_video(self, video_path: str) -> str: """从视频提取文本:结合ASR和OCR""" full_text = "" # 1. 使用Whisper进行语音识别 asr_text = await self._run_async(self._transcribe_audio, video_path) if asr_text: full_text += f"[语音内容]\n{asr_text}\n\n" # 2. 使用PaddleOCR识别关键帧字幕(简化:抽第1, 中间,最后帧) # 此处省略具体的抽帧和OCR代码,核心是调用 self.ocr_engine.ocr(img_path) # ocr_results = self.ocr_engine.ocr(frame_path, cls=True) # ocr_text = ' '.join([line[1][0] for res in ocr_results for line in res]) # full_text += f"[字幕内容]\n{ocr_text}\n" return full_text if full_text else "未能从视频中提取到有效文本。" def _transcribe_audio(self, video_path: str) -> str: """同步的Whisper转录函数,需在线程池中运行避免阻塞事件循环""" if self.asr_model is None: self.asr_model = whisper.load_model("base") # 可选 tiny, base, small result = self.asr_model.transcribe(video_path, language="zh", fp16=False) return result["text"] async def _run_async(self, func, *args): """将同步CPU密集型函数放入线程池执行""" loop = asyncio.get_event_loop() return await loop.run_in_executor(None, func, *args) async def generate_summary_with_llm(self, extracted_text: str) -> str: """调用千帆大模型生成摘要""" if not settings.qianfan_ak or not settings.qianfan_sk: raise ValueError("千帆API配置未设置") # 构建提示词 system_prompt = """你是一个专业的视频内容总结助手。请根据用户提供的视频转录文本,生成一个简洁、准确、连贯的摘要。 摘要要求: 1. 长度在150-300字之间。 2. 抓住核心论点、关键数据和结论。 3. 用中文输出,语言流畅自然。 4. 如果文本中有明显的时间戳或章节标记,可以在摘要中提及。 请直接输出摘要正文,不要加“摘要:”等前缀。""" messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"视频转录文本:\n{extracted_text}"} ] # 获取Access Token (千帆API需要) async with httpx.AsyncClient() as client: token_url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={settings.qianfan_ak}&client_secret={settings.qianfan_sk}" token_resp = await client.post(token_url) access_token = token_resp.json().get("access_token") if not access_token: logger.error("获取千帆Access Token失败") return "调用AI服务失败,请检查API配置。" # 调用Chat Completion接口 api_url = f"{settings.qianfan_api_base}?access_token={access_token}" payload = { "messages": messages, "stream": False, "temperature": 0.7, "top_p": 0.8, } headers = {"Content-Type": "application/json"} try: resp = await client.post(api_url, json=payload, headers=headers, timeout=30.0) resp.raise_for_status() result = resp.json() return result.get("result", "AI生成摘要时出现未知错误。") except httpx.RequestError as e: logger.error(f"请求千帆API失败: {e}") return f"网络请求失败: {str(e)}" except Exception as e: logger.error(f"处理AI响应失败: {e}") return f"AI服务处理异常: {str(e)}" # 创建全局服务实例 ai_service = AIService()第四步:创建数据模型与API路由创建backend/app/models/schemas.py:
# backend/app/models/schemas.py from pydantic import BaseModel from typing import Optional class SummaryRequest(BaseModel): video_url: Optional[str] = None # 或通过文件上传 class SummaryResponse(BaseModel): task_id: str status: str # processing, completed, failed extracted_text: Optional[str] = None summary: Optional[str] = None error_message: Optional[str] = None创建backend/app/api/endpoints.py:
# backend/app/api/endpoints.py from fastapi import APIRouter, UploadFile, File, BackgroundTasks, HTTPException import uuid import os from typing import Dict from ...services.ai_service import ai_service from ..models.schemas import SummaryRequest, SummaryResponse router = APIRouter() # 内存中存储任务状态(生产环境应用数据库或Redis) tasks: Dict[str, dict] = {} @router.post("/summarize", response_model=SummaryResponse) async def create_summary_task( background_tasks: BackgroundTasks, file: UploadFile = File(...) ): """上传视频文件并创建摘要任务""" # 1. 文件验证 file_ext = os.path.splitext(file.filename)[1].lower() from ..core.config import settings if file_ext not in settings.allowed_extensions: raise HTTPException(400, detail=f"不支持的文件格式。允许的格式: {settings.allowed_extensions}") # 2. 保存文件 task_id = str(uuid.uuid4()) save_dir = f"uploads/{task_id}" os.makedirs(save_dir, exist_ok=True) file_path = os.path.join(save_dir, file.filename) with open(file_path, "wb") as f: content = await file.read() f.write(content) # 3. 初始化任务状态 tasks[task_id] = {"status": "processing", "file_path": file_path} # 4. 将耗时的处理任务加入后台 background_tasks.add_task(process_video_summary, task_id, file_path) return SummaryResponse(task_id=task_id, status="processing") @router.get("/task/{task_id}", response_model=SummaryResponse) async def get_task_status(task_id: str): """查询任务状态和结果""" task = tasks.get(task_id) if not task: raise HTTPException(status_code=404, detail="任务不存在") return SummaryResponse(**task) async def process_video_summary(task_id: str, file_path: str): """后台处理任务的核心逻辑""" try: tasks[task_id]["status"] = "processing" # 步骤1: 提取视频文本 extracted_text = await ai_service.extract_text_from_video(file_path) tasks[task_id]["extracted_text"] = extracted_text # 步骤2: 调用LLM生成摘要 summary = await ai_service.generate_summary_with_llm(extracted_text) tasks[task_id]["summary"] = summary tasks[task_id]["status"] = "completed" except Exception as e: tasks[task_id]["status"] = "failed" tasks[task_id]["error_message"] = str(e) # 生产环境应记录详细日志 print(f"任务 {task_id} 处理失败: {e}")第五步:组装主应用创建backend/app/main.py:
# backend/app/main.py from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles import os from .core.config import settings from .api.endpoints import router as api_router app = FastAPI(title=settings.project_name) # 配置CORS,允许前端访问 app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173"], # Vue开发服务器默认端口 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 创建上传文件目录 os.makedirs("uploads", exist_ok=True) app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") # 注册API路由 app.include_router(api_router, prefix=settings.api_v1_prefix) @app.get("/") async def root(): return {"message": "欢迎使用视频智能摘要生成API服务", "docs": "/docs"}第六步:运行后端服务在backend目录下创建requirements.txt并安装所有依赖后,运行:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000访问http://localhost:8000/docs即可看到自动生成的交互式API文档,并可以测试接口。
4.2 前端界面搭建(Vue 3 + Element Plus)
第一步:初始化Vue项目并安装依赖
# 在项目根目录下 npm create vue@latest frontend # 按照提示选择项目特性,确保选中 TypeScript, Vue Router, Pinia cd frontend npm install npm install axios element-plus @element-plus/icons-vue第二步:封装后端API调用创建frontend/src/api/backend.js:
// frontend/src/api/backend.js import axios from 'axios'; const apiClient = axios.create({ baseURL: 'http://localhost:8000/api/v1', // 后端API地址 timeout: 60000, // 超时时间设长,因为处理视频可能较慢 }); export default { // 上传视频并创建摘要任务 async createSummaryTask(file) { const formData = new FormData(); formData.append('file', file); const response = await apiClient.post('/summarize', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); return response.data; }, // 查询任务状态 async getTaskStatus(taskId) { const response = await apiClient.get(`/task/${taskId}`); return response.data; }, };第三步:创建主页面组件创建frontend/src/views/HomeView.vue:
<!-- frontend/src/views/HomeView.vue --> <template> <div class="home-container"> <el-card class="box-card"> <template #header> <div class="card-header"> <span>B站AI创造赛 - 视频智能摘要生成器</span> </div> </template> <!-- 文件上传区域 --> <el-upload class="upload-demo" drag action="#" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false" :disabled="isProcessing" > <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text"> 将视频文件拖到此处,或 <em>点击上传</em> </div> <template #tip> <div class="el-upload__tip"> 支持 MP4, AVI, MOV, MKV 格式,文件大小不超过200MB </div> </template> </el-upload> <!-- 上传文件信息 --> <div v-if="uploadFile" class="file-info"> <el-tag type="info">{{ uploadFile.name }}</el-tag> <el-button size="small" @click="startProcessing" type="primary" :loading="isProcessing"> 开始生成摘要 </el-button> </div> <!-- 任务状态与结果展示 --> <div v-if="currentTaskId" class="task-section"> <el-divider /> <h3>任务处理状态</h3> <el-steps :active="stepActive" align-center finish-status="success"> <el-step title="上传完成" /> <el-step title="内容提取中" /> <el-step title="AI摘要生成中" /> <el-step title="完成" /> </el-steps> <div class="status-display"> <el-alert v-if="taskStatus === 'processing'" title="正在处理中,请稍候..." type="info" :closable="false" show-icon /> <el-alert v-if="taskStatus === 'completed'" title="摘要生成成功!" type="success" show-icon /> <el-alert v-if="taskStatus === 'failed'" :title="'处理失败: ' + errorMessage" type="error" show-icon /> </div> <!-- 结果显示 --> <div v-if="taskStatus === 'completed'" class="result-container"> <el-tabs type="border-card"> <el-tab-pane label="生成的摘要"> <div class="summary-text">{{ summaryText }}</div> <el-button type="primary" @click="copyToClipboard(summaryText)">复制摘要</el-button> </el-tab-pane> <el-tab-pane label="提取的原始文本"> <div class="extracted-text">{{ extractedText }}</div> </el-tab-pane> </el-tabs> </div> </div> </el-card> </div> </template> <script setup lang="ts"> import { ref, computed, onUnmounted } from 'vue'; import { UploadFilled } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; import backendApi from '@/api/backend'; const uploadFile = ref<File | null>(null); const currentTaskId = ref<string>(''); const taskStatus = ref<string>(''); // processing, completed, failed const extractedText = ref<string>(''); const summaryText = ref<string>(''); const errorMessage = ref<string>(''); const isProcessing = ref<boolean>(false); let pollInterval: number | null = null; const stepActive = computed(() => { switch (taskStatus.value) { case 'processing': return 2; case 'completed': return 4; case 'failed': return 0; default: return 1; } }); const handleFileChange = (file: any) => { uploadFile.value = file.raw; }; const startProcessing = async () => { if (!uploadFile.value) return; isProcessing.value = true; taskStatus.value = 'processing'; try { // 1. 上传文件,创建任务 const response = await backendApi.createSummaryTask(uploadFile.value); currentTaskId.value = response.task_id; ElMessage.success('任务已提交,开始处理'); // 2. 开始轮询任务状态 startPollingTaskStatus(); } catch (error: any) { ElMessage.error('任务提交失败: ' + error.message); isProcessing.value = false; taskStatus.value = 'failed'; errorMessage.value = error.message; } }; const startPollingTaskStatus = () => { if (pollInterval) clearInterval(pollInterval); pollInterval = setInterval(async () => { if (!currentTaskId.value) return; try { const statusResp = await backendApi.getTaskStatus(currentTaskId.value); taskStatus.value = statusResp.status; if (statusResp.status === 'completed') { extractedText.value = statusResp.extracted_text || ''; summaryText.value = statusResp.summary || ''; isProcessing.value = false; stopPolling(); ElMessage.success('摘要生成完成!'); } else if (statusResp.status === 'failed') { errorMessage.value = statusResp.error_message || '未知错误'; isProcessing.value = false; stopPolling(); ElMessage.error('处理失败'); } // 如果状态仍是 processing,继续轮询 } catch (error) { console.error('轮询任务状态失败:', error); } }, 2000); // 每2秒查询一次 }; const stopPolling = () => { if (pollInterval) { clearInterval(pollInterval); pollInterval = null; } }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text).then(() => { ElMessage.success('已复制到剪贴板'); }); }; onUnmounted(() => { stopPolling(); }); </script> <style scoped> .home-container { max-width: 900px; margin: 40px auto; padding: 20px; } .file-info { margin-top: 20px; display: flex; align-items: center; gap: 15px; } .task-section { margin-top: 30px; } .status-display { margin: 20px 0; } .summary-text, .extracted-text { white-space: pre-wrap; line-height: 1.6; padding: 15px; background-color: #f9f9f9; border-radius: 4px; margin-bottom: 15px; max-height: 400px; overflow-y: auto; } </style>第四步:运行前端应用在frontend目录下:
npm run dev访问http://localhost:5173,即可看到一个完整的视频上传、处理状态跟踪、摘要结果展示的Web应用。
5. 部署与优化:从本地到可分享的参赛作品
完成本地开发后,为了让你的作品能在B站AI创造公开赛中展示,你需要将其部署到一个可公开访问的环境。
5.1 容器化部署(使用Docker)这是最推荐的方式,能确保环境一致性。
后端Dockerfile (backend/Dockerfile):
# backend/Dockerfile FROM python:3.9-slim WORKDIR /app # 安装系统依赖(Whisper需要ffmpeg) RUN apt-get update && apt-get install -y \ ffmpeg \ && rm -rf /var/lib/apt/lists/* # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY ./app ./app # 创建上传目录 RUN mkdir -p uploads # 环境变量(在运行时通过docker-compose或云平台注入) ENV PYTHONPATH=/app CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]前端Dockerfile (frontend/Dockerfile):
# frontend/Dockerfile FROM node:18-alpine as build-stage WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine as production-stage COPY --from=build-stage /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]前端Nginx配置 (frontend/nginx.conf):
# frontend/nginx.conf server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # 处理前端路由(如Vue Router的history模式) location / { try_files $uri $uri/ /index.html; } # 代理后端API请求 location /api/ { proxy_pass http://backend:8000; # 指向后端服务名 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }Docker Compose编排 (docker-compose.yml):
# docker-compose.yml version: '3.8' services: backend: build: ./backend ports: - "8000:8000" environment: - QIANFAN_AK=${QIANFAN_AK} - QIANFAN_SK=${QIANFAN_SK} volumes: - ./uploads:/app/uploads # 持久化上传文件 restart: unless-stopped frontend: build: ./frontend ports: - "80:80" depends_on: - backend restart: unless-stopped运行docker-compose up --build -d即可一键启动整个应用栈。
5.2 云服务器部署你可以将上述Docker Compose项目部署到任何支持Docker的云服务器(如阿里云ECS、腾讯云CVM)。关键步骤:
- 在服务器安装Docker和Docker Compose。
- 将项目代码上传至服务器(使用Git或SCP)。
- 在服务器项目根目录创建
.env文件并填入你的千帆API密钥。 - 运行
docker-compose up -d。 - 配置服务器安全组,开放80(前端)和8000(后端API,可选)端口。
- 绑定域名(可选),并配置Nginx反向代理。
5.3 部署到Vercel / Netlify (前端) + Railway / Render (后端)对于不想管理服务器的开发者,可以使用Serverless或PaaS平台:
- 前端:将
frontend/dist目录构建的静态文件部署到Vercel或Netlify,并配置重写规则(支持SPA)。 - 后端:将Backend服务部署到Railway或Render。这些平台能直接连接你的Git仓库,自动构建和部署,并方便地管理环境变量。
6. 常见问题与排查思路
在开发与部署过程中,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 上传视频后,任务长时间处于“processing”状态 | 1. 视频文件过大,处理耗时。 2. Whisper模型首次加载慢。 3. 千帆API调用超时或失败。 4. 后台任务线程阻塞。 | 1. 查看后端日志,确认处理进度。 2. 增加前端轮询超时时间,并给用户进度提示。 3. 对于大文件,考虑使用Celery等分布式任务队列,并提供更精确的任务状态。 4. 在 ai_service.py中添加更详细的日志,记录每个步骤的开始和结束。 |
| 调用千帆API返回“认证失败”或“无效的Access Token” | 1. API Key或Secret Key错误。 2. 环境变量未正确加载。 3. Token获取URL或参数有误。 | 1. 检查.env文件中的QIANFAN_AK和QIANFAN_SK是否正确无误,并确保已重启服务。2. 在代码中打印或日志输出 settings.qianfan_ak的前几位,确认是否成功加载。3. 参考百度千帆官方文档,确认Token获取接口是否有更新。 |
| Whisper识别中文视频效果差 | 1. 使用了不支持中文的模型(如tiny.en)。2. 视频背景噪音过大。 3. 语音方言或语速问题。 | 1. 确保加载模型时指定language="zh",如whisper.load_model("base", language="zh")。2. 尝试更大的模型,如 small或medium,精度更高但速度更慢。3. 考虑在调用Whisper前,使用 ffmpeg对音频进行降噪预处理。 |
| 前端访问后端API出现CORS错误 | 后端未正确配置CORS,或前端请求的Origin不在允许列表中。 | 1. 检查后端main.py中allow_origins是否包含了前端运行的地址(如http://localhost:5173)。2. 生产环境部署时,需将其改为前端的实际域名或使用动态配置。 3. 对于复杂请求(如带自定义头),需在CORS配置中明确允许 allow_headers。 |
| Docker容器内无法写入上传文件 | 容器内的应用用户权限不足,或挂载的卷权限不正确。 | 1. 在Dockerfile中创建目录时指定权限:RUN mkdir -p uploads && chmod 777 uploads。2. 在 docker-compose.yml中检查volume挂载路径是否正确。3. 确保宿主机对应目录有写权限。 |
| PaddleOCR初始化失败或识别慢 | 1. 首次运行需要下载模型文件,网络问题可能导致失败。 2. 服务器内存或CPU不足。 | 1. 检查网络,或考虑在构建Docker镜像时预先下载好模型文件。 2. 对于轻量级应用,可以关闭角度分类( use_angle_cls=False)以提升速度。3. 如果OCR不是核心需求,可以考虑移除该功能,或改用更轻量的OCR引擎。 |
7. 进阶优化与参赛建议
一个能打动评委的参赛作品,不仅在于功能的实现,更在于细节的打磨和创意的延伸。
7.1 功能增强
- 支持视频URL输入:除了文件上传,允许用户输入B站、YouTube等平台的视频链接,后端自动使用
yt-dlp等工具下载并处理。 - 摘要风格化:让用户选择摘要风格,如“学术报告风”、“轻松口语化”、“要点罗列(bullet points)”、“适合发微博的简短文案”。通过修改LLM的System Prompt实现。
- 关键片段定位:不仅生成摘要,还输出视频中对应核心观点的时间戳。这需要更复杂的算法,例如在ASR结果的时间戳中,找出与摘要句子语义最匹配的片段。
- 多语言支持:利用Whisper的多语言识别能力和LLM的多语言生成能力,支持中英文视频的摘要生成。
7.2 性能与体验优化
- 任务队列与状态持久化:使用Celery + Redis或RQ管理后台任务,将任务状态存入数据库(如PostgreSQL),即使服务重启也不会丢失任务。
- 进度反馈:将视频处理拆分为“下载中”、“语音识别中”、“文本分析中”、“摘要生成中”等多个子步骤,并通过WebSocket或Server-Sent Events (SSE) 向前端实时推送进度百分比。
- 结果缓存:对同一视频文件(可通过MD5判断)的摘要结果进行缓存,避免重复处理,节省资源和API调用成本。
- 前端优化:使用Vue的Suspense组件优化加载状态,对长文本结果实现虚拟滚动。
7.3 工程化与可维护性
- 配置中心化:将所有配置(API密钥、模型路径、超时时间)移至环境变量或配置中心(如Apollo),避免硬编码。
- 完善的日志与监控:集成
structlog或loguru进行结构化日志记录,并接入Sentry等错误监控平台。 - 单元测试与集成测试:为关键的AI服务函数和API端点编写测试,确保代码质量。
- API限流与鉴权:使用
slowapi为公开接口添加限流,防止滥用。为管理接口添加JWT鉴权。
7.4 针对B站AI创造公开赛的包装建议
- 清晰的README:在项目仓库根目录提供详细的
README.md,说明项目背景、技术架构、快速启动方式、功能演示和未来规划。 - 录制演示视频:这是最重要的一环。录制一个2-3分钟的高清视频,清晰展示从上传一个B站知识区视频到获得精炼摘要的全过程。视频中可简要口述技术亮点。
- 突出创意与实用性:在项目描述中,强调你的作品解决了B站用户或UP主的什么痛点(如快速了解长视频内容、为视频自动生成章节摘要、辅助创作等)。
- 代码规范与注释:确保代码结构清晰,有必要的注释,这能体现你的工程素养。
通过以上步骤,你不仅完成了一个可用于参赛的“视频智能摘要生成器”项目,更系统地实践了从AI模型选型、前后端开发、异步处理到服务部署的全栈开发流程。B站AI创造公开赛提供的正是这样一个将前沿AI技术转化为具体应用场景的舞台,关键在于动手实现,并在过程中不断迭代优化。