← Back to Lab
// AI2026.07.105 min read
本地 FAISS 还是托管向量库:小说 RAG 的向量库选型与边界
小说 RAG 向量库选型:为什么从 Chroma 迁回 FAISS,工程化落地要点,以及何时该换 Qdrant / pgvector 的边界。
本地 FAISS 还是托管向量库?一个小说 RAG 系统的向量库选型与边界
适用场景:准备给 RAG 系统选向量库,纠结"本地 FAISS 够不够,要不要上 Qdrant / pgvector / Pinecone"。
一、背景
做长篇小说写作助手的 RAG,知识库规模在万级以内(世界观 / 角色 / 伏笔 / 章节摘要 / 风格样本)。最初用的是 Chroma,后来迁回了 FAISS。下面讲为什么,以及边界在哪。
二、为什么从 Chroma 迁回 FAISS
Chroma 在某版本 Windows 环境下出现写入崩溃(进程异常退出),且依赖偏重、和单机轻量诉求不匹配。迁回 FAISS 的理由很直接:
faiss-cpu纯 CPU、零外部服务、磁盘文件即索引;- 用
IndexFlatIP:bge-m3 输出已 L2 归一化,内积即余弦相似度,无需额外归一化; - 索引文件可随项目目录一起备份、随容器一起打包,冷启动极快;
- 用
langchain_community.vectorstores.FAISS包装,省去手写序列化。
# 从文档构建(langchain 包装)
store = FAISS.from_documents(docs, embedding)
store.save_local(faiss_dir, name) # 磁盘文件:index.faiss + index.pkl
# 批量入库预计算向量,避免重复调 API
embs = await client.embeddings.batch_create(texts, batch_size=16)
store.add_embeddings(text_embeddings=embs, ids=ids)
三、工程化落地(不止"能搜")
- 多 collection 物理隔离:按业务分
role / worldview / foreshadow / chapter_summary / ±style,每项目独立目录,不混在一个库里靠 metadata 过滤(避免误召回 + 便于整体重建); - 稳定 doc_id 增量更新:id 形如
worldview:{id},更新时先按同 id 删旧向量再 add,实现"增量覆盖"而非重复; - 重建即清脏:全量重建时"建空占位 → 删占位 → 全量写 → 磁盘覆盖",确保清掉历史遗留的旧派生向量;
- 标脏 + 后台兜底:mutation 标
embedding_dirty,由后台任务(arq)定时消费;单条操作即时同步,批量操作靠定时任务兜底。
def _rebuild_faiss_collection(store, docs, ids):
dummy = Document(page_content="__init_placeholder__", metadata={})
s = FAISS.from_documents([dummy], embedding)
s.delete([s.index_to_docstore_id[0]]) # 删占位
if docs:
s.add_documents(docs, ids=ids) # 全量写(稳定 id)
s.save_local(faiss_dir, name) # 磁盘覆盖
四、边界:何时该换
| 场景 | 建议 |
|---|---|
| 数据量 万级以内、单机、读写不频繁 | FAISS,零运维、磁盘可备份、冷启动快 |
| 数据量 破万 / 高并发写 / 需过滤+持久化 | Qdrant(单机也能跑,支持 payload 过滤、持久化好) |
| 项目已经用了 Postgres,不想新增组件 | pgvector(SQL 内联合查询,运维统一) |
| 需要弹性扩容、不想管服务器 | Pinecone 等托管(代价:外部依赖 + 成本) |
判断标准只有三条:数据规模、运维成本、团队承受能力。小说知识库万级以内,FAISS 完全够;真破万再迁 Qdrant / pgvector,迁移成本也不高(向量维度不变的话,重灌即可)。
五、复盘
- 向量库选型不是追新,是看"规模 + 运维 + 团队"三件套;
- 单机轻量项目,少一个外部服务就少一个故障点;
- 把"重建清脏""稳定 id 增量""标脏+后台兜底"这三件事做扎实,比换库更重要——很多 RAG 的脏数据问题不在库,在同步策略。
另外,项目硬约束是不引入重型依赖(Kafka / Celery / ES 一律禁止),所以即便上 Qdrant,也是"单机进程"形态,不会拉一套分布式中间件。