dify实现原理分析-rag-检索(Retrieval)服务的实现

概述

本文对dify的检索服务的检索过程的实现逻辑进行了分析。通过本文可以对检索服务的检索过程有一个比较清晰的理解,若是要关注实现细节,可以阅读对应部分的代码。

检索的分类

dify实现了三种类型的检索

  • 语义搜索
  • 全文搜索
  • 混合搜索

检索的类型定义如下:

class RetrievalMethod(Enum):SEMANTIC_SEARCH = "semantic_search"FULL_TEXT_SEARCH = "full_text_search"HYBRID_SEARCH = "hybrid_search"

在进行检索的过程中,会根据检索类型来执行不同的流程。

检索服务的总流程

检索服务的实际执行过程在RetrievalService.retrieve(…)函数中实现。该函数的主要实现逻辑如下:

  1. 若检索方法为关键词检索(retrieval_method == "keyword_search"),启动关键词检索线程,来根据查询语句来查询相关文档。结果保存到all_documents中。
  2. 若检索服务支持语义检索(is_support_semantic_search),则根据参数query查询嵌入向量,返回top_k个最相似的documents。结果保存到all_documents中。
  3. 若检索服务支持全文检索(is_support_fulltext_search),则根据参数query在对应向量数据库中进行全文检索。注意,有些向量数据库不支持全文检索。结果保存到all_documents中。
  4. 若检索方法是混合检索retrieval_method == RetrievalMethod.HYBRID_SEARCH.value,则启动数据后处理相关流程。结果保存到all_documents中。

关键词检索的实现

关键词检索功能是在函数:RetrievalService.keyword_search()中实现,该函数在 Flask应用程序的上下文中进行关键词搜索操作。该函数的实现逻辑如下:

  1. 在给定的应用程序上下文中,从数据库中获取指定ID的数据集。
  2. 使用该数据集创建一个关键词检索对象(Keyword),并执行关键词搜索。检索的时候,会根据关键词的存储类来构建检索的类。默认是:JIEBA。然后再调用Keyword对象的search函数来查询对应关键词的文档。
  3. 将找到的文档添加到传入的 all_documents 列表中。
  4. 如果发生异常,将错误信息添加到 exceptions 列表中。
关键词搜索search函数的执行逻辑

该函数从关键词表中查询关键词列表,并去掉停用词,然后对关键词所在的文档id进行计数和排序。具体的实现逻辑如下:

  1. 获取保存数据集关键词的表名,若该数据表不存在,则创建一个。数据表名为:dataset_keyword_tables。
  2. 获取top_k参数值,默认为4;
  3. 在数据集关键词表中,根据查询语句获取关键词列表,对每个关键词对应的文本ID进行计数,然后根据计数值对文本片段(chunk)ID进行从大到小排序。
  4. 从DocumentSegment表中查出对应的segment的详细内容,然后把segment的内容保存到Document对象中,返回Document列表。

语义检索(semantic_search)的实现

若检索方法支持语义检索RetrievalMethod.is_support_semantic_search(retrieval_method),则会进行启动语义检索线程。该线程调用RetrievalService.embedding_search函数,并把结果保存到all_documents结果列表中。

让我们来看一下RetrievalService.embedding_search函数的实现逻辑:

  1. 查询数据集id对应的数据集;
  2. 在向量数据库种搜素与查询文档相似的嵌入向量,查询过程可以参考《dify实现原理分析-rag-文本的嵌入向量的计算和存储第2步》的“嵌入向量的查询”章节的内容。这里不再赘述。

全文检索(full_text_search)的实现

若检索方法支持全文检索RetrievalMethod.is_support_fulltext_search(retrieval_method),则会进行启动全文检索线程。该线程调用RetrievalService.full_text_index_search函数,并把结果保存到all_documents结果列表中。

  1. 查询对应数据集id的数据集,然后创建一个向量数据库对象。vector = Vector(dataset)
  2. 调用vector.search_by_full_text函数通过全文检索,找到最相似的文档。不同向量数据库实现的全文检索的方式不同。
  3. 全文检索的过程是通过bm25算法来进行搜索的。有些向量数据库不支持bm25算法,也就不支持全文检索方式。
  4. 这里通过用PGVecotor举例说明全文检索的具体实现:

