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

dify实现原理分析-rag-数据检索的实现

数据检索的总体执行步骤

数据检索总体步骤如下:

输入验证
模型初始化
策略选择: 1.单线程检索 2.多线程检索
数据集筛选: 选择符合条件的数据集
执行检索: 使用单线程或多线程检索来查询数据
结果处理: 结果选择和格式化处理
返回格式化内容: 最终返回一个结果字符串

数据检索是在DatasetRetrieval.retrieve函数中实现的,主要实现逻辑分为以下几步:

  1. 检查输入的模型、数据集id列表等是否为空;

  2. 获取模型实例,并把它转换成LargeLanguageModel对象;

  3. 获取模型实例,并获取模型的元数据,主要是模型的各种参数,以及认证参数等。若模型的元数据为空,直接返回None。

  4. 默认情况下,规划路由策略被设置为 REACT_ROUTER。如果模型支持工具调用(TOOL_CALL)或多重工具调用(MULTI_TOOL_CALL),则将规划策略更改为 ROUTER;

  5. 筛选可用的数据集:若数据集为空,或数据集不可用则过滤掉数据集;后续的数据检索,会从这些可用数据集中来进行检索。

  6. 根据配置选择单线程(RetrieveStrategy.SINGLE)或多线程检索(RetrieveStrategy.MULTIPLE),获取检索到的document列表;这里只是设置检索的参数,而这两种检索方式都会调用检索服务的RetrievalService.retrieve(…)函数来检索符合条件的数据集。

  7. 处理dify提供者的document:使用回调函数返回检索结果,根据分数对检索结果进行排序,并返回格式化后的字符串。

    1)获取每个文档的分数(score)的值

    2)查询状态为completed,且可用,doc_id在检索出来的文档列表中的DocumentSegment列表

    3)若segment(文档块)不为空。

    ​ 3.1) 获取segment的id和位置(确定文档内容的读取位置)

    ​ 3.2) 按id所在的position(位置)排序,若id不在字典中排到最后(无穷大inf)

    ​ 3.3) 遍历排好序的segment:根据条件构建新的列表,然后进行一下操作:

    ​ a) 检查每个segment是否包含answer

    ​ b) 包含:构建一个包含问题和答案的字符串

    ​ c) 不包含:则只构建一个问题的字符串

    ​ 3.4) 对已排好序的segment进行遍历

    ​ 3.5) 获取segment对应的dataset_id对应的dataset

    ​ 3.6) 获取segment.document_id对应的document

  8. 使用回调函数返回检索结果,根据分数对检索结果进行排序,并返回格式化后的字符串。

检索的详细执行流程

数据检索的函数声明如下:

class DatasetRetrieval:def __init__(self, application_generate_entity=None):self.application_generate_entity = application_generate_entitydef retrieve(self,app_id: str,user_id: str,tenant_id: str,model_config: ModelConfigWithCredentialsEntity,config: DatasetEntity,query: str,invoke_from: InvokeFrom,show_retrieve_source: bool,hit_callback: DatasetIndexToolCallbackHandler,message_id: str,memory: Optional[TokenBufferMemory] = None,) -> Optional[str]:"""Retrieve dataset.:param app_id: app_id:param user_id: user_id:param tenant_id: tenant id:param model_config: model config:param config: dataset config:param query: query:param invoke_from: invoke from:param show_retrieve_source: show retrieve source:param hit_callback: hit callback:param message_id: message id:param memory: memory:return:"""

