Retrieval
elastic search
Elasticsearch 是当今最流行的 开源分布式搜索与分析引擎,用 Java 开发,基于 Apache Lucene 构建。它把全文检索、实时分析、时序数据、地理空间查询和向量检索统一到一个平台,被广泛用于日志、指标、安全、企业搜索以及 AI/RAG 场景。
RAG 系统中,Elasticsearch 不仅是向量数据库,更是语义检索引擎和上下文构建器,更准确地说,是向量数据库 + 全文检索引擎的混合角色。
查找语句
1 | GET /test_full_v1/_search |
可视化平台kibana的内网路径:
http://10.117.128.50:5601- http://10.117.128.50:5601/app/dev_tools#/console/shell 开发者工具
es支持的几种检索函数
向量搜索
这里的向量搜索也就是稠密向量检索,过程为“把文本/图像等非结构化数据映射成高维向量 → 在向量空间里做近似最近邻(ANN)搜索 → 按相似度排序返回结果”
1 | def vector_query(search_query: str): |
bm25
BM25(Best Matching 25)也就是全文关键词搜索,或者叫传统关键词搜索,他的核心思想为“词频越高、文档越短、词越稀有,则相关性越高”,在 TF-IDF 的基础上引入词频饱和、文档长度归一化两项修正。
TF-IDF(Term Frequency–Inverse Document Frequency,词频-逆文档频率)是一种经典的 文本特征权重计算方法,用于衡量 一个词对一篇文档的重要性。
项目中的bm25检索采取多字段匹配的方式,还有几个参数需要了解,如下
‘type’ :决定了如何把多个字段的 BM25 打分合并成最终得分,常用的参数有以下三种
type 中文含义 打分逻辑 best_fields(默认) 最佳字段优先 取 得分最高的那个字段 做最终分(可用 tie_breaker让次佳字段再贡献一点点)。most_fields 最多字段优先 把所有命中字段的得分 直接相加(类似 OR 逻辑),字段越多分越高。 cross_fields 跨字段合并 把多个字段视为 一个虚拟大字段,统一计算 TF 和 IDF,解决“关键词分散在不同字段”问题。 'tie_breaker':当多个字段都匹配时,“最佳字段”得分 + 其余字段得分×tie_breaker 作为最终得分。'operator':控制单个字段内的多个词项是“AND”还是“OR”关系。"AND"要求同一个字段必须同时包含所有查询词,减少噪音,提高精准度。适合地址、姓名等跨字段严格匹配。"OR"只要字段里出现任意一个词就匹配,召回量大,但可能引入不相关结果。
最后,每个字段还可以人工设置权重,如"fields": ["title^3", "content", "tags^2"]
1 | def bm25_query(search_query: str): |
混合检索
混合检索也就是 Reciprocal Rank Fusion(RRF),通过结合向量搜索和 BM25 搜索的结果综合判断。
但由于es中混合检索需要付费使用,后续检索效果评估时不做测试
1 | def hybrid_query(search_query: str): |
模糊检索
无论是在网页搜索、文件检索,还是数据库查询中,我们时常会因为拼写错误或信息不完整而无法找到需要的结果。模糊搜索(Fuzzy Search)应运而生,它通过识别与查询相似的词语来帮助我们获得更加灵活的搜索结果。
这里是将bm25与模糊搜索结合起来
1 | def fuzzy_query(search_query: str): |
AUTO规则:0~2 字符不允许错;3~5 字符最多1错;>5 字符最多2错。- 对 text 字段先分词,再对每个 token 做模糊 → 召回
Elasticsearch。
elasticsearch的连接
1 | from langchain_elasticsearch import ElasticsearchRetriever |
elasticsearch的入库
1 | document = [] |
相关资料
Elasticsearch检索器 | 🦜️🔗 LangChain 框架
主流的检索策略
BM25 全文关键词检索
BM25(Best Matching 25)是一种久经考验的排序算法,广泛应用于传统搜索引擎中。它基于“词袋模型”,核心思想是通过关键词匹配程度来衡量文档与查询的相关性。
核心原理概览:
词频 (Term Frequency, TF):一个词在文档中出现的次数越多,通常意味着这篇文档与该词相关性越高。但BM25会进行“饱和度”处理,避免某些超高频词过度影响结果。可以想象成,一篇文章提到“苹果”10次,比提到1次更相关,但提到100次和提到50次,在“苹果”这个主题上的相关性增加可能就没那么显著了。 逆文档频率 (Inverse Document Frequency, IDF):如果一个词在整个文档集合中都很罕见(只在少数文档中出现),那么它对于区分文档主题就更重要,IDF值就高。比如“量子纠缠”这个词远比“的”、“是”这类词更能锁定专业文档。 文档长度归一化:用于平衡长短文档的得分,避免长文档仅仅因为内容多而获得不公平的高分。 工作方式举例:当用户搜索“深度学习入门教程”时,BM25会倾向于找出那些更频繁出现“深度学习”、“入门”、“教程”这些词,且这些词相对不那么常见的文档。
1. 公式整体结构 $$ \text{score}(D, Q) = \sum_{i=1}^{n} \text{IDF}(q_i) \cdot \underbrace{\frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot \left(1 - b + b \cdot \frac{|D|}{\text{avgdl}}\right)}}_{\text{词频归一化项(TF)}} $$ - IDF 部分:衡量词项 $ q_i $ 的区分能力(逆文档频率)。 - TF 部分:衡量词项 $ q_i $ 在文档 $ D $ 中的匹配程度(词频归一化)。 - 求和:对查询中的所有词项 $ q_i $ 的得分求和,得到最终相关性分数。
2. IDF 部分 $$ \text{IDF}(q_i) = \ln\left(\frac{N - n(q_i) + 0.5}{n(q_i) + 0.5}\right) $$ - 意义:IDF 值越高,词项 $ q_i $ 越能区分文档(常见于少数文档中的词)。 - 平滑处理:分子和分母均加 0.5,避免极端值(如 $ n(q_i) = 0 $ 时 ID 无限大)。 - 参数: - $ N $:文档总数。 - $ n(q_i) $:包含 $ q_i $ 的文档数。
3. 词频归一化项(TF) $$ \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot \left(1 - b + b \cdot \frac{|D|}{\text{avgdl}}\right)} $$ - 非线性饱和:分子和分母均包含 $ f(q_i, D) $,使词频增长带来的增益逐渐减小(避免长文档中重复词项的过度影响)。 - 文档长度归一化: - $ |D| $:文档 $ D $ 的长度(词数)。 - $ $:整个文档集合的平均文档长度。 - $ b :控制文档长度对得分的影响( b=1 $ 时完全归一化,$ b=0 $ 时忽略长度)。 - 参数: - $ k_1 $:控制词频饱和的系数(通常 $ k_1 $,默认 $ k_1 = 1.5 $)。 - $ b $:默认 $ b = 0.75 $。
这个公式虽然看起来有些复杂,但它精妙地平衡了词频、词的稀有度以及文档长度这几个核心因素,是BM25算法效果出色的关键。
BM25、全文搜索与倒排索引:它们是如何协同工作的?
这三者是构建搜索系统的关键组件:
全文搜索 (Full-Text Search):这是我们希望达成的目标——在大量文本中找到包含特定信息的文档。
倒排索引 (Inverted Index):这是实现高效全文搜索的数据结构基础。它像一本书末尾的详细“关键词索引”,记录了每个词出现在哪些文档中以及相关位置信息。当用户查询时,系统可以通过倒排索引快速定位到包含查询词的候选文档。
BM25:在通过倒排索引找到候选文档后,BM25算法登场,为每个文档计算一个相关性得分,然后按分排序,将最相关的结果呈现给用户。
把它们比作在图书馆找特定主题的书籍:
- 你告诉图书管理员你要找关于“天体物理学”的书(用户查询)。
- 管理员查阅一个总卡片索引(倒排索引),迅速告诉你哪些书架(文档ID)上有包含“天体物理学”这个词的书。
- 你走到这些书架,快速翻阅这些书(BM25评分过程),根据目录、摘要和提及“天体物理学”的频繁程度及重要性,判断哪几本最符合你的需求,并把它们按相关性高低排好。
Dense Vector / kNN 向量语义检索
向量语义检索(Dense Vector / k-Nearest Neighbor,简称 kNN)是一种基于高维稠密向量表示的语义检索技术,与传统关键词倒排索引不同,它通过自然语言的上下文含义而非字面匹配来寻找最相关的文档或实体。
Embedding:使用预训练语言模型(如 BERT、Sentence-Transformers)把文本映射为固定维度的稠密向量(通常 128–1024 维)。
kNN 搜索:给定查询向量,在向量空间中找距离最近的 k 个文档向量。距离度量常见:
- 余弦相似度(cosine)
- 内积 / dot-product
- L2 欧氏距离
Hybrid Retrieval 混合检索
既然不同的检索策略各有千秋(例如,BM25擅长关键词精确匹配,Embedding擅长语义理解),那么将它们结合起来,是不是能达到“1+1>2”的效果呢?答案是肯定的,这就是混合检索的核心思想。
常见做法:同时使用BM25(或其他稀疏检索方法)和一种或多种Embedding检索方法,然后将它们各自的检索结果进行融合排序。
融合策略举例:
RRF (Reciprocal Rank Fusion, 倒数排序融合):一种简单但鲁棒的融合方法。它不关心不同检索系统输出的原始得分,只关心每个文档在各自结果列表中的排名。一个文档 doc 的RRF得分计算如下: ScoreRRF(doc) = Σs ∈ Systems(1/(k + ranks(doc))) 其中:
- Systems 是所有参与融合的检索系统的集合。
- rank_s(doc) 是文档 doc 在检索系统 s 给出的结果列表中的排名(例如,第一名是1,第二名是2)。
- k 是一个小常数(例如,常设置为60),用于平滑得分,避免排名靠后的文档得分过小或排名第一的文档得分占比过高。
Reranker 重排序器
经过上述一种或多种检索策略的“粗筛”(召回阶段),我们通常会得到一个包含较多候选文档的列表(比如几百个)。为了进一步提升最终喂给LLM的文档质量,Reranker(重排序器)应运而生。它相当于一位“精炼师”或“质量品鉴官”,对初步召回的结果进行更细致、更精准的二次排序。
- 召回 倒排 / 向量 / 混合先给 Top-N(N≈100~1 k)。
- 拼特征 把
(query, doc)拼成一条输入:[CLS] 用户问题 [SEP] 标题+正文 [SEP]。 - 打分 扔给 Cross-Encoder 或 ColBERT → 输出一个相关性分数。
- 重排 按分数从高到低重新排序,只留 Top-K(K≈10~20)。
- 返回 重排后的短列表交给前端或 LLM,完成一次“精读”。
参考
大模型RAG | 深入了解几种主流的检索策略(BM25、Embedding、混合检索、Reranker)-CSDN博客
项目关于elasticsearch代码阅读记录
阅读elasticsearch代码相关记录:
- embedding_model.select_embeddings_model:根据指定的模型名称加载
- make_es_vector_store:
- docs_url = pd.read_excel(‘docs_new.xlsx’),从excel加载url并进行数据处理,保存筛选后的ur
- 完成文件加载测试:xizhang/retrival/docfile_test/test.py;
- 初始化es,使用elastic_search.load_es_index加载存储索引
- 完成测试elasticsearch连接与索引构建(索引名zxj_test):/aisys/repo_dev/xizhang/retrival/elasticsearch_test/test_es_connect.py,/aisys/repo_dev/xizhang/retrival/elasticsearch_test/test_add_es.py
- 顺序批量处理文件:共16000多份,每十份为一批进行处理,使用download_pdf.py进行文件下载,使用,使用vector_base.rewrite_file对文件进行处理,这里可以修改代码,增加对mineru处理pdf的markdown文件的处理,返回Document对象列表;
- elastic_search
重写了检索策略的函数,包括BM25,KNN,混合搜索
elastic_retriever创建Elasticsearch检索器:根据搜索类型选择对应的查询函数,创建Elasticsearch检索器ElasticsearchRetriever.from_es_params
retrievers
- 定义函数select_retriever,根据指定的名称选择并返回相应的检索器,目前只有bm25