​ 在PGVector中执行全文检索,其实就是执行了一条SQL语句,该语句如下:

                f"""SELECT meta, text, ts_rank(to_tsvector(coalesce(text, '')), plainto_tsquery(%s)) AS scoreFROM {self.table_name}WHERE to_tsvector(text) @@ plainto_tsquery(%s)ORDER BY score DESCLIMIT {top_k}""",# f"'{query}'" is required in order to account for whitespace in query(f"'{query}'", f"'{query}'"),)

​ 其中query是给出的查询内容。

混合检索(hybrid_search)的实现

若retrieval_method的方法是混合检索,执行的过程如下:

        # 混合搜索if retrieval_method == RetrievalMethod.HYBRID_SEARCH.value:data_post_processor = DataPostProcessor(str(dataset.tenant_id), reranking_mode, reranking_model, weights, False)all_documents = data_post_processor.invoke(query=query, documents=all_documents, score_threshold=score_threshold, top_n=top_k)
DataPostProcessor的构建

DataPostProcessor构建时,就是获取RerankRunner对象和ReorderRunner对象。

这里的RerankRunner有2种:

  • 基于模型的重排模式:RerankModelRunner
  • 基于权重的重排模式:WeightRerankRunner
基于模型的重排模式(RerankModelRunner)的执行
  1. 对文档列表去重,针对dify的文档,记录doc_id;
  2. 调用rerank模型对文档进行重排,该方法返回一个包含重新排序后文档的信息(如文本和索引);
  3. 格式化重新排序后的文档:新建Document对象,并把重排序的内容保存到该对象中。包括:将重新排序后的分数添加到新文档的元数据中,把文档内容添加到
  4. 最后把重排序的结果保存到:rerank_documents结果列表中;

调用重排序模型对文档进行重排序的过程如下:

  1. 把需要重排序的文档和查询条件发送给重排序模型;
  2. 重排序模型会返回重排序完成后的结果列表;
  3. 对结果列表进行过滤,过滤掉所得分数不满足要求的文档(小于分数阈值的文档);
基于权重的重排模式(WeightRerankRunner)的执行
  1. 通过文档id对文档进行去重;
  2. 根据关键词匹配计算每个文档的分数;
  3. 针对每个文档:将基于关键词和向量的两个分数按照给定的权重综合,得到最终的评分。如果设置了 score_threshold 且文档的综合评分为负数,则跳过;
  4. 将所有符合要求的文档按分数从高到低排序;
  5. 根据 top_n 参数返回评分最高的前 N 个文档;如果不设置 top_n,则返回所有符合条件的文档。

总结

本文分析了dify的检索服务的实现逻辑。从检索服务的实现来看,总体的检索步骤大体分为:(1)从关键词数据库或向量数据库中获取满足查询条件的文档。(2)对查询到的多个文档进行rerank和去重,过滤,合并等操作。(3)然后对结果进行排序后得到最终结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9815.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器,是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用来与PC进行通信,包括与监控…

解锁微服务:五大进阶业务场景深度剖析

目录 医疗行业:智能诊疗的加速引擎 电商领域:数据依赖的破局之道 金融行业:运维可观测性的提升之路 物流行业:智慧物流的创新架构 综合业务:服务依赖的优化策略 医疗行业:智能诊疗的加速引擎 在医疗行业迈…

基于Flask的旅游系统的设计与实现

【Flask】基于Flask的旅游系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用Python作为后端开发语言,结合前端Bootstrap框架,为用户提供了丰富…

《HelloGitHub》第 106 期

兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、…

一文讲解Java中的BIO、NIO、AIO之间的区别

BIO、NIO、AIO是Java中常见的三种IO模型 BIO:采用阻塞式I/O模型,线程在执行I/O操作时被阻塞,无法处理其他任务,适用于连接数比较少的场景;NIO:采用非阻塞 I/O 模型,线程在等待 I/O 时可执行其…

Linux——网络(tcp)

文章目录 目录 文章目录 前言 一、TCP逻辑 1. 面向连接 三次握手(建立连接) 四次挥手(关闭连接) 2. 可靠性 3. 流量控制 4. 拥塞控制 5. 基于字节流 6. 全双工通信 7. 状态机 8. TCP头部结构 9. TCP的应用场景 二、编写tcp代码函数…

Flutter使用Flavor实现切换环境和多渠道打包