该函数的详细实现逻辑如下:

  1. 检查数据集id列表,若数据集id列表为空,则直接返回None;
        # 检查输入的模型、数据集等是否有效。dataset_ids = config.dataset_idsif len(dataset_ids) == 0:return None
  1. 根据模型配置来获取和构建模型实例对象,并获取模型的元数据(各种参数)
        model_type_instance = model_config.provider_model_bundle.model_type_instancemodel_type_instance = cast(LargeLanguageModel, model_type_instance)# 获取模型实例model_manager = ModelManager()model_instance = model_manager.get_model_instance(tenant_id=tenant_id, model_type=ModelType.LLM, provider=model_config.provider, model=model_config.model)
  1. 设置计划策略,默认情况下计划策略是:PlanningStrategy.REACT_ROUTER,若模型特征支持工具调用:ModelFeature.TOOL_CALL或MULTI_TOOL_CALL,则把计划策略设置成:ROUTER,即:planning_strategy = PlanningStrategy.ROUTER
        # 默认情况下,规划策略被设置为 REACT_ROUTER。planning_strategy = PlanningStrategy.REACT_ROUTER# 检查模型的特性(features)。# 如果模型支持工具调用(TOOL_CALL)或多重工具调用(MULTI_TOOL_CALL),则将规划策略更改为 ROUTER。features = model_schema.features# 检查模型是否支持工具调用,若支持计划策略设置为ROUTERif features:if ModelFeature.TOOL_CALL in features or ModelFeature.MULTI_TOOL_CALL in features:planning_strategy = PlanningStrategy.ROUTERavailable_datasets = []
  1. 筛选可用数据集:遍历参数中的dataset_ids列表,从数据库中查询对应id的数据集,过滤掉数据集可用文档为0的和数据集的provide为external的数据集。
        # 筛选可用的数据集。for dataset_id in dataset_ids:# 查询对应id列表的数据集dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()# 数据集为空,pass掉if not dataset:continue# 数据集不可用,pass掉if dataset and dataset.available_document_count == 0 and dataset.provider != "external":continue# 把数据集添加到可用数据集列表中available_datasets.append(dataset)
  1. 根据配置选择单线程(single_retrieve)或多线程(multiple_retrieve)检索来检索document,得到结果document列表:all_documents。
        if retrieve_config.retrieve_strategy == DatasetRetrieveConfigEntity.RetrieveStrategy.SINGLE:all_documents = self.single_retrieve(...)elif retrieve_config.retrieve_strategy == DatasetRetrieveConfigEntity.RetrieveStrategy.MULTIPLE:all_documents = self.multiple_retrieve(...)
  1. 从all_documents中抽取出:dify_documents(provider == “dify”)和external_documents(provider == “external”)的结果。处理外部和Dify提供者的document,生成相应的上下文和资源信息。
        # 得到不同提供者的documentdify_documents = [item for item in all_documents if item.provider == "dify"]external_documents = [item for item in all_documents if item.provider == "external"]
  1. 处理结果队列dify_documents,步骤如下:

(1)收集评分信息(document_score_list):通过检查 dify_documents 列表中每个文档片段的 score 元数据,构建一个字典 document_score_list,其中键是文档 ID,值是对应的评分。

            # 获取每个文档的分数(score)的值for item in dify_documents:if item.metadata.get("score"):document_score_list[item.metadata["doc_id"]] = item.metadata["score"]

(2)过滤和排序文档片段:根据给定的 dataset_ids 和一些状态条件(如 status=completed, enabled=True),从数据库中查询相关的文档片段(DocumentSegment)。然后将这些文档片段按其在原始列表中的索引顺序进行排序。

(3)构建文档上下文(document_context_list):对于每个排序后的文档片段,创建一个 DocumentContext 实例,并将其添加到 document_context_list 中。如果文档片段包含答案(answer),则将答案与问题一起作为一个字符串存储在内容字段中;否则,只存储问题。

             # 获取segment的id和位置(确定文档内容的读取位置)index_node_id_to_position = {id: position for position, id in enumerate(index_node_ids)}# 按id所在的position(位置)排序,若id不在字典中排到最后(无穷大inf)sorted_segments = sorted(segments, key=lambda segment: index_node_id_to_position.get(segment.index_node_id, float("inf")))# 遍历排好序的segment:根据条件构建新的列表for segment in sorted_segments:# 检查每个segment是否包含answerif segment.answer: # 包含:构建一个包含问题和答案的字符串document_context_list.append(DocumentContext(content=f"question:{segment.get_sign_content()} answer:{segment.answer}",score=document_score_list.get(segment.index_node_id, None),))else: # 不包含:则只构建一个问题的字符串document_context_list.append(DocumentContext(content=segment.get_sign_content(),score=document_score_list.get(segment.index_node_id, None),))

