数据截至 (上游 commit c412a093a820)
存储后端与 LLM 抽象(深入实现)
本章给要改后端 / 换模型 / 接私有部署的人。VideoRAG 把「存什么」和「用哪个模型」都做成了可替换抽象。
5.1 存储:三类抽象 + 默认实现
base.py 定义了三个抽象基类,VideoRAG 在 __post_init__(videorag.py:131)里按命名空间实例化它们:
| 抽象基类 | 干什么 | 默认实现 | 备选 |
|---|---|---|---|
BaseKVStorage(base.py:83) | 键值存储(段信息、文本块、LLM 缓存、视频路径) | JsonKVStorage(kv_json.py) | — |
BaseVectorStorage(base.py:68) | 向量库(实体、文字块、视频段特征) | NanoVectorDBStorage(vdb_nanovectordb.py:16) | vdb_hnswlib.py |
BaseGraphStorage(base.py:107) | 图存储(实体关系图) | NetworkXStorage(gdb_networkx.py:19) | gdb_neo4j.py |
这些类型在 VideoRAG dataclass 上是可替换字段(videorag.py:109-113),换后端只需传不同的类。
两种向量库实例的区别
注意有两个不同的向量库类:
NanoVectorDBStorage(vdb_nanovectordb.py:16)——文字向量库,embedding_func是文本嵌入模型,给实体库和 chunk 库用。查询带相似度阈值cosine_better_than_threshold=0.2(vdb_nanovectordb.py:17)。NanoVectorDBVideoSegmentStorage(vdb_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_config | OpenAI | _llm.py:157 |
azure_openai_config | Azure OpenAI | _llm.py:280 |
ollama_config | 本地 Ollama | _llm.py:381 |
deepseek_bge_config | DeepSeek + 硅基流动 bge-m3 | _llm.py:491 |
注意视觉模型不在这套抽象里: 字幕/精读用的 MiniCPM-V 是直接在 caption.py/videorag.py:124 里 from_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_cache(videorag.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),便于按会话管理/杀进程。 - 后端动态构造
LLMConfig(videorag_api.py:646)再VideoRAG(llm=...)(videorag_api.py:667),调insert_video(videorag_api.py:693)和query(mode="videorag")(videorag_api.py:808)。
本章未逐行通读 Vimo 的 TS 前端,只覆盖了它与算法层的调用面。