基于RAG技术构建B站收藏夹AI知识库:从视频到智能问答的完整实践
在实际项目中我们经常需要将分散在不同平台如视频网站、文档、网页的优质内容整合起来构建一个统一、可查询的知识库。BilibiliB站作为国内最大的视频学习社区之一其收藏夹里往往沉淀了大量有价值的教程、技术分享和行业洞见。然而这些内容通常以非结构化的视频形式存在难以被高效检索和利用。本文将介绍如何通过一个开源项目将你的B站收藏夹视频内容转化为一个支持语义搜索和智能问答的AI知识库。整个过程涉及视频信息获取、内容转录、向量化存储以及基于RAG检索增强生成的问答应用构建。这个方案的核心思路是通过B站API获取收藏夹中的视频列表利用语音识别技术将视频内容转为文本然后将文本切片并转换为向量存入向量数据库。当用户提问时系统会从向量库中检索出最相关的文本片段连同问题一起提交给大语言模型生成精准的答案。我们将使用Python作为主要开发语言结合一些成熟的开源库来完成每个环节。最终你将拥有一个私有、可定制、且能持续更新的个人知识库系统。1. 理解核心概念RAG与AI知识库的工作机制在动手之前我们需要理解几个关键概念这决定了后续技术选型和实现路径。1.1 什么是RAG检索增强生成RAG是一种结合了信息检索和文本生成的技术范式。传统的语言模型仅依赖其训练时学到的参数化知识来回答问题这可能导致信息过时或产生“幻觉”即编造事实。RAG通过引入一个外部的、可更新的知识源如向量数据库来解决这个问题。其工作流程可以概括为索引Indexing将原始文档如视频字幕、文章分割成较小的文本块Chunks并使用嵌入模型Embedding Model将每个文本块转换为一个高维向量Vector存入向量数据库。检索Retrieval当用户提出一个问题Query时系统同样使用嵌入模型将问题转换为向量然后在向量数据库中搜索与之最相似的若干个文本块向量。增强Augmentation将检索到的相关文本块作为“上下文”Context与用户的原始问题一起组合成一个新的提示Prompt发送给大语言模型。生成Generation大语言模型基于这个包含了相关上下文的提示生成最终的回答。这样模型的回答不仅基于其内部知识更基于你提供的、最新的、具体的外部知识从而大幅提升答案的准确性和可靠性。1.2 AI知识库的典型架构一个完整的AI知识库系统通常包含以下组件数据源如B站收藏夹、本地文档、网页等。数据加载器负责从不同来源读取原始数据如通过B站API获取视频信息。文本处理器包括语音转文字ASR、文本清洗、分割等。嵌入模型将文本转换为向量的模型如text-embedding-ada-002、bge-large-zh等。向量数据库存储和高效检索向量的数据库如Chroma、Milvus、Qdrant、PGVector。大语言模型负责最终生成答案可以是OpenAI API、智谱AI、DeepSeek等云端API也可以是本地部署的Ollama模型。应用框架用于编排以上流程如LangChain、LlamaIndex、Spring AI等。本方案将采用一个轻量级的组合Python 相关SDK Chroma向量数据库 开源嵌入模型 大语言模型API。2. 环境准备与项目初始化我们将创建一个独立的Python项目来完成所有工作。请确保你的开发环境满足以下要求。2.1 基础环境与工具检查首先确认你的系统已安装以下工具Python 3.9这是大多数AI库支持的最低版本。pipPython包管理工具。Git用于克隆示例项目或管理代码。FFmpeg用于处理视频和音频文件是语音转文字工具的常见依赖。在终端中执行以下命令进行检查和安装# 检查Python和pip版本 python --version pip --version # 检查Git git --version # 安装FFmpeg (以Ubuntu/Debian为例) sudo apt update sudo apt install ffmpeg -y # 对于macOS可以使用Homebrew # brew install ffmpeg2.2 创建项目目录与虚拟环境为了避免包依赖冲突强烈建议为项目创建独立的虚拟环境。# 1. 创建项目目录并进入 mkdir bilibili_ai_knowledge_base cd bilibili_ai_knowledge_base # 2. 创建Python虚拟环境 python -m venv venv # 3. 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows # venv\Scripts\activate # 激活后命令行提示符前应显示 (venv)2.3 安装核心Python依赖我们将分步安装所需的库。创建一个requirements.txt文件并填入以下内容# 基础工具与网络请求 requests2.28.0 beautifulsoup44.11.0 # 可选用于解析网页 # B站API相关 (非官方社区维护) bilibili-api-python14.0.0 # 语音转文字 openai-whisper20231117 # OpenAI开源的ASR模型 # 或者使用 faster-whisper (Whisper的C实现更快) # faster-whisper0.10.0 # 文本处理与RAG框架 langchain0.1.0 langchain-community0.0.10 # 包含各种社区加载器 langchain-chroma0.1.0 # Chroma向量库集成 sentence-transformers2.2.0 # 用于本地嵌入模型 # 向量数据库 chromadb0.4.0 # 大语言模型调用 openai1.0.0 # 调用OpenAI API # 或使用智谱、DeepSeek等国内API的SDK # zhipuai2.0.0 # dashscope1.14.0 # 阿里通义千问然后使用pip安装pip install -r requirements.txt注意openai-whisper模型首次运行时会自动下载模型文件较大如base模型约150MBlarge模型约3GB请确保网络通畅和足够的磁盘空间。如果追求速度可以使用faster-whisper它需要单独安装ctranslate2。3. 构建数据管道从B站收藏夹到文本向量这是整个项目的核心我们将分步实现数据获取、处理和存储的完整流程。3.1 获取B站收藏夹视频列表B站官方提供了开放的API但获取用户收藏夹需要登录后的Cookie信息。我们使用社区维护的bilibili-api-python库来简化操作。首先你需要获取自己的B站Cookie。在浏览器中登录B站后打开开发者工具F12切换到Network选项卡刷新页面点击任意一个请求在Request Headers中找到Cookie字段将其复制出来。重要请妥善保管你的Cookie不要泄露或提交到公开仓库。创建一个名为bilibili_collector.py的文件import asyncio from bilibili_api import user, sync import json async def get_favorite_videos(fav_list_id: int, cookie: str): 获取指定收藏夹内的所有视频 :param fav_list_id: 收藏夹ID默认为0表示“默认收藏夹” :param cookie: 你的B站登录Cookie :return: 视频信息列表 # 初始化用户对象这里需要你的UID # 你可以在B站个人主页的URL中找到UID uid 12345678 # 替换为你的实际UID u user.User(uiduid) # 设置Cookie关键步骤 from bilibili_api import settings settings.cookie cookie # 获取收藏夹内容 favorite_list await u.get_favorite_list(fidfav_list_id) videos_info [] for item in favorite_list[medias]: video_info { bvid: item[bvid], # 视频BV号 title: item[title], owner: item[owner][name], duration: item[duration], # 视频时长秒 pubdate: item[pubdate], # 发布时间戳 desc: item.get(desc, ), # 视频描述 url: fhttps://www.bilibili.com/video/{item[bvid]} } videos_info.append(video_info) print(f已获取视频: {video_info[title]}) return videos_info if __name__ __main__: # 你的B站Cookie YOUR_COOKIE SESSDATAxxxxxx; bili_jctxxxxxx; ... # 收藏夹ID默认为0默认收藏夹也可以在收藏夹页面URL中找到mlid参数 FAV_LIST_ID 0 videos sync(get_favorite_videos(FAV_LIST_ID, YOUR_COOKIE)) # 将视频列表保存为JSON文件供后续步骤使用 with open(favorite_videos.json, w, encodingutf-8) as f: json.dump(videos, f, ensure_asciiFalse, indent2) print(f共获取 {len(videos)} 个视频信息已保存到 favorite_videos.json)运行此脚本你将得到一个包含收藏夹内所有视频基本信息的JSON文件。3.2 下载视频音频并转换为文字有了视频列表下一步是获取视频的音频内容并将其转为文本。这里我们使用yt-dlp下载音频再用Whisper进行转录。首先安装yt-dlppip install yt-dlp然后创建transcribe_videos.py文件import yt_dlp import whisper import json import os from pathlib import Path def download_audio(bvid: str, url: str, output_dir: Path): 使用yt-dlp下载视频的音频 :param bvid: 视频BV号用于命名 :param url: 视频完整URL :param output_dir: 音频文件输出目录 :return: 下载的音频文件路径 ydl_opts { format: bestaudio/best, outtmpl: str(output_dir / f{bvid}.%(ext)s), quiet: True, no_warnings: True, postprocessors: [{ key: FFmpegExtractAudio, preferredcodec: mp3, preferredquality: 192, }], } try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) # 获取实际生成的文件名 audio_file list(output_dir.glob(f{bvid}.*))[0] print(f音频下载成功: {audio_file.name}) return audio_file except Exception as e: print(f下载 {bvid} 失败: {e}) return None def transcribe_audio(audio_path: Path, model_namebase): 使用Whisper模型转录音频 :param audio_path: 音频文件路径 :param model_name: Whisper模型大小可选 tiny, base, small, medium, large :return: 转录文本 print(f开始转录: {audio_path.name}使用模型 {model_name}) model whisper.load_model(model_name) result model.transcribe(str(audio_path), languagezh) return result[text] def process_videos_from_json(json_path: str, output_base_dirdata): 主处理流程读取JSON下载音频转录保存文本 with open(json_path, r, encodingutf-8) as f: videos json.load(f) base_dir Path(output_base_dir) audio_dir base_dir / audio text_dir base_dir / text audio_dir.mkdir(parentsTrue, exist_okTrue) text_dir.mkdir(parentsTrue, exist_okTrue) knowledge_data [] for idx, video in enumerate(videos, 1): print(f\n处理进度: {idx}/{len(videos)}) bvid video[bvid] url video[url] title video[title] # 1. 下载音频 audio_file download_audio(bvid, url, audio_dir) if not audio_file: continue # 2. 转录 try: transcript transcribe_audio(audio_file, model_namebase) # 初次尝试可使用base模型 except Exception as e: print(f转录失败 {bvid}: {e}) transcript # 3. 保存文本 text_file text_dir / f{bvid}.txt with open(text_file, w, encodingutf-8) as f: f.write(transcript) # 4. 构建知识条目 knowledge_entry { bvid: bvid, title: title, owner: video[owner], url: url, text_path: str(text_file), transcript: transcript[:500] ... if len(transcript) 500 else transcript # 摘要 } knowledge_data.append(knowledge_entry) # 可选删除音频文件以节省空间 # audio_file.unlink() # 保存处理后的元数据 meta_file base_dir / knowledge_metadata.json with open(meta_file, w, encodingutf-8) as f: json.dump(knowledge_data, f, ensure_asciiFalse, indent2) print(f\n处理完成元数据已保存至: {meta_file}) return knowledge_data if __name__ __main__: # 从第一步生成的JSON文件开始处理 process_videos_from_json(favorite_videos.json)这个脚本会依次下载每个视频的音频调用Whisper模型进行中文转录并将文本保存下来。首次运行会下载Whisper模型请耐心等待。3.3 文本分割与向量化存储现在我们有了原始文本但需要将其切割成适合检索的片段并转换为向量存入数据库。这里我们使用LangChain的文档加载器和Chroma向量数据库。创建build_vector_store.py文件from langchain_community.document_loaders import TextLoader, DirectoryLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma import os def create_vector_store(text_dir: str, persist_dir: str ./chroma_db): 从文本目录创建向量存储 :param text_dir: 存放转录文本的目录 :param persist_dir: Chroma数据库持久化目录 # 1. 加载文档 print(正在加载文档...) loader DirectoryLoader(text_dir, glob**/*.txt, loader_clsTextLoader) documents loader.load() print(f共加载 {len(documents)} 个文档) # 2. 分割文本 print(正在分割文本...) # 使用递归字符分割器适合中文 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块的最大字符数 chunk_overlap50, # 块之间的重叠字符数保持上下文连贯 separators[\n\n, \n, 。, , , , , 、, , ] # 中文分隔符优先 ) splits text_splitter.split_documents(documents) print(f分割为 {len(splits)} 个文本块) # 3. 初始化嵌入模型 print(正在初始化嵌入模型...) # 使用开源的中文嵌入模型无需API密钥 model_name BAAI/bge-small-zh-v1.5 # 轻量且效果不错的中文模型 model_kwargs {device: cpu} # 使用CPU如果有GPU可改为 cuda encode_kwargs {normalize_embeddings: True} # 标准化向量有利于相似度计算 embeddings HuggingFaceEmbeddings( model_namemodel_name, model_kwargsmodel_kwargs, encode_kwargsencode_kwargs ) # 4. 创建并持久化向量存储 print(正在构建向量数据库...) vectordb Chroma.from_documents( documentssplits, embeddingembeddings, persist_directorypersist_dir ) vectordb.persist() print(f向量数据库已构建并保存至: {persist_dir}) return vectordb if __name__ __main__: # 指定上一步保存文本的目录 TEXT_DIR ./data/text DB_DIR ./chroma_db vectordb create_vector_store(TEXT_DIR, DB_DIR) # 可以测试一下检索 test_query Python如何读取文件 results vectordb.similarity_search(test_query, k2) print(f\n测试查询: {test_query}) for i, doc in enumerate(results): print(f\n结果 {i1}:) print(f内容片段: {doc.page_content[:200]}...) print(f来源: {doc.metadata.get(source, N/A)})这个脚本完成了从原始文本到向量数据库的构建。关键参数chunk_size和chunk_overlap需要根据你的视频内容特点调整。对于技术教程chunk_size500可能偏小可以尝试800-1000。4. 实现智能问答应用向量数据库建好后我们就可以构建一个简单的问答应用了。这里我们将使用LangChain的检索链并集成一个大语言模型来生成答案。4.1 配置大语言模型你可以选择多种大语言模型。这里以使用 OpenAI API 和智谱AI API 为例。方案一使用 OpenAI API (如 GPT-3.5/4)创建qa_app_openai.pyfrom langchain.chains import RetrievalQA from langchain_openai import ChatOpenAI from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma import os # 设置OpenAI API Key (请从OpenAI平台获取) os.environ[OPENAI_API_KEY] your-openai-api-key-here def create_qa_chain(persist_dir: str ./chroma_db): # 1. 加载相同的嵌入模型必须与构建时一致 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) # 2. 加载已存在的向量数据库 vectordb Chroma(persist_directorypersist_dir, embedding_functionembeddings) # 3. 初始化LLM llm ChatOpenAI(modelgpt-3.5-turbo, temperature0.1) # temperature控制创造性越低越确定 # 4. 创建检索问答链 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 将检索到的所有文档“塞”进上下文 retrievervectordb.as_retriever(search_kwargs{k: 4}), # 检索4个最相关片段 return_source_documentsTrue, # 返回源文档便于追溯 verboseFalse # 设为True可看到详细过程 ) return qa_chain def ask_question(qa_chain, question): result qa_chain.invoke({query: question}) answer result[result] sources result[source_documents] print(f\n问题: {question}) print(f\n答案: {answer}) print(f\n参考来源:) for i, doc in enumerate(sources): print(f [{i1}] {doc.metadata.get(source, 未知)}) # 打印来源文本片段 print(f 片段: {doc.page_content[:150]}...) return answer, sources if __name__ __main__: qa_chain create_qa_chain() # 交互式问答 print(B站收藏夹知识库问答系统已启动 (输入 quit 退出)) while True: user_input input(\n请输入你的问题: ) if user_input.lower() quit: break if user_input.strip(): ask_question(qa_chain, user_input)方案二使用智谱AI API (国产对中文优化)首先安装SDKpip install zhipuai然后创建qa_app_zhipu.pyfrom langchain.chains import RetrievalQA from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma from langchain_community.llms import ZhipuAI # LangChain社区集成 import os # 设置智谱AI API Key (请从智谱AI开放平台获取) os.environ[ZHIPUAI_API_KEY] your-zhipuai-api-key-here def create_qa_chain(persist_dir: str ./chroma_db): embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) vectordb Chroma(persist_directorypersist_dir, embedding_functionembeddings) # 使用智谱AI的模型 llm ZhipuAI(modelglm-4, temperature0.1) qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrievervectordb.as_retriever(search_kwargs{k: 4}), return_source_documentsTrue, verboseFalse ) return qa_chain # ... ask_question 函数与上面相同4.2 运行与测试运行你的问答脚本系统会加载向量数据库并等待提问。你可以尝试问一些收藏夹视频中可能涉及的问题例如“视频里讲了哪些关于Python装饰器的内容”或“如何配置Nginx反向代理”。系统会返回一个综合了检索信息的答案并列出答案所参考的原始文本片段及其来源文件这有助于你验证答案的可靠性。5. 常见问题排查与优化在实际搭建和运行过程中你可能会遇到以下问题。这里提供排查思路和解决方案。5.1 数据获取与处理阶段问题现象可能原因检查与解决方式B站API无法获取收藏夹返回“未登录”错误。Cookie失效或格式错误收藏夹ID不对。1. 重新从浏览器获取Cookie确保包含SESSDATA和bili_jct。2. 确认uid是否正确。3. 尝试在浏览器中打开收藏夹从URL中获取fid或mlid参数替换FAV_LIST_ID。yt-dlp下载失败提示“无法提取视频信息”或网络错误。视频已删除、下架或地区限制网络连接问题。1. 手动在浏览器中打开视频链接确认可访问。2. 尝试为yt-dlp设置代理如果需要。3. 在ydl_opts中添加cookiefile: cookies.txt将从浏览器导出的Cookie文件放入项目目录。Whisper转录速度极慢或内存不足。使用了较大的模型如large硬件资源不足。1. 初次尝试使用tiny或base模型。2. 考虑使用faster-whisper效率更高。3. 确保有足够的内存和交换空间。转录结果全是英文或乱码。未指定语言或音频质量差。在transcribe函数中明确指定languagezh。对于嘈杂音频可以尝试先使用whisper的fp16False参数更耗内存或预处理音频。5.2 向量化与检索阶段问题现象可能原因检查与解决方式构建向量数据库时嵌入模型下载失败。网络连接问题Hugging Face访问不稳定。1. 配置国内镜像源如设置环境变量HF_ENDPOINThttps://hf-mirror.com。2. 手动下载模型文件到本地然后指定model_name为本地路径。检索结果不相关答非所问。文本分割策略不佳嵌入模型不匹配检索数量k值不合适。1.调整chunk_size和chunk_overlap技术文档可适当增大chunk_size如800-1000。2.尝试不同嵌入模型中文可换用BAAI/bge-large-zh-v1.5或moka-ai/m3e-base。3.调整k值在as_retriever(search_kwargs{k: N})中尝试不同的N如3, 5, 7。4.优化查询尝试在用户问题后补充一些背景关键词。相似度搜索报错提示维度不匹配。构建和查询时使用的嵌入模型不一致。确保create_vector_store和create_qa_chain中使用的model_name完全相同。重建向量库可以解决此问题。5.3 问答生成阶段问题现象可能原因检查与解决方式调用LLM API超时或返回错误。API Key错误、余额不足、网络问题、模型服务异常。1. 检查API Key是否正确设置是否有访问权限。2. 查看对应平台的账单或额度页面。3. 尝试简单的API测试调用确认服务正常。4. 对于网络问题检查代理设置或重试。答案看起来是模型“编造”的未基于检索内容。检索到的上下文相关性低LLM的temperature参数过高提示词Prompt未做限制。1. 首先优化检索结果见上一条。2.降低temperature如设为0.1让模型输出更确定。3.定制提示词在RetrievalQA.from_chain_type中通过chain_type_kwargs参数传入自定义提示模板明确要求模型“仅根据提供的上下文回答”。答案包含无关信息或格式混乱。检索的上下文块过多或包含无关信息。1. 减少检索数量k。2. 在文本分割前对原始转录文本进行清洗去除过多的语气词、重复语句等。3. 使用更高级的检索方式如vectordb.as_retriever(search_typemmr)最大边际相关性在相关性和多样性间取得平衡。6. 生产环境部署与最佳实践将个人项目推向可长期使用的服务需要考虑更多因素。6.1 系统架构优化建议异步处理视频下载和转录是IO密集型任务使用asyncio或任务队列如CeleryRedis可以大幅提升处理效率避免阻塞。增量更新定期运行脚本更新知识库。可以记录已处理视频的BV号每次只处理新增收藏。在build_vector_store时使用Chroma的from_documents并指定相同的persist_directory它会自动增量添加。元数据过滤在存储文档时将视频标题、作者、发布时间等作为元数据metadata存入向量库。检索时可以进行过滤例如“只检索某个UP主的视频内容”。多路召回与重排序单一向量检索可能不够精准。可以结合关键词检索如BM25将结果融合后再进行重排序Rerank例如使用bge-reranker模型提升最终上下文的质量。6.2 配置与安全敏感信息管理永远不要将API Key、Cookie等硬编码在脚本中。使用环境变量或配置文件。# 在运行前设置环境变量 export BILIBILI_COOKIEyour_cookie export OPENAI_API_KEYyour_key或在项目根目录创建.env文件使用python-dotenv加载。模型本地化对于嵌入模型下载到本地服务器避免每次运行时从网络拉取。对于LLM如果数据敏感或追求低成本可以考虑使用Ollama本地部署开源模型如qwen:7b,llama2但需要足够的GPU资源。日志与监控为关键步骤下载、转录、入库、问答添加日志记录便于排查问题。可以监控向量库的大小、检索耗时、LLM调用开销等。6.3 扩展方向支持多数据源本项目框架很容易扩展。你可以编写新的Loader来处理本地PDF、Word、网页UnstructuredFileLoader,WebBaseLoader等统一接入向量库。构建Web界面使用Gradio或Streamlit快速构建一个Web界面让问答更直观。实现对话记忆将RetrievalQA链升级为ConversationalRetrievalChain让AI能够记住对话历史进行多轮问答。接入Agent将你的知识库作为一个工具Tool接入到LangChain Agent或AutoGen框架中让AI能够自主决定何时查询你的知识库来回答问题。通过以上步骤你不仅拥有了一个可用的B站收藏夹AI知识库更掌握了一套将任意非结构化数据转化为可查询知识的方法论。核心在于理解RAG的流程获取数据 - 提取文本 - 分割切片 - 向量化存储 - 检索增强 - 生成回答。每个环节都有多种工具和模型可选你可以根据自身需求、硬件条件和数据特点进行灵活调整和优化。