Yujun's Blog

选最适合业务的检索引擎

March 20, 2025 (7mo ago)AI

还在纠结FAISS、HNSW还是BM25?老司机教你怎么选最适合业务的检索引擎

兄弟们,最近做RAG(Retrieval-Augmented Generation,检索增强生成)应用做得有点上头。

大家应该都有同感,现在大模型的能力已经很强了,你喂给它什么,它就能基于什么回答。所以,RAG系统的成败,核心不在生成,而在于检索。说白了,挑战就在于:怎么在几毫秒内,从你那堆成千上万的文档里,精准地捞出最有用的那几段话(top-k),然后喂给大模型。

如果捞出来的是垃圾,那大模型也只能给你生成“精致的垃圾”。

在这“第一跳”的检索环节,我们面临的选择可太多了。每次开新项目,团队里总要争论一番:是用经典的BM25?还是上Facebook的FAISS?亦或是最近很火的HNSW?

今天,咱们不搞复杂的数学公式,就从一个后端工程师落地的角度,聊聊这三个主流选手的“脾气秉性”,以及到底该怎么选。

选手一:BM25 —— 关键字匹配的老前辈

BM25,这哥们儿其实不算严格意义上的“向量检索引擎”。它是传统搜索领域的老前辈了,你可以把它理解为TF-IDF的加强版。它的核心逻辑就是词频统计

它的脾气:

  • “实在”且省钱:这是它最大的优点。不需要你训练任何Embedding模型,不需要GPU,甚至对内存要求都很低。纯CPU跑起来,性能也杠杠的。冷启动极快,部署门槛几乎没有。
  • “讲道理”:可解释性特别强。如果你老板或者业务方问你:“为啥搜‘报销流程’,这条排第一?” 你可以直接指着结果里高亮的“报销”和“流程”这两个词说:“你看,这几个关键词全命中了,而且在这个文档里出现频率很高。” 简单明了。

它的硬伤:

它太“耿直”了。你说“番茄”,它就找“番茄”。你要是说“西红柿”,它可能就懵了。它不懂语义相似性,也不懂同义词,对拼写错误或模糊表达的容忍度很低。

适合场景:语料高度结构化、用词严谨的场景,比如政策条文、法律法规、FAQ知识库。在这些场景里,精准的关键词匹配往往比模糊的语义理解更重要。

选手二:FAISS —— Facebook的工业级重武器

FAISS(Facebook AI Similarity Search),这名字一听就知道是为大规模而生的。它不是一个单一的算法,而是一个高效向量相似度搜索库,里面集成了Flat、IVF、PQ、HNSW等多种索引结构。

它的脾气:

  • “能打”:它是工业界的首选之一,支持百亿级别的向量数据。而且,它完美支持GPU加速。在超大规模数据集上,GPU加持的FAISS性能非常残暴。
  • 灵活多变:因为它是个工具库,你可以根据需要选择不同的索引。想要绝对精确?用IndexFlatL2(暴力搜索)。想要平衡速度和内存?用IVF+PQ(倒排+乘积量化)。它让你在召回率、延迟和内存占用之间灵活走位。

它的硬伤:

成也萧何,败也萧何。如果你用的是它最经典的IVF索引,有个坑得注意:数据动态更新很麻烦

IVF的原理是先把向量空间聚类划分成多个“格子”(簇)。检索时只去最相关的几个格子里找。但是,如果你新增了一大批数据,原有的聚类中心可能就不准了,你得重新训练(re-train)整个索引。这就做不到分分钟的实时更新了。

适合场景大规模的语义检索任务,比如新闻推荐、图像检索、内容去重。前提是,你的公司得有一定的算力预算(买得起GPU),且对数据实时更新的要求没那么变态。

选手三:HNSW —— 为了速度不要“命”的图索引

HNSW(Hierarchical Navigable Small World),这玩意儿是基于图结构的近邻搜索算法。简单来说,它在你的数据点之间建立了一个多层的高速公路网。

它的脾气:

  • “快”和“准”:这是它的核心卖点。它的召回率非常接近暴力搜索,但查询速度却是对数级(O(log n))的,延迟极低。
  • 支持实时更新:相比FAISS的IVF,HNSW对增删改的支持要好得多,可以做到秒级的标记删除。

它的硬伤:

天下没有免费的午餐。HNSW的代价就是吃内存。它需要维护那张庞大而复杂的图结构,并且这些结构都要塞进内存里才能保证速度。另外,构建这个索引的过程也比较慢。

适合场景:对召回率低延迟都有极高要求的场景,比如电商搜索、智能客服、实时问答机器人。只要你内存给够,它就能给你飞一般的体验。

灵魂拷问:到底该怎么选?

说了这么多,三个选手各怀绝技,到底怎么选?老实说,没有绝对的“最好”,只有“最合适”。

别光盯着参数看,得先问问你自己的业务:

  1. 要不要“懂你”?(语义 vs. 关键词) 如果你的用户喜欢问“怎么请假?”而你的文档里写的是“休假申请规范”,那你必须得上向量检索(FAISS或HNSW),BM25搞不定的。 但如果你的用户搜索的是精确的“错误码E10023”或者法律条文“第xx条”,那BM25反而更准、更可靠。

  2. 你的数据更新有多快? 你的知识库是每天更新一次,还是每秒都在变?如果是电商商品,随时有上下架,那支持实时更新的HNSW更有优势。如果你用FAISS的IVF,可能就得忍受一段时间的数据延迟,或者半夜偷偷重建索引。

  3. 你有多少“家底”?(内存/显存预算) 这是个很现实的问题。

    • (只有CPU,内存也紧张):老老实实BM25,或者小规模的FAISS Flat。
    • 小康(内存充足):果断上HNSW,享受飞一般的速度。
    • 土豪(有GPU集群,数据量百亿级):FAISS是你的不二之选。

光说不练假把式:上手试试

现在工具链这么发达,比如用LlamaIndex,把这几个引擎跑起来也就是几行代码的事儿。我们可以快速构建原型,用自己的数据跑一跑。

比如,我们想快速试一下BM25的效果:

# %pip install llama-index
# %pip install llama-index-retrievers-bm25

from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core import Document

# 假设你有几个简单的文档
documents = [
    Document(text="Java工程师的请假流程是先提OA,再找Leader审批。"),
    Document(text="Python开发的环境搭建需要用到conda或者pipenv。"),
    Document(text="公司对于迟到超过30分钟的情况将扣除全勤奖。"),
]

# 核心代码就这一行,初始化 BM25 Retriever
# nodes就是你的文档块,similarity_top_k 就是你要召回前几条
retriever = BM25Retriever.from_defaults(nodes=documents, similarity_top_k=2)

# 搜索试试
results = retriever.retrieve("怎么请假?")

for node in results:
     print(f"找到结果: {node.text}, 得分: {node.score}")

你会发现,即使“请假”两个字没有完全匹配(虽然例子里匹配了),BM25也能根据相关度找出最匹配的结果。

同样地,如果你想换成FAISS或HNSW,LlamaIndex也提供了现成的VectorStore集成,切换成本非常低。

写在最后

选型的本质,就是在召回率、延迟、资源占用(内存/显存)和更新频率这四个鸡蛋上跳舞。

我个人的建议是:不要过早优化。项目刚开始,数据量不大时,怎么简单怎么来。甚至可以先用BM25或者最简单的内存向量搜索做个基线(Baseline)。

等业务跑起来了,发现召回率不够,再上Embedding和向量库;发现延迟高了,再考虑换HNSW或者上GPU。

适合自己的,才是最好的。希望这篇总结能帮你少走点弯路。

Comments