(4)构建检索资源(retrieval_resource_list):如果设置了 show_retrieve_source 标志为真,对于每个排序后的文档片段,查询相关的数据集(dataset)和文档(document)信息。创建一个 source 字典,其中包含数据集、文档的详细信息以及文档片段的相关属性(如评分、命中次数、词数等)。将包含详细信息的 source 字典添加到 retrieval_resource_list 中。

 	          if show_retrieve_source: # 设置了展示检索源的标识			for segment in sorted_segments: # 遍历排序segment# 获取segment对应的dataset_id对应的datasetdataset = Dataset.query.filter_by(id=segment.dataset_id).first()# 获取segment.document_id对应的documentdocument = DatasetDocument.query.filter(DatasetDocument.id == segment.document_id,...).first()# 若2者同时存在if dataset and document:# 构建source字典,包含各种信息source = {"dataset_id": dataset.id,"dataset_name": dataset.name,"document_id": document.id,"document_name": document.name,"data_source_type": document.data_source_type,"segment_id": segment.id,"retriever_from": invoke_from.to_source(),"score": document_score_list.get(segment.index_node_id, 0.0),}...# 若segment的回答不为空,则获取:question与answerif segment.answer:source["content"] = f"question:{segment.content} \nanswer:{segment.answer}"else: # 仅获取questionsource["content"] = segment.content# 将源字典添加到retrieval源列表中retrieval_resource_list.append(source)
  1. 使用回调函数返回检索结果,根据分数对检索结果进行排序,并返回格式化后的字符串。
        # 使用回调函数返回检索结果,根据分数对检索结果进行排序,并返回格式化后的字符串。                   if hit_callback and retrieval_resource_list:# 根据segment所在doc_id的分数进行排序retrieval_resource_list = sorted(retrieval_resource_list, key=lambda x: x.get("score") or 0.0, reverse=True)# 获取检索列表中的位置参数for position, item in enumerate(retrieval_resource_list, start=1):item["position"] = positionhit_callback.return_retriever_resource_info(retrieval_resource_list)
  1. 按分数进行排序,并把文档内容合并在一个字符串中返回
if document_context_list:# 按分数进行排序,并把文档内容合并在一个字符串中返回document_context_list = sorted(document_context_list, key=lambda x: x.score or 0.0, reverse=True)return str("\n".join([document_context.content for document_context in document_context_list]))

总结

总结一下数据检索的主要步骤:(1)参数验证和模型选择;(2)检索策略选择:单线程或多线程检索(3)结果筛选和处理(4)结果合并和格式化处理,然后返回。

不管是单线程检索还是多线程检索,都会调用检索服务的retrieve函数来实现检索功能,检索服务的检索具体实现会在后面的文章中进行分析。

相关文章:

dify实现原理分析-rag-数据检索的实现

数据检索的总体执行步骤 数据检索总体步骤如下: #mermaid-svg-YCRNdSE7T1d0Etyj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-YCRNdSE7T1d0Etyj .error-icon{fill:#552222;}#mermaid-svg-YCRNdSE7T1d…...

Day30-【AI思考】-错题分类进阶体系——12维错误定位模型

文章目录 错题分类进阶体系——12维错误定位模型**一、认知层错误(根源性缺陷)****二、操作层错误(执行过程偏差)****三、心理层错误(元认知障碍)****四、进阶错误(专业级陷阱)** 错…...

全国31省空间权重矩阵(地理相邻空间、公路铁路地理距离空间、经济空间)权重矩阵数据-社科数据

中国31个省份空间权重矩阵-社科数据https://download.csdn.net/download/paofuluolijiang/90028597 https://download.csdn.net/download/paofuluolijiang/90028597 空间权重矩阵是反映个体在空间中依赖关系的矩阵,本数据计算全国31个省三种标准化处理的空间权重矩…...

Docker容器数据恢复

Docker容器数据恢复 1 创建mongo数据库时未挂载数据到宿主机2 查找数据卷位置3 将容器在宿主机上的数据复制到指定目录下4 修改docker-compose并挂载数据(注意端口)5 重新运行新容器 以mongodb8.0.3为例。 1 创建mongo数据库时未挂载数据到宿主机 versi…...

Visual Studio使用GitHub Copilot提高.NET开发工作效率

GitHub Copilot介绍 GitHub Copilot 是一款 AI 编码助手,可帮助你更快、更省力地编写代码,从而将更多精力集中在问题解决和协作上。 GitHub Copilot Free包含哪些功能? 每月 2000 代码补全,帮助开发者快速完成代码编写。 每月 …...

【matlab】绘图 离散数据--->连续函数

matlab绘图练习 离散数据及离散函数对离散区间进行细划分 达到连续效果画plot(y)图 与 复数的应用 离散数据及离散函数 例1 x1[1 2 4 6 7 8 10 11 12 14 16 17 18 20] y1[1 2 4 6 7 8 10 10 8 7 6 4 2 1] figure(1); plot(x1,y1,o,MarkerSize,15); x21:20; y2log(x2); figure…...

