当前位置: 首页 > news >正文

RAGFlow Agent 知识检索节点源码解析:从粗排到精排的完整流程

RAGFlow Agent 知识检索节点深度解析:从查询到重排序的完整流程

1. 总体架构概览

RAGFlow Agent 中的知识检索(Retrieval)节点是整个RAG系统的核心组件,负责从知识库中找到与用户查询最相关的文档片段。检索流程可以分为以下几个核心阶段:

用户查询 → 查询预处理 → 粗排阶段(ES混合检索) → 重排阶段(LLM重排序) → 结果输出
  • 查询预处理:清理用户输入、收集知识库ID、绑定 Embedding 和 ReRank 模型等
  • 粗排阶段(ES混合检索):结合文本检索(BM25)和向量检索(KNN)从ES中召回候选 Chunk
  • 重排阶段(LLM重排序):使用 LLM ReRank 模型对候选 Chunk 进行重排序
  • 结果输出:按输出格式整理数据,返回最终满足条件的 Chunk

2. 源码位置与调用链路详解

主要源码 对应于:

  • 检索节点入口 (agent/component/retrieval.py):其中的类 Retrieval 对应了 RAGFlow 前端页面 Agent 中的 知识检索节点
  • 核心检索逻辑 (rag/nlp/search.py):其中的类 Dealer 是类 Retrieval 的底层实现,实现了粗排、重排等逻辑。主要方法有:retrieval、search(粗排)、rerank_by_model(重排)
  • ES存储层 (rag/utils/es_conn.py):对应 ES 层的实现,封装了 ES 的操作方法
  • ReRank模型 (rag/llm/rerank_model.py):对应了 LLM Rerank 模型的实现,封装了各种模型的调用方法

完整的调用链路 如下:

Retrieval._run() [agent/component/retrieval.py] // 前端 Agent 的知识检索节点,调用下层服务
└── Dealer.retrieval() [rag/nlp/search.py]  // 检索核心逻辑:调用 粗排 + 重排├── Dealer.search() [rag/nlp/search.py]  // 实现粗排逻辑│   └── ESConnection.search() [rag/utils/es_conn.py]  // 调用 ES 执行 BM25 / KNN 查询│└── Dealer.rerank_by_model() [rag/nlp/search.py]  // 实现基于 LLM 的重排逻辑└── QWenRerank.similarity() [rag/llm/rerank_model.py]  // 调用 LLM 计算 Question/Chunk 语义相关性得分,此处以 QWen 为例

3. 粗排阶段(ES混合检索)

3.1 检索策略概述

RAGFlow采用混合检索策略,结合两种互补的检索方法:

  1. 文本检索:基于关键词匹配,擅长精确匹配和术语查找
  2. 向量检索:基于语义相似度,擅长理解查询意图和同义词匹配

3.2 ES 实际查询 DSL 语句

测试条件如下,以下数值都是通过 RAGFlow 前端界面设置与输入的:

  • 用户问题:RAGFlow和FastGPT是什么?
  • 相似度阈值:0.11
  • 关键字权重:0.16
  • Top-K:1024 (粗排结果 Chunk 限制数)
  • Top-N:14 (重排结果 Chunk 限制数)
  • Rerank模型:阿里的 get-rerank

通过在 ESConnection.search 函数调用 es.search(index=indexNames, body=q, …) 之前打印 q 变量,可以得到以下实际执行的 ES DSL 语句(DSL 是 Elasticsearch 的查询语言,类似于 MySQL 的 SQL 语句):

