← 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)

三、工程化落地(不止"能搜")

  1. 多 collection 物理隔离:按业务分 role / worldview / foreshadow / chapter_summary / ±style,每项目独立目录,不混在一个库里靠 metadata 过滤(避免误召回 + 便于整体重建);
  2. 稳定 doc_id 增量更新:id 形如 worldview:{id},更新时先按同 id 删旧向量再 add,实现"增量覆盖"而非重复;
  3. 重建即清脏:全量重建时"建空占位 → 删占位 → 全量写 → 磁盘覆盖",确保清掉历史遗留的旧派生向量;
  4. 标脏 + 后台兜底: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,也是"单机进程"形态,不会拉一套分布式中间件。