手把手教你用 RAG 技术实现长视频智能问答系统
手把手教你用 RAG 技术实现长视频智能问答系统前言最近在做一个长视频分析项目需要让用户能够通过自然语言查询视频内容。调研后发现 RAGRetrieval-Augmented Generation是非常适合的技术方案。经过一段时间的开发终于完成了这个系统今天来分享一下整个过程。****项目地址https://github.com/DOGOLD/rag-video-qa一、项目概述1.1 解决什么问题想象一下你有一个 2 小时的教学视频想快速找到某个知识点的位置你想了解视频中提到的某个概念的具体解释你想对视频内容进行深度问答但不想看完整视频传统的解决方案是人工标注或者文本搜索但效率很低。而我的系统可以✅ 自动转录视频音频为文本✅ 智能分割视频场景✅ 构建向量索引实现语义搜索✅ 基于视频内容进行智能问答1.2 核心特性多源视频输入支持本地视频和 YouTube 链接智能语音转录使用 OpenAI Whisper 模型️帧级特征提取使用 CLIP 模型自动场景分割基于内容相似度向量语义检索使用 ChromaDBRAG 问答结合 GPT 模型生成答案Docker 部署一行命令启动服务二、技术架构2.1 整体流程视频输入 → 音频转录 → 帧提取 → 场景分割 → 向量索引 → 用户问答 ↓ ↓ ↓ ↓ ↓ ↓ YouTube Whisper CLIP 聚类算法 ChromaDB GPT-4 /本地 算法 API****这里我使用的是自己的中转站api2.2 核心技术栈模块技术选型说明音视频处理FFmpeg视频帧提取、音频分离语音转录WhisperOpenAI 开源的语音识别模型图像嵌入CLIPOpenAI 的图文对比学习模型向量数据库ChromaDB轻量级向量检索数据库LLMOpenAI APIGPT-3.5/4 生成答案前端框架Streamlit快速构建数据应用视频下载yt-dlp支持 YouTube 等多平台2.3 目录结构rag-video-qa/ ├── main.py # 主应用入口 ├── audio_transcriber.py # 音频转录模块 ├── video_downloader.py # 视频下载模块 ├── frame_processor.py # 帧处理模块 ├── scene_segmenter.py # 场景分割模块 ├── vector_index.py # 向量索引模块 ├── qa_engine.py # 问答引擎模块 ├── config.py # 配置文件 ├── requirements.txt # 依赖列表 ├── Dockerfile # Docker 配置 └── docker-compose.yml # 容器编排配置三、核心功能实现3.1 音频转录使用 Whisper 模型进行语音识别。关键点是需要将长音频分段处理因为 Whisper 单次处理有限制。# audio_transcriber.pydeftranscribe_audio(self,audio_path,languagezh):# 将音频分段每段30秒segmentsself._split_audio(audio_path,segment_duration30)all_results[]fori,segmentinenumerate(segments):# 使用 Whisper 转录resultself.model.transcribe(segment,languagelanguage,tasktranscribe)all_results.extend(result[segments])returnself._merge_segments(all_results)****3.2 帧特征提取使用 CLIP 模型提取视频帧的视觉特征# frame_processor.pydefextract_frame_embeddings(self,video_path):framesself._extract_frames(video_path)embeddings[]forframeinframes:# CLIP 图像编码imgImage.fromarray(frame)img_inputself.preprocess(img).unsqueeze(0)withtorch.no_grad():featuresself.clip_model.encode_image(img_input)embeddings.append(features.numpy().tolist())returnembeddings3.3 场景分割基于转录文本和帧嵌入使用聚类算法分割场景# scene_segmenter.pyfromsklearn.clusterimportAgglomerativeClusteringdefsegment_scenes(self,transcripts,frame_embeddings):# 构建相似度矩阵similarity_matrixself._build_similarity_matrix(transcripts,frame_embeddings)# 层次聚类n_scenesmax(1,len(transcripts)//10)# 根据内容长度确定场景数clusteringAgglomerativeClustering(n_clustersn_scenes,metricprecomputed,linkageaverage)labelsclustering.fit_predict(1-similarity_matrix)# 生成场景列表scenesself._create_scenes(transcripts,frame_embeddings,labels)returnscenes3.4 向量索引构建使用 ChromaDB 存储和检索向量# vector_index.pyimportchromadbclassVectorIndex:def__init__(self,index_dir):self.clientchromadb.PersistentClient(pathindex_dir)self.collectionself.client.get_or_create_collection(namevideo_scenes)defadd_scenes(self,scenes):embeddings[s[frame_embedding]forsinscenes]metadatas[{scene_id:int(s[scene_id]),start:float(s[start]),end:float(s[end]),text:str(s[text])}forsinscenes]self.collection.add(embeddingsembeddings,metadatasmetadatas,ids[fscene_{i}foriinrange(len(scenes))])defquery(self,query_embedding,top_k3):returnself.collection.query(query_embeddings[query_embedding],n_resultstop_k)四、踩坑与解决方案这部分是本文最有价值的部分记录了我在开发过程中遇到的各种问题及解决方案。4.1 Whisper 长音频处理失败问题描述ValueError: audio features have variable timesteps原因分析Whisper 模型默认只能处理约 30 秒的音频如果直接传入长音频会报错。解决方案将音频分段处理def_split_audio(self,audio_path,segment_duration30):将长音频分割为小段audioAudioSegment.from_file(audio_path)segments[]foriinrange(0,len(audio),segment_duration*1000):segmentaudio[i:isegment_duration*1000]temp_pathf/tmp/segment_{i}.wavsegment.export(temp_path,formatwav)segments.append(temp_path)returnsegments4.2 帧嵌入与文本嵌入维度不匹配问题描述ValueError: operands could not be broadcast together原因分析TF-IDF 生成的文本嵌入是低维的如 14 维而 CLIP 帧嵌入是高维的768 维无法直接合并。解决方案简化设计仅使用帧嵌入进行检索defadd_scenes(self,scenes,text_embedding_modelNone):forsceneinscenes:ifscene[frame_embedding]isnotNone:embscene[frame_embedding]else:continueself.collection.add(embeddings[emb.tolist()],metadatas[{scene_id:int(scene[scene_id]),start:float(scene[start]),end:float(scene[end]),text:str(scene[text])}],ids[fscene_{scene[scene_id]}])4.3 ChromaDB 数据类型错误问题描述ValueError: Expected metadata value to be a str, int, float, bool... got 7 which is a int64原因分析NumPy 的 int64 类型不被 ChromaDB 支持需要转换为 Python 原生类型。解决方案显式类型转换metadatas.append({scene_id:int(scene[scene_id]),# 转换 int64 → intstart:float(scene[start]),# 转换 float64 → floatend:float(scene[end]),text:str(scene[text])})4.4 YouTube 下载被识别为机器人问题描述ERROR: Sign in to confirm youre not a bot原因分析YouTube 的反爬机制越来越严格yt-dlp 会被识别为机器人。解决方案支持上传 cookies 文件绕过验证defdownload_youtube_video(url,output_dir,cookies_fileNone):ydl_opts{format:bestvideo[extmp4]bestaudio[extm4a]/best,outtmpl:os.path.join(output_dir,%(title)s.%(ext)s),}ifcookies_fileandos.path.exists(cookies_file):ydl_opts[cookiefile]cookies_filewithyt_dlp.YoutubeDL(ydl_opts)asydl:infoydl.extract_info(url,downloadTrue)returnydl.prepare_filename(info)4.5 sklearn API 兼容性问题描述TypeError: AgglomerativeClustering.__init__() got an unexpected keyword argument affinity原因分析新版本 scikit-learn 将affinity参数重命名为metric。解决方案# 旧版本clusteringAgglomerativeClustering(n_clustersn_scenes,affinityprecomputed,linkageaverage)# 新版本clusteringAgglomerativeClustering(n_clustersn_scenes,metricprecomputed,# 注意这里linkageaverage)五、Docker 部署5.1 为什么用 Docker部署 AI 应用的环境配置非常复杂需要 Python 3.10需要 FFmpeg需要下载 Whisper、CLIP 等大模型模型文件可能达到数 GB使用 Docker 可以一键安装所有依赖而且环境隔离不影响主机。5.2 DockerfileFROM python:3.10-slim WORKDIR /app # 安装系统依赖 RUN apt-get update apt-get install -y \ ffmpeg git gcc g python3-dev \ rm -rf /var/lib/apt/lists/* # 安装 Python 依赖 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 下载预训练模型 RUN python -c import whisper; whisper.load_model(base) \ python -c import clip; clip.load(ViT-L/14) # 复制应用代码 COPY . . EXPOSE 8501 CMD [streamlit, run, main.py, --server.port8501]5.3 docker-compose.ymlversion:3.8services:rag-video-qa:build:.container_name:rag-video-qaports:-8501:8501volumes:-./data:/app/data-./models:/app/modelsenvironment:-TRANSFORMERS_CACHE/app/modelsrestart:unless-stopped5.4 一键启动# 构建并启动docker-composeup-d# 查看日志docker-composelogs-f# 停止服务docker-composedown六、使用指南6.1 环境准备方式一Docker 部署推荐gitclone https://github.com/DOGOLD/rag-video-qa.gitcdrag-video-qadocker-composeup-d方式二本地运行pipinstall-rrequirements.txt streamlit run main.py6.2 配置 API Key在左侧边栏输入 OpenAI API KeyAPI Key你的 OpenAI 密钥Base URLAPI 地址使用代理服务时需要修改模型选择系统会自动获取可用模型列表**6.3 上传视频并处理选择输入方式本地文件 / YouTube 链接上传或输入视频点击「开始处理」等待处理完成根据视频长度可能需要几分钟6.4 开始问答处理完成后在问答区域输入问题****七、性能优化建议7.1 GPU 加速如果有 NVIDIA 显卡开启 GPU 加速可以大幅提升性能# config.pyUSE_GPUTrue# 开启 GPU# Whisper 使用 GPUmodelwhisper.load_model(base,devicecuda)# CLIP 使用 GPUclip_model,_clip.load(ViT-L/14,devicecuda)7.2 模型选择权衡模型参数量速度准确性内存需求Tiny39M⚡⚡⚡⭐~1GBBase74M⚡⚡⭐⭐~1GBSmall244M⚡⭐⭐⭐~2GBMedium769M⭐⭐⭐⭐~5GBLarge1550M⭐⭐⭐⭐⭐~10GB建议中文视频使用base或small英文视频可以用medium。7.3 处理速度参考视频时长Whisper 转录CLIP 提取场景分割总计5 分钟~30s~2min~10s~3min30 分钟~3min~12min~30s~16min1 小时~6min~25min~1min~32min八、未来展望目前系统还有一些可以改进的地方8.1 短期优化添加批处理支持同时处理多个视频支持更多视频平台Bilibili、抖音等添加视频摘要自动生成功能8.2 长期规划WebRTC 实时问答多语言翻译支持视频内容知识图谱构建对话式交互界面九、总结本文详细介绍了基于 RAG 技术的长视频问答系统的开发过程包括技术架构使用 Whisper CLIP ChromaDB GPT 的组合核心实现音频转录、帧提取、场景分割、向量检索踩坑记录详细记录了开发过程中遇到的问题和解决方案Docker 部署一键启动服务的完整方案项目已经开源到 GitHub欢迎大家使用和交流。如果有问题或建议可以在 GitHub 提 Issue。参考资料OpenAI WhisperOpenAI CLIPChromaDBStreamlit 文档yt-dlp【全文完】如果觉得文章有帮助欢迎⭐ Star 项目https://github.com/DOGOLD/rag-video-qa 留言讨论 转发分享有任何问题可以在评论区留言我会尽量解答