{"query": {"bool": {"must": [{"query_string": {"fields": ["title_tks^10","title_sm_tks^5","important_kwd^30","important_tks^20","question_tks^20","content_ltks^2","content_sm_ltks"],"type": "best_fields","query": "(ragflow^0.4312 ) (和^0.1377 ) (fastgpt^0.4312 ) \"ragflow 和\"^0.8623 \"和 fastgpt\"^0.8623","minimum_should_match": "0%","boost": 1}}],"filter": [{"terms": {"kb_id": ["cb7e95d8683311f080cb725e7685c9ee","03316fc06e7c11f0a10ec67c9f2d779f"]}},{"bool": {"must_not": [{"range": {"available_int": {"lt": 1}}}]}}],"boost": 0.050000000000000044}},"knn": {"field": "q_1024_vec","k": 1024,"num_candidates": 2048,"query_vector": [-0.07246076315641403,0.007768463809043169,...,-0.02680240198969841],"filter": {"bool": {"must": [{"query_string": {"fields": ["title_tks^10","title_sm_tks^5","important_kwd^30","important_tks^20","question_tks^20","content_ltks^2","content_sm_ltks"],"type": "best_fields","query": "(ragflow^0.4312 ) (和^0.1377 ) (fastgpt^0.4312 ) \"ragflow 和\"^0.8623 \"和 fastgpt\"^0.8623","minimum_should_match": "0%","boost": 1}}],"filter": [{"terms": {"kb_id": ["cb7e95d8683311f080cb725e7685c9ee","03316fc06e7c11f0a10ec67c9f2d779f"]}},{"bool": {"must_not": [{"range": {"available_int": {"lt": 1}}}]}}],"boost": 0.050000000000000044}},"similarity": 0.11},"from": 0,"size": 70
}

通过 DSL 语句可以看到,ES搜索结合了文本搜索与向量搜索,分别对应 “query” (文本搜索)与 “knn” (向量搜索)两部分:

  • "query"文本搜索部分:通过 fields 参数指定了被搜索的字段、字段权重,然后使用最匹配的字段的分数作为文本搜索分数 (“type”: “best_fields”)。
  • "knn"向量搜索部分:传入 User Question 向量 query_vector,指定需要的Chunk数 k 1024(对应RAGFlow前端中指定的 Top-K)、相似度阈值 similarity 0.11

ES会综合文本搜索、向量搜索两部分数得到最终粗排召回的 Chunk 。

在 "query"文本搜索的 fields 字段解读

  • "important_kwd^30"表示对 important_kwd 字段搜索,权重调整系数为 30,权重调整值越大表示该字段越重要。
  • 从 fields 中几个字段的权重可以看出 ES 文本搜索时几个字段重要程度为 important_kwd/important_tks (Chunk 绑定的关键词) > question_tks (Chunk 绑定的问题) > title_tks/title_sm_tks (Chunk 对应的文档标题) > content_ltks/content_sm_ltks (Chunk 文本内容)
  • 关于 Chunk 的这几个字段是如何生成的,可以参考我上一篇文章:《 Ragflow 文档处理深度解析:从解析到存储的完整流程 》

4. 重排阶段(基于 Rerank 模型)

4.1 重排序策略选择

RAGFlow 其实是支持两种重排序策略:

  1. 基于模型的重排序 (源码 Dealer.rerank_by_model):基于前端界面中为知识检索节点设定的 Rerank 模型实现重排。
  2. 非模型的重排序 (源码 Dealer.rerank):基于User Question/ES Chunk的文本特征、现有Embedding余弦相似度相似度、Chunk 排名等计算对Chunk进行重排序。

下文只介绍 基于模型的重排序。

4.2 LLM Rerank 重排模型概念介绍

Embedding 检索方法通过分别编码 Query 和 Chunk 得到向量,并用余弦相似度评估相关性。优点是可以提前计算Chunk的向量并存储,检索效率高、可大规模向量召回,适合在粗排阶段使用。但这种独立编码方式无法建模两者之间的语义交互。

而 Rerank 模型会将 Query 和 Chunk 作为一个成对的输入,同时送入模型进行处理。模型可以在编码过程中捕捉两者之间的深层语义关系和交互信息,更准确地评估相关性。这种方式虽然计算开销较大,但在排序质量上具有明显优势,常用于精排阶段。

调用阿里的 gte-rerank 重排模型的代码示例

