
我是从2019年开始用ES做知识库检索的。当时最常见的需求是企业客服文档、FAQ和技术手册的问答把资料灌进Elasticsearch用户提问走一遍BM25全文检索返回TopK内容。这套链路在当时的业务里确实够用但随着AI对话和大模型普及知识库检索技术开始面临一轮真正意义上的升级用户问“怎么改签机票”资料里写的是“航班变更操作指引”传统ES一个字都搜不出来用户问“里面的液体超过100毫升能不能带上飞机”资料里只出现了“液态物品限制规定”关键词完全错开。这个问题不是靠加同义词表能解决的它指向的是检索范式的变化——从关键词匹配走向语义理解而承载这个变化的核心组件就是从ES走向向量数据库。本文不打算泛泛介绍概念而是想把我从ES迁移到向量数据库、再把两者做成混合检索方案的完整思路讲清楚。适合正在做RAG、知识库问答或者企业搜索的同学参考尤其是那些已经有一堆ES历史包袱又想把语义检索能力补上的人。1. 知识库检索的痛点关键词匹配为什么搜不到“意思相近”的问题1.1 从一次典型失败说起当时客服系统接到一个真实提问“机票买错了想改签怎么办”知识库里对应的文档标题是《航班变更、退票与改期操作指引》。标准答案在文档里但ES检索返回的前十名里根本没有这篇文档。原因很朴素用户句子里的“改签”“买错”“怎么办”和文档里的“变更”“退票”“改期”没有任何一个词面重叠。BM25的前提是查询和文档必须共享词汇一旦不共享这个相关性就是0。很多人第一反应是维护同义词表把“改签”映射到“变更”“买错”映射到“退票”。我试过这在少数高频词上是有效的但长期维护成本高到你根本跟不上业务口语的更新速度。“改签”“退票”能维护“机票买错了想改签怎么办”这种长尾表达怎么维护用户换一个句式词表就得换一套。同义词本质上还是在字面空间里打补丁补丁越打越多检索效果却越来越不稳定。1.2 问题的本质是“表示”而非“匹配”那时候我发现真正的问题不在匹配算法而在文本表示方式。ES里一个文档被表示成一组词项词与词之间是独立的丢掉了上下文也丢掉了语义关系。“白菜”和“蔬菜”在ES眼里是两个完全不相干的词除非词典里写了它们是上下位关系。而用户在知识库问答场景里恰恰是用自然语言描述一个他其实不知道术语的诉求。知识库检索的核心任务从来不是“找出包含查询词的文档”而是“找出语义上回答这个问题的内容”。用户表达里的每个词都不必出现在答案原文里只要含义相近就应该被召回。这就是我说的“表示”问题把文本从词袋表示转换成语义向量表示。ES的倒排索引是字面上的匹配向量检索是在语义向量空间做近邻查找这是两条完全不同的技术路径。1.3 知识库检索的定位已经变了还有一个容易被忽视的变化在RAG架构里知识库检索的结果不是给用户直接展示而是喂给大模型当上下文。这种场景对召回率更敏感因为模型能不能答对取决于检索环节有没有把关键文档带到生成窗口里。ES时代追求的是“把最可能点开的那篇置顶”向量时代追求的是“别漏掉任何一篇能和问题对得上的文档”。顺序、连续、语句表达的完整度都比排序微调重要得多。想清楚这一点你就明白为什么不能只在ES上做文章。2. ES的看家本领与边界倒排索引和BM25到底擅长什么2.1 倒排索引到底是怎么工作的ES的底层是Lucene核心索引结构是倒排索引。你可以把它理解成一本“字书”书前面是词汇表每个词后面跟着一大串页码这些页码就是该词出现的文档ID列表。查询的时候用户输入“改签”程序直接把“改签”这个词对应的所有文档ID拉出来再做打分排序。整个查询过程不需要扫描全部文档它是词到文档的映射所以叫倒排。这套设计在全文搜索场景里是殿堂级的。电商搜商品名日志系统搜错误码法律文书搜案号这些都是字面精确匹配的典型场景ES能处理得又快又准还能配合分词器、过滤器、聚合分析做大屏统计分布式集群的稳定性和工具链成熟度也没话说。做搜索起家的人不会对ES的能力有异议。2.2 BM25打分它靠什么判断“相关”BM25是ES默认的相关性算法。它的核心思想很清晰一个词在文档中出现得越多文档越相关TF部分但增长是饱和的一个词在整个索引中出现得越少对文档相关性的贡献越大IDF部分文档越短、命中的词越重要长文档会做长度归一化。这套打分体系在词汇重叠度可以度量的场景里非常有用一个小搜索引擎只要用ES默认配置效果就能超过手写的TF-IDF。但BM25自始至终都在回答一个问题查询词和文档词的重叠程度有多高。它没有读取词的语义也不关心词在句子里的位置和上下文同义词、反义词、上下位概念它统统不感知。这就是ES在知识库语义检索场景的先天边界它擅长判断“说的一样”不擅长判断“意思一样”。2.3 ES在知识库场景的三个具体短板我把迁移过程中感受到的短板总结成三点都是实际踩出来的。其一是词面不匹配导致召回为0这是最致命的用户提问描述语义知识库文档描述概念两边词汇交集极小其二是长句处理不稳定ES分词会把“液体超过100毫升能不能带上飞机”切成若干碎片每个碎片分别匹配反而会召回一堆不相关的手续指南精确匹配在长口语表达前非常脆弱其三是相关度排序在语义场景下失真缺少词汇重叠的文档得分低即使它恰好是正确答案排在后面的结果会被RAG的TopK窗口直接过滤掉模型根本看不到。这三点不是改配置、换分词器能解决的。它们共同指向一个方向需要让文档“学会表达自己的意思”而不是只暴露词面。于是就有了向量表示和向量检索的出场机会。3. 向量数据库的核心机制把“意思接近”变成“距离很近”3.1 嵌入让文本变成空间中的点向量数据库的基本输入是向量而向量来自嵌入模型embedding模型。我们选一段文本把它喂给语言模型模型会输出一个固定维度的浮点数数组比如768维、1024维或1536维。这个数组就叫向量。模型的训练过程学到的是文本中的语义特征表达“航班变更”的句子生成的向量和表达“机票改签”的句子生成的向量在空间里的位置会比较靠近。用地图来类比最容易理解上游大模型做的是把地球上的一切内容标到地图上语义相近的文本标在相邻的坐标点语义无关的文本则相隔千山万水。向量数据库存储的就是这些坐标点它本身不产生语义但它负责管理这些坐标点并且回答“地图上离用户问的那个点最近的坐标点是谁”。3.2 相似度计算余弦、内积还是欧氏距离向量数据库需要计算向量之间的相似度。最常用的是余弦相似度计算两个向量夹角的余弦值范围在-1到1越大越相似。它只关注方向是否一致不关注向量的模长所以对于文本嵌入比较友好很多开源嵌入模型默认都用它。内积则是两向量各维度乘积之和如果不做归一化模长大的向量天然占便宜通常会先把向量归一化成单位向量再算。欧氏距离衡量的是空间里的直线距离通常用于低维特征、图像特征这类场景。实操上我建议优先跟随机型推荐的相似度度量。BGE系列模型官方推荐余弦相似度OpenAI的text-embedding-3系列的默认用法也是余弦。你选的向量数据库和嵌入模型必须统一这一项否则结果会偏。3.3 HNSW与IVF不用全表扫描也能找邻居如果数据量只有几千条全表暴力计算余弦也很快但真实知识库动辄几百万片段绝对不能暴力扫。这就是ANN近似最近邻索引要解决的问题。目前最主流的算法是HNSW可导航小世界网络。它的做法是构建一张多层图顶层节点稀疏连接全局底层节点稠密连接局部细节。查询时从顶层出发沿着邻居关系逐层下降每次跳转都能大幅缩小搜索范围最终在底层找到候选集。HNSW的直觉类似出行策略从城市到城市先坐大干线高铁到站后再转短途公交。干线少但够用公交多但只在底层细节里精找。HNSW的查询精度和召回率都很高代价是内存占用比较大。另一类常见算法是IVF先做K-means聚类把向量空间分成若干个桶查询时先定位最近的桶再在桶内扫描。它比HNSW省内存但召回率通常要低一些适合超大规模且硬件受限的场景。3.4 向量数据库不等于ANN索引库这里要特别说明Faiss这类库只提供ANN索引能力它不负责数据持久化、主键管理、元数据过滤和增删改查。而Milvus、Qdrant、Weaviate、Chroma、pgvector这些向量数据库是完整的数据系统支持标量字段、过滤条件、事务和索引管理。工程上真正需要的是一个能落数据的系统不是一堆索引API这也是把ES替换成向量数据库可以成立的根本原因——它不是引入了一个新算法而是引入了一个新的数据存储组件。4. 选型权衡ES自带向量检索 vs 独立向量数据库4.1 方案A在ES里加向量检索能力新版本ES已经支持dense_vector字段配合HNSW索引和kNN查询可以直接在现有集群里跑向量检索。这个方案的诱惑力在于不需要新运维一套系统继续用现有的集群、监控、权限体系数据也不用导出再同步。我见过不少团队一开始都是这么接的把embedding后的向量放到文档里查询时用kNN搜索返回TopK语义检索先跑通再说。但ES的向量检索能力毕竟不是核心场景大规模向量索引的性能、索引构建速度、混合查询的复杂度都不如专业向量数据库。更关键的是ES节点的内存配置和JVM参数是为倒排索引调的向量索引吃内存特别猛两个索引混在一起全文搜索和向量搜索会互相拖累。方案A适合数据量在百万级以内、已经有稳定ES集群、目的是快速验证RAG效果的场景。4.2 方案B部署独立向量数据库独立向量数据库的选项这两年非常多Milvus擅长超大规模分布式场景单集成度最高的中文社区也广泛使用Qdrant写得好、性能稳定、过滤功能完整API清爽中小团队上手极快pgvector最取巧直接在PostgreSQL里加向量类型和索引如果你的业务数据本来就在PG里这个方案最省事Chroma适合本地开发和Demo生产环境能力偏弱。把向量检索拆到独立数据库里最大的收益是职责清晰。全文搜索归ES管语义检索归向量库管各自按需扩容。业务量上来之后向量库的写性能和查询性能可以单独调优不再受ES的全局GC影响。代价是系统变多——ES写一份数据向量库还要同步一份双写一致性和运维成本都增加。独立向量库适合知识库规模较大、对并发和过滤要求高、已经有稳定数据管道的团队。4.3 我的三把选型尺子第一把尺子是数据量级。百万级以内ES加向量字段足够起步千万级以上直接考虑Milvus或Qdrant这类专门为高吞吐设计的系统。第二把尺子是已有设施的资产复用度。ES集群已经跑了好几年团队熟悉度极高那先不拆把dense_vector玩明白再决定如果知识库是新项目没必要拘泥于ES。第三把尺子是过滤复杂度。知识库经常需要按业务线、部门、权限、更新时间过滤向量数据库如果支持的过滤表达式太弱你会在这一步撞墙。把这三把尺子都过一遍选型不会太偏。5. 混合检索 重排落地RAG时最稳的召回架构5.1 为什么纯向量检索不够让我先泼一盆冷水纯向量检索并不能解决所有知识库问题。语义召回擅长处理口语描述但在精确标识符面前非常吃亏。用户搜“RTX 4060 参数”你希望的是精确命中产品型号用户搜“接口报错50099”你希望的是原样匹配错误码。这类内容在语义词空间里没有“近似”可言向量可能召回一堆“显卡”“GPU”“报错码”相关的片段反而稀释了精度。知识库里有大量专有名词、型号、编号、代码、法规条款这些内容的召回必须走字面匹配。正确的姿势不是站队而是把两种检索方式都接进来。5.2 混合召回与RRF融合我在生产环境用的方案是一路走ES的BM25做关键词召回一路走向量库做语义召回两路各取Top200然后做融合。融合方法推荐RRF倒数排名融合公式特别简单每个文档在两路排序里分别有排名计算它的分数为各路的1/(60rank)之和然后按总分重排。这么做的好处是无需把不同打分体系的分数做归一化因为不同检索模型的分数根本不具备可比性强行归一化只会引入新的偏差。举个例子某文档在BM25路排第3在向量路排第10那么它的RRF得分就是1/631/70约等于0.030。而另一篇文档在两边都排第30得分约0.022就排在它后面。RRF的优势是只需要排名不需要分数校准工程上非常简单效果却相当稳定。5.3 重排用交互式模型精排候选召回阶段要求快且全所以用了轻量级的双塔模型做海选精排阶段则切换到cross-encoder重排模型例如bge-reranker系列。双塔模型把问题和文档分别编码速度飞快但它无法对两个文本做细粒度的交互cross-encoder把问题和文档拼在一起输入模型输出一个0到1的相关性分数精度高很多但慢所以只对Top50以内的候选做精排。用重排模型把RRF融合后的结果再扫一遍最终只取TopK喂给大模型。这套“双路召回 RRF融合 cross-encoder精排”的架构是我在多个知识库项目里验证过的稳定形态。ES和向量库的关系不是替代而是互补精确的东西交给ES语义的东西交给向量库重排模型做最终裁决。6. 从ES迁到向量检索的实操要点分块、建模、双写与评测6.1 分块与重叠每个chunk都是检索单元向量化不能直接把整篇文档变成一个向量太长的文档语义会被稀释定位到具体答案全靠运气。所以要先分块每个chunk独立生成向量chunk就是未来被检索进上下文的单元。技术文档按标题、章节切块FAQ按一问一答切块合同按条款切块。长度控制上我看到大量团队采用200到500个token的粒度并且相邻chunk之间保留10%到20%的重叠防止一个完整的语义句子被切分边界拦腰斩断。我在做过两个知识库之后养成的习惯是切分完先把文本拼回去检查一遍重点看是否把“该问题由以下部门负责”这类承上启下的句子切成了两半。很多检索效果差不是模型不行而是源头chunk本身就是残缺的。6.2 Embedding模型选型embedding模型决定了向量空间的句法中文场景要用中文语料训练过的模型。BGE系列、E5系列还有text-embedding-3系列差别主要在中英文语言覆盖度和检索效果上。通用场景下我会先用开源模型跑基线比如BGE-large-zh-v1.5。如果业务术语极专业再考虑在私有语料上微调embedding模型。这里要提醒一下维度陷阱768维向量比1536维向量少一半存储但不代表效果差一半很多模型降维后检索能力依然很能打。先跑通流程再考虑是否为了成本降维不要一开始就为了参数好看选超大模型。6.3 HNSW索引参数与存储成本HNSW有两个参数最常调整M控制每个节点在底层图里的最大连接数M越大图越稠密召回率越高内存也越高常见取16到32之间efConstruction控制构建时的候选大小影响索引构建质量和构建速度常见取200左右。查询侧的efSearch也类似它控制检索时检查的候选节点数量调大能提升召回率但查询延迟会上来。向量存储成本相比倒排索引确实高一个量级。举个例子100万条文本、每条1024维float向量裸数据就约4GB加上HNSW的图结构开销真实内存占用会翻倍。评估预算时别只盯着向量表本身要把索引开销算进去。6.4 数据双写与灰度切流从ES迁到向量数据库最忌讳做“搬家式替换”。正确的做法是双写新数据继续进ES同时同步进向量库历史数据先全量灌入向量库期间不中断线上检索。双写跑稳之后把线上查询切到混合检索架构观察准召率和延迟等充分稳定再考虑是否把某些纯语义场景从ES侧摘掉。整个过程要保持灰度保证随时能切回旧链路。双写的一致性是容易被忽略的坑。ES和向量库不是同一个事务如果对一致性要求高建议走消息队列异步同步端到端延迟控制在秒级即可。知识库对实时性要求没有交易系统那么苛刻秒级延迟完全可以接受。6.5 用一个评测集守住质量底线最后一定要建一个属于你自己业务的评测集。拿真实用户问题记录几十到上百条每条标注期望命中的文档ID作为回归基准。每次改分块策略、换embedding模型、调整索引参数之后都跑一遍评测看两个核心指标召回率也就是正确文档有没有进TopK首位命中率也就是答案有没有排在第一。我自己的经验是很多模型和参数调整表面上在单个例子上效果变好但跑全量回归后才发现整体召回率在悄悄下滑没有评测集根本发现不了。流量大一点之后还可以把检索结果埋点记录下来定期抽样检查用户实际点击和追问情况把线上信号回灌进评测集。检索质量不是一次改造就能锁定的它是一个持续迭代的活评测集就是这个工程的地基。如果你也在走这条路我的建议是别急着把ES拆掉。把向量数据库当成一个真正能打配合的新队友而不是旧的对手。ES管精确、向量库管语义、重排模型做融合这套组合拳在大多数知识库场景里都能稳定落地。迁移过程中踩过的分块坑、参数坑、评测坑只要提前做了准备其实都还好。最关键的是别在没建好评测集之前就盲目调参那样你永远不知道自己是在改好还是在改坏。