在Android开发中通常我们使用flavor进行多渠道打包,flutter开发中同样有这种方式,不过需要在原生中配置 具体方案其实flutter官网个了相关示例(https://docs.flutter.dev/deployment/flavors),我这里记录一下自己的操作 Android …

MySQL备忘录

MySQL 的一些基础知识记录,包括一些配置文件,cmd命令等 前言 这里使用的MySQL版本是8.0.25 MySQL安装,包括相关配置文件文本内容,相关cmd命令 通过安装包配置环境变量使用cmd管理员权限通过命令安装MySQL 8.0.25 一、安装配置 …

Prompt提示词完整案例:让chatGPT成为“书单推荐”的高手

大家好,我是老六哥,我正在共享使用AI提高工作效率的技巧。欢迎关注我,共同提高使用AI的技能,让AI成功你的个人助理。 许多人可能会跟老六哥一样,有过这样的体验:当我们遇到一个能力出众或对事物有独到见解的…

Maui学习笔记- SQLite简单使用案例02添加详情页

我们继续上一个案例,实现一个可以修改当前用户信息功能。 当用户点击某个信息时,跳转到信息详情页,然后可以点击编辑按钮导航到编辑页面。 创建项目 我们首先在ViewModels目录下创建UserDetailViewModel。 实现从详情信息页面导航到编辑页面…

Linux文件原生操作

Linux 中一切皆文件,那么 Linux 文件是什么? 在 Linux 中的文件 可以是:传统意义上的有序数据集合,即:文件系统中的物理文件 也可以是:设备,管道,内存。。。(Linux 管理的一切对象…

HttpClient学习

目录 一、概述 二、HttpClient依赖介绍 1.导入HttpClient4依赖 2.或者导入HttpClient5依赖 3.二者区别 三、HttpClient发送Get请求和Post请求测试 (一)通过HttpClient发送Get请求 (二)通过HttpClient发送Post请求 一、概述 HttpClient是 Apache 软件基金会提供的一…

【重生之我在学习C语言指针详解】

目录 ​编辑 --------------------------------------begin---------------------------------------- 引言 一、指针基础 1.1 内存地址 1.2 指针变量 1.3 指针声明 1.4 取地址运算符 & 1.5 解引用运算符 *** 二、指针运算 2.1 指针加减运算 2.2 指针关系运算 三…

< OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)

目标: 使用 日本阿里云的 VPM 传输文件。 暂时方案: 使用 主机JPN 下载 https://huggingface.co/ 上模型从 JPN 放到 度狗上在家里从狗度下载 为了减少编程,尽量使用现在软件 ,就找到 GitHub - qjfoidnh/BaiduPCS-Go: iikira…

98.1 AI量化开发:长文本AI金融智能体(Qwen-Long)对金融研报大批量处理与智能分析的实战应用

目录 0. 承前1. 简介1.1 通义千问(Qwen-Long)的长文本处理能力 2. 基础功能实现2.1 文件上传2.2 单文件分析2.3 多文件分析 3. 汇总代码&运行3.1 封装的工具函数3.2 主要功能特点3.3 使用示例3.4 首次运行3.5 运行结果展示 4. 注意事项4.1 文件要求4.2 错误处理机制4.3 最佳…

Linux环境基础开发工具的使用(apt, vim, gcc, g++, gbd, make/Makefile)

目录 什么是软件包 Linux 软件包管理器 apt 认识apt 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式下各指令汇总 vim底行模式个指令汇总 Linux编译器 - gcc/g gcc/g的作…

deepseek R1的确不错,特别是深度思考模式

deepseek R1的确不错,特别是深度思考模式,每次都能自我反省改进。比如我让 它写文案: 【赛博朋克版程序员新春密码——2025我们来破局】 亲爱的代码骑士们: 当CtrlS的肌肉记忆遇上抢票插件,当Spring Boot的…

SpringBoot源码解析(八):Bean工厂接口体系

SpringBoot源码系列文章 SpringBoot源码解析(一):SpringApplication构造方法 SpringBoot源码解析(二):引导上下文DefaultBootstrapContext SpringBoot源码解析(三):启动开始阶段 SpringBoot源码解析(四):解析应用参数args Sp…

基于Django的个人博客系统的设计与实现

【Django】基于Django的个人博客系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统采用Python作为主要开发语言,结合Django框架构建后端逻辑,并运用J…

Vue-day2

7.Vue的生命周期 mounted函数:在页面加载完毕时,发送异步请求,加载数据,渲染页面 createApp({date(){},methods:{},mounted:function(){console.log(Vue挂载完毕,发送请求获取数据)} }).mount(#{app}) 8.ajax函数库…