import dashscope# 请替换为您自己的DashScope API密钥
API_KEY = "sk-d9a37e073d3e446ab359e445315d8394"# 查询问题
query = "如何学习Python编程语言"# 候选文档列表
documents = ["Java是一种面向对象的编程语言,广泛用于企业级应用开发。它具有跨平台性和强类型系统。","Python是一种简单易学的编程语言,语法清晰,适合初学者。它有丰富的第三方库,在数据科学、Web开发等领域应用广泛。","机器学习是人工智能的一个分支,通过算法让计算机从数据中学习模式。常用的框架包括TensorFlow和PyTorch。","Python学习建议:先掌握基础语法,然后通过实际项目练习。推荐从官方教程开始,多写代码多实践。","JavaScript是一种脚本语言,主要用于Web前端开发。现在也可以用Node.js进行后端开发。","数据结构和算法是编程的基础,包括数组、链表、树、图等。掌握这些对提高编程能力很重要。",
]# 调用重排序模型 gte-rerank API
response = dashscope.TextReRank.call(api_key=API_KEY,model="gte-rerank",query=query,documents=documents
)# 输出重排序结果
for rank, result in enumerate(response.output.results, 1):score = result.relevance_scoredoc_text = documents[result.index]print(f"文档排名: {rank}; 相关度: {score:.4f}; 文档内容: {doc_text}")

输出结果:

文档排名: 1; 相关度: 0.6545; 文档内容: Python学习建议:先掌握基础语法,然后通过实际项目练习。推荐从官方教程开始,多写代码多实践。
文档排名: 2; 相关度: 0.5206; 文档内容: Python是一种简单易学的编程语言,语法清晰,适合初学者。它有丰富的第三方库,在数据科学、Web开发等领域应用广泛。
文档排名: 3; 相关度: 0.2310; 文档内容: 数据结构和算法是编程的基础,包括数组、链表、树、图等。掌握这些对提高编程能力很重要。
文档排名: 4; 相关度: 0.0737; 文档内容: JavaScript是一种脚本语言,主要用于Web前端开发。现在也可以用Node.js进行后端开发。
文档排名: 5; 相关度: 0.0701; 文档内容: 机器学习是人工智能的一个分支,通过算法让计算机从数据中学习模式。常用的框架包括TensorFlow和PyTorch。
文档排名: 6; 相关度: 0.0527; 文档内容: Java是一种面向对象的编程语言,广泛用于企业级应用开发。它具有跨平台性和强类型系统。

4.3 RAGFlow LLM Rerank 重排序

RAGFlow 前端界面中如果在知识检索节点配置了 Rerank 模型,那么会调用 Dealer.rerank_by_model,执行逻辑大致如下:

  1. 计算 tksim:基于文本特征计算 Question/Chunk 的 tksim (Token Similarity,其实是知识检索节点前端配置页中指代的 关键词相似度)
  2. 计算 vtsim:基于 LLM Rerank 模型计算 Question/Chunk 的 vtsim (Vector Similarity,向量相似度)
  3. 计算最终 sim:使用 vtsim 与 tksim 加权计算最终的 Question/Chunk 的 sim (Similarity,最终相似度)。

源码如下:

