跳到主要内容

存储后端与 LLM 抽象(深入实现)

本章给要改后端 / 换模型 / 接私有部署的人。VideoRAG 把「存什么」和「用哪个模型」都做成了可替换抽象。

5.1 存储:三类抽象 + 默认实现

base.py 定义了三个抽象基类,VideoRAG__post_init__videorag.py:131)里按命名空间实例化它们:

抽象基类干什么默认实现备选
BaseKVStoragebase.py:83键值存储(段信息、文本块、LLM 缓存、视频路径)JsonKVStoragekv_json.py
BaseVectorStoragebase.py:68向量库(实体、文字块、视频段特征)NanoVectorDBStoragevdb_nanovectordb.py:16vdb_hnswlib.py
BaseGraphStoragebase.py:107图存储(实体关系图)NetworkXStoragegdb_networkx.py:19gdb_neo4j.py

这些类型在 VideoRAG dataclass 上是可替换字段videorag.py:109-113),换后端只需传不同的类。

两种向量库实例的区别

注意有两个不同的向量库类

  • NanoVectorDBStoragevdb_nanovectordb.py:16)——文字向量库,embedding_func 是文本嵌入模型,给实体库和 chunk 库用。查询带相似度阈值 cosine_better_than_threshold=0.2vdb_nanovectordb.py:17)。
  • NanoVectorDBVideoSegmentStoragevdb_nanovectordb.py:76)——视频段向量库,embedding_func=None嵌入过程内嵌在 upsert/query 里用 ImageBind 实现videorag.py:191 注释也点明了这点)。查询阈值设 -1(不过滤),保证总有候选。

5.2 LLM:三类模型 + 多 provider

LLMConfig_llm.py:44)把项目用到的模型抽象成三类:

角色用在哪默认(openai_config)
best_model_func(最强)实体抽取、片段过滤、最终作答gpt-4o
cheap_model_func(便宜)查询改写、关键词抽取、描述摘要gpt-4o-mini
embedding_func(嵌入)文字向量库text-embedding-3-small

仓库预置了多套 provider 配置,换 provider 只是换一个 LLMConfig 常量传给 VideoRAG(llm=...)

配置常量provider文件位置
openai_configOpenAI_llm.py:157
azure_openai_configAzure OpenAI_llm.py:280
ollama_config本地 Ollama_llm.py:381
deepseek_bge_configDeepSeek + 硅基流动 bge-m3_llm.py:491

注意视觉模型不在这套抽象里: 字幕/精读用的 MiniCPM-V 是直接在 caption.py/videorag.py:124from_pretrained('./MiniCPM-V-2_6-int4') 硬编码加载的;ImageBind 同理在 feature.py/vdb_nanovectordb.py 里直接用。换文本 LLM 容易,换视觉模型要改源码。

5.3 两个横切关注点:缓存 + 限流

响应缓存: 每个 provider 的 *_complete_if_cache(如 _llm.py:91)都内置 KV 缓存——把 (model, messages) 哈希成 key(compute_args_hash),命中就直接返回,不调 API(_llm.py:104)。缓存存在 llm_response_cachevideorag.py:151,可由 enable_llm_cache 关掉)。多选题模式重试时会显式 use_cache=False 强制重新生成(_op.py:921)。

并发限流: limit_async_func_call_utils.py:177)是个信号量装饰器,限制三类模型函数的最大并发数(best_model_max_async 等)。__post_init__ 里把模型函数包了一层(videorag.py:195),所以索引期那些 asyncio.gather 的大批 LLM 调用不会一次性打爆 API 限速。重试本身由 tenacity 的 @retry 处理 RateLimit/连接错误(_llm.py:86)。

5.4 Vimo 桌面应用怎么用它

仓库另一半 Vimo-desktop/ 是个 Electron 应用,Python 后端 videorag_api.py 把 VideoRAG 包成长驻服务:

  • 索引和查询各起独立进程,进程名打成 videorag-index-{chat_id} / videorag-query-{chat_id}videorag_api.py:591/718),便于按会话管理/杀进程。
  • 后端动态构造 LLMConfigvideorag_api.py:646)再 VideoRAG(llm=...)videorag_api.py:667),调 insert_videovideorag_api.py:693)和 query(mode="videorag")videorag_api.py:808)。

本章未逐行通读 Vimo 的 TS 前端,只覆盖了它与算法层的调用面。

5.5 巧妙之处(可借鉴)

  • 粗/精两级字幕分摊视觉成本caption.py:17 vs caption.py:56):所有段做廉价 5 帧粗描述,只对命中段做 15 帧定向精读——省 GPU 又保质量。
  • 段名即元数据split.py:45):把 start-end 编进文件名,全程不用额外数据库就能还原时间戳。
  • ImageBind 同空间对齐feature.py):文本和视频共享向量空间,使「用一句话搜画面」成立。
  • 过滤全删则全保留的兜底_op.py:694):避免过度过滤导致无答可生。
  • 子进程 error_queue 不吞异常videorag.py:268):并行管线里子进程崩溃会被收集、落盘、再抛出。

5.6 边界与局限

  • 重度依赖本地模型与 GPU: MiniCPM-V、ImageBind、faster-whisper 都需本地下载权重并跑在 CUDA 上(vdb_nanovectordb.py:94 直接 .cuda())——没 GPU 基本跑不动索引。
  • 视觉模型路径硬编码: ./MiniCPM-V-2_6-int4./faster-distil-whisper-large-v3 是写死的相对路径(caption.py:19asr.py:9)。
  • 图谱无增量社区/聚类: BaseGraphStorage 声明了 clustering/community_schemabase.py:141,继承自 nano-graphrag),但 VideoRAG 主线没用到——ainsert 里相关代码被注释(videorag.py:367)。这部分是来自上游的「惰性」机制。
  • 多选题重试无上限: _op.py:914 的 while 循环靠模型最终给合法 JSON 收敛,理论上可能久转。
  • wo_reference 字段未声明:02 章 3.2 步骤七——QueryParam 没声明该字段,需手动赋到实例上,否则 mode="videorag" 会在 _op.py:730 出错(inferred)。

5.7 横向对比(同 shelf)

VideoRAG 与纯文本 GraphRAG 家族同源(提示词直接来自 microsoft/graphrag,prompt.py:2),但把「文档」换成了「视频段」,并叠加了视觉通道——这是它区别于普通 RAG 的本质。相比只做字幕检索的方案,它多保留了画面语义(ImageBind + 视觉语言模型精读),代价是重 GPU 依赖。

代码地图

主题文件符号
存储抽象基类videorag/base.pyBaseKVStorage, BaseVectorStorage, BaseGraphStorage
JSON 键值存储videorag/_storage/kv_json.pyJsonKVStorage
文字向量库videorag/_storage/vdb_nanovectordb.pyNanoVectorDBStorage
视频段向量库videorag/_storage/vdb_nanovectordb.pyNanoVectorDBVideoSegmentStorage
NetworkX 图存储videorag/_storage/gdb_networkx.pyNetworkXStorage
Neo4j 图存储(备选)videorag/_storage/gdb_neo4j.pyNeo4jStorage
LLM 配置抽象videorag/_llm.pyLLMConfig
各 provider 配置videorag/_llm.pyopenai_config, deepseek_bge_config, ollama_config
缓存的补全videorag/_llm.pyopenai_complete_if_cache
并发限流装饰器videorag/_utils.pylimit_async_func_call
Vimo 后端集成Vimo-desktop/python_backend/videorag_api.py(进程 videorag-index-* / videorag-query-*