Yujun's Blog
选最适合业务的检索引擎
还在纠结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的代价就是吃内存。它需要维护那张庞大而复杂的图结构,并且这些结构都要塞进内存里才能保证速度。另外,构建这个索引的过程也比较慢。
适合场景:对召回率和低延迟都有极高要求的场景,比如电商搜索、智能客服、实时问答机器人。只要你内存给够,它就能给你飞一般的体验。
灵魂拷问:到底该怎么选?
说了这么多,三个选手各怀绝技,到底怎么选?老实说,没有绝对的“最好”,只有“最合适”。
别光盯着参数看,得先问问你自己的业务:
- 
要不要“懂你”?(语义 vs. 关键词) 如果你的用户喜欢问“怎么请假?”而你的文档里写的是“休假申请规范”,那你必须得上向量检索(FAISS或HNSW),BM25搞不定的。 但如果你的用户搜索的是精确的“错误码E10023”或者法律条文“第xx条”,那BM25反而更准、更可靠。
 - 
你的数据更新有多快? 你的知识库是每天更新一次,还是每秒都在变?如果是电商商品,随时有上下架,那支持实时更新的HNSW更有优势。如果你用FAISS的IVF,可能就得忍受一段时间的数据延迟,或者半夜偷偷重建索引。
 - 
你有多少“家底”?(内存/显存预算) 这是个很现实的问题。
- 穷(只有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。
适合自己的,才是最好的。希望这篇总结能帮你少走点弯路。