# sres :包含 ES Chunk 的内容
# query :User Question;
# tkweight :前端配置的“关键字相似度权重”
# vtweight :是公式 “1 - tkweight” 计算的结果,表示 rerank 得分的权重
def rerank_by_model(self, rerank_mdl, sres, query, tkweight=0.3,vtweight=0.7, cfield="content_ltks",rank_feature: dict | None = None):# 1. 提取 Query 的关键词                    _, keywords = self.qryr.question(query) # 2. 基于 Chunk 的 token 构建 ins_tw for i in sres.ids:if isinstance(sres.field[i].get("important_kwd", []), str):sres.field[i]["important_kwd"] = [sres.field[i]["important_kwd"]]ins_tw = []for i in sres.ids:content_ltks = sres.field[i][cfield].split()           # Chunk 的内容tokentitle_tks = [t for t in sres.field[i].get("title_tks", "").split() if t]  # Chunk 的文档标题 tokenimportant_kwd = sres.field[i].get("important_kwd", []) # Chunk 的关键词tks = content_ltks + title_tks + important_kwd ins_tw.append(tks) # 组合各种 Token 拼接为 Ins_tw# 3. 计算 Query Keywords 与 Chunk token 间的 关键词相似度 tksim (基于词汇匹配和TF-IDF权重)tksim = self.qryr.token_similarity(keywords, ins_tw)# 4. 计算 向量相似度 vtsim(使用 Rerank Model)vtsim, _ = rerank_mdl.similarity(query, [rmSpace(" ".join(tks)) for tks in ins_tw])# 5. 获取 知识库配置 页中设置的 “页面排名” (Page Rank)参数数值rank_fea = self._rank_feature_scores(rank_feature, sres)# 6. 融合多种相似度得到最终排序分数,并返回三个项:sim, tsim, vsim#    最终分数 sim = tkweight * (tksim + “页面排名”) + vtweight * vtsimreturn tkweight * (np.array(tksim)+rank_fea) + vtweight * vtsim, tksim, vtsim

这个 Dealer.rerank_by_model 函数返回 “sim, tsim, vsim” 三个返回值后,Dealer.retrieval 会基于 sim 变量得到每个 Chunk 的排名(rank)

# 部分源码如下
def retrieval(...):sim, tsim, vsim = self.rerank_by_model(...) # 得到粗排召回的 question/chunk 的精排相似度 sim                  idx = np.argsort(sim * -1)[(page - 1) * page_size:page * page_size] # 基于 sim 从大到小重排 chunk ranks = {"chunks": [], ...} # 基于排序结果 idx 将每个重排后的 chunk 添加到最终召回结果 ranks 中for i in idx:ranks["chunks"].append(d) 
http://www.dtcms.com/a/309554.html

相关文章:

  • Java学习第九十六部分——Eureka
  • Elasticsearch IK 中文分词器指南:从安装、配置到自定义词典
  • IPAM如何帮助企业解决IP冲突、识别未经授权设备并管理子网混乱
  • MAC 升级 Ruby 到 3.2.0 或更高版本
  • ARM Cortex-M 处理器的应用
  • Smart Launcher:安卓设备上的智能启动器
  • ElasticSearch Linux 下安装及 Head 插件 | 详情
  • 设计Mock CUDA库的流程与实现
  • 【秋招笔试】07.27文远知行-第一题
  • Git 实现原理剖析
  • Boost.Asio学习(5):c++的协程
  • Python Flask框架Web应用开发完全教程
  • 后台管理系统权限管理:前端实现详解
  • 关于WIKI的一些使用技巧
  • windows系统安装文生图大模型Stable diffusion V3.5 large(完整详细可用教程)
  • 20250801在Ubuntu24.04.2LTS下编译firefly_itx_3588j的Android12时解决boot.img过大的问题
  • 李宏毅深度学习教程 第4-5章 CNN卷积神经网络+RNN循环神经网络
  • 基于SpringBoot+MyBatis+MySQL+VUE实现的经方药食两用服务平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)
  • 【科普】进程与线程的区别
  • 电商前端Nginx访问日志收集分析实战
  • 机器学习【三】SVM
  • 无人机避让路径规划模块运行方式
  • uniapp无线(WIFI)运行调试APP(真机)
  • C++继承中虚函数调用时机问题及解决方案
  • 无人机模式的切换
  • 服务端之nestJS常用异常类及封装自定义响应模块
  • 无人机上的 “气象侦察兵”:无人机用气象仪
  • 在线教程丨全球首个 MoE 视频生成模型!阿里 Wan2.2 开源,消费级显卡也能跑出电影级 AI 视频
  • linux中HADOOP_HOME和JAVA_HOME删除后依然指向旧目录
  • 从 0 到 1 认识 Spring MVC:核心思想与基本用法(下)