Python大数据可视化:基于python的电影天堂数据可视化_django+hive

开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 电影数据 看板展示 我的信息 摘要 电影天堂数据可视化是…...

几种K8s运维管理平台对比说明

目录 深入体验**结论**对比分析表格**1. 功能对比****2. 用户界面****3. 多租户支持****4. DevOps支持** 细对比分析1. **Kuboard**2. **xkube**3. **KubeSphere**4. **Dashboard****对比总结** 深入体验 KuboardxkubeKubeSphereDashboard 结论 如果您需要一个功能全面且适合…...

YOLO11/ultralytics:环境搭建

前言 人工智能物体识别行业应该已经饱和了吧?或许现在并不是一个好的入行时候。 最近看到了各种各样相关的扩展应用,为了理解它,我不得不去尝试了解一下。 我选择了git里非常受欢迎的yolo系列,并尝试了最新版本YOLO11或者叫它ultr…...

Effective Objective-C 2.0 读书笔记—— 消息转发

Effective Objective-C 2.0 读书笔记—— 消息转发 文章目录 Effective Objective-C 2.0 读书笔记—— 消息转发前言消息转发机制概述动态方法解析处理dynamic的属性用于懒加载 消息转发快速消息转发完整消息转发 总结 前言 在前面我学习了关联对象和objc_msgSend的相关内容&a…...

【Python-办公自动化】实现自动化输出json数据类型的分析报告和正逆转换

分析报告 import json from pprint import pprint, PrettyPrinterdef analyze_energy_data(file_path):"""能源数据分析与结构查看函数参数:file_path (str): JSON文件路径功能:1. 加载并解析JSON数据2. 显示数据结构概览3. 交互式结构探索"""…...

Docker小游戏 | 使用Docker部署RPG网页小游戏

Docker小游戏 | 使用Docker部署RPG网页小游戏 前言一、项目介绍项目简介项目预览二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署RPG网页小游戏下载镜像创建容器检查容器状态检查服务端口安全设置四、访问RPG网页小游戏五、总结前言 随着互联网技术的不断…...

技术周总结 01.13~01.19 周日(Spring Visual Studio git)

文章目录 一、01.14 周二1.1)问题01:Spring的org.springframework.statemachine.StateMachine 是什么,怎么使用?:如何使用StateMachine: 1.2)问题02:Spring StateMachine 提供了一系列高级特性 …...

Linux中使用unzip

安装命令 yum install unzip unzip常用选项和参数 选项 说明 -q 隐藏解压过程中的消息输出 -d /path/to/directory 指定解压文件的目标目录 -P password 如果.zip文件被密码保护,使用此选项可以指定打开文件所需的密码 解压命令 unzip 要解压的压缩包unz…...

Baklib引领内容管理平台新时代优化创作流程与团队协作

内容概要 在迅速变化的数字化时代,内容管理平台已成为各种行业中不可或缺的工具。通过系统化的管理,用户能够有效地组织、存储和共享信息,从而提升工作效率和创意表达。Baklib作为一款新兴的内容管理平台,以其独特的优势和创新功…...

利用Redis实现数据缓存

目录 1 为啥要缓存捏? 2 基本流程(以查询商铺信息为例) 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除(半自动) 3.3 主动更新(手动) 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…...

jQuery小游戏(二)

jQuery小游戏(二) 今天是新年的第二天,本人在这里祝大家,新年快乐,万事胜意💕 紧接jQuery小游戏(一)的内容,我们开始继续往下咯😜 游戏中使用到的方法 key…...

农产品价格报告爬虫使用说明

农产品价格报告爬虫使用说明 # ************************************************************************** # * * # * 农产品价格报告爬虫 …...

xceed PropertyGrid 如何做成Visual Studio 的属性窗口样子

类似这样的&#xff0c;我百度了一下&#xff0c;发现使用Xceed 不错。使用PropertyGrid 前台代码为 <Windowx:Class"WpfApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.co…...

Fork/Join框架_任务分解与并行执行

1 概述 Fork/Join框架是Java 7引入的一个用于并行执行任务的框架。它特别适用于可以递归分解为多个子任务的工作,每个子任务可以独立执行,并且结果可以合并以获得最终结果。Fork/Join框架通过工作窃取(work-stealing)算法提高了多核处理器上的任务执行效率。 2 核心组件 …...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...