Dify中的文本分词处理技术详解

Dify中的文本分词处理技术详解

    • 引言
    • 核心架构概览
      • 索引处理器工厂
    • 文本分词技术详解
      • 基础分词器
      • 增强型递归字符分词器
      • 固定分隔符文本分词器
      • 递归分割算法
    • 索引处理器中的分词应用
    • 特殊索引处理器的分词特点
      • 问答索引处理器
      • 父子索引处理器
    • 分词技术的应用场景
    • 技术亮点与优势
    • 总结

引言

在现代RAG(检索增强生成)系统中,文本分词(Text Splitting)是一个至关重要的环节。它直接影响到检索的精度和生成内容的质量。本文将深入解析Dify项目中的文本分词处理技术,探讨其实现原理、核心算法和应用场景。

核心架构概览

Dify采用了工厂模式和策略模式来实现灵活的文本处理流程。整个文本处理架构主要包含两个核心部分:

  1. 索引处理器(Index Processor):负责文档的提取、转换和加载
  2. 文本分词器(Text Splitter):负责将长文本切分成适合处理的小块

索引处理器工厂

索引处理器工厂(IndexProcessorFactory)是创建不同类型索引处理器的核心类,它通过工厂模式实现了对不同索引处理策略的封装和创建:

class IndexProcessorFactory:"""IndexProcessorInit."""def __init__(self, index_type: str | None):self._index_type = index_typedef init_index_processor(self) -> BaseIndexProcessor:"""Init index processor."""if not self._index_type:raise ValueError("Index type must be specified.")if self._index_type == IndexType.PARAGRAPH_INDEX:return ParagraphIndexProcessor()elif self._index_type == IndexType.QA_INDEX:return QAIndexProcessor()elif self._index_type == IndexType.PARENT_CHILD_INDEX:return ParentChildIndexProcessor()else:raise ValueError(f"Index type {self._index_type} is not supported.")

该工厂类支持三种索引处理器:

  1. 段落索引处理器(ParagraphIndexProcessor):将文档分割成段落级别的块
  2. 问答索引处理器(QAIndexProcessor):专门处理问答格式的文本
  3. 父子索引处理器(ParentChildIndexProcessor):创建层次化的文档结构

文本分词技术详解

基础分词器

Dify的分词系统建立在抽象基类TextSplitter之上,它定义了分词的基本接口:

@abstractmethod
def split_text(self, text: str) -> list[str]:"""Split text into multiple components."""

所有具体的分词器都必须实现这个方法,以提供特定的分词逻辑。

增强型递归字符分词器

EnhanceRecursiveCharacterTextSplitter是一个关键的分词器实现,它通过递归方式处理文本,并支持使用不同的编码器计算token数量:

class EnhanceRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter):"""This class is used to implement from_gpt2_encoder, to prevent using of tiktoken"""@classmethoddef from_encoder(cls: type[TS],embedding_model_instance: Optional[ModelInstance],allowed_special: Union[Literal["all"], Set[str]] = set(),disallowed_special: Union[Literal["all"], Collection[str]] = "all",**kwargs: Any,):def _token_encoder(texts: list[str]) -> list[int]:if not texts:return []if embedding_model_instance:return embedding_model_instance.get_text_embedding_num_tokens(texts=texts)else:return [GPT2Tokenizer.get_num_tokens(text) for text in texts]# ... 其他代码 ...return cls(length_function=_token_encoder, **kwargs)

这个分词器的特点是可以使用嵌入模型的tokenizer或默认的GPT2 tokenizer来计算文本长度,避免了对tiktoken的依赖。

固定分隔符文本分词器

FixedRecursiveCharacterTextSplitter是一个更为专业的分词器,它在增强型递归分词器的基础上,增加了对固定分隔符的支持:

class FixedRecursiveCharacterTextSplitter(EnhanceRecursiveCharacterTextSplitter):def __init__(self, fixed_separator: str = "\n\n", separators: Optional[list[str]] = None, **kwargs: Any):"""Create a new TextSplitter."""super().__init__(**kwargs)self._fixed_separator = fixed_separatorself._separators = separators or ["\n\n", "\n", " ", ""]def split_text(self, text: str) -> list[str]:"""Split incoming text and return chunks."""if self._fixed_separator:chunks = text.split(self._fixed_separator)else:chunks = [text]final_chunks = []chunks_lengths = self._length_function(chunks)for chunk, chunk_length in zip(chunks, chunks_lengths):if chunk_length > self._chunk_size:final_chunks.extend(self.recursive_split_text(chunk))else:final_chunks.append(chunk)return final_chunks

这个分词器的工作流程如下:

  1. 首先使用固定分隔符(默认为\n\n)将文本分割成初步的块
  2. 对每个块计算token长度
  3. 如果块的长度超过了设定的最大长度(_chunk_size),则调用recursive_split_text方法进一步分割
  4. 否则直接将块添加到最终结果中

递归分割算法

recursive_split_text方法是固定分隔符分词器的核心,它实现了复杂的递归分割逻辑:

def recursive_split_text(self, text: str) -> list[str]:"""Split incoming text and return chunks."""final_chunks = []separator = self._separators[-1]new_separators = []# 寻找最合适的分隔符for i, _s in enumerate(self._separators):if _s == "":separator = _sbreakif _s in text:separator = _snew_separators = self._separators[i + 1 :]break# 使用找到的分隔符分割文本if separator:if separator == " ":splits = text.split()else:splits = text.split(separator)else:splits = list(text)splits = [s for s in splits if (s not in {"", "\n"})]# ... 处理分割后的文本块 ...

该算法的精妙之处在于:

  1. 它会按照优先级顺序尝试不同的分隔符(如\n\n, \n, , ""
  2. 一旦找到文本中存在的分隔符,就使用它进行分割
  3. 如果当前分隔符分割后的块仍然过大,会使用下一级别的分隔符继续分割
  4. 最终确保所有文本块都不超过指定的最大token数量

索引处理器中的分词应用

在实际应用中,索引处理器会根据处理规则选择合适的分词器。以ParagraphIndexProcessor为例,它在transform方法中使用分词器处理文档:

def transform(self, documents: list[Document], **kwargs) -> list[Document]:# ... 其他代码 ...splitter = self._get_splitter(processing_rule_mode=process_rule.get("mode"),max_tokens=rules.segmentation.max_tokens,chunk_overlap=rules.segmentation.chunk_overlap,separator=rules.segmentation.separator,embedding_model_instance=kwargs.get("embedding_model_instance"),)all_documents = []for document in documents:# 文档清洗document_text = CleanProcessor.clean(document.page_content, kwargs.get("process_rule", {}))document.page_content = document_text# 解析文档为节点document_nodes = splitter.split_documents([document])# ... 处理分割后的节点 ...

分词器的选择逻辑在_get_splitter方法中实现:

def _get_splitter(self,processing_rule_mode: str,max_tokens: int,chunk_overlap: int,separator: str,embedding_model_instance: Optional[ModelInstance],) -> TextSplitter:"""Get the NodeParser object according to the processing rule."""if processing_rule_mode in ["custom", "hierarchical"]:# 用户自定义分割规则# ... 参数验证 ...character_splitter = FixedRecursiveCharacterTextSplitter.from_encoder(chunk_size=max_tokens,chunk_overlap=chunk_overlap,fixed_separator=separator,separators=["\n\n", "。", ". ", " ", ""],embedding_model_instance=embedding_model_instance,)else:# 自动分割character_splitter = EnhanceRecursiveCharacterTextSplitter.from_encoder(chunk_size=DatasetProcessRule.AUTOMATIC_RULES["segmentation"]["max_tokens"],chunk_overlap=DatasetProcessRule.AUTOMATIC_RULES["segmentation"]["chunk_overlap"],separators=["\n\n", "。", ". ", " ", ""],embedding_model_instance=embedding_model_instance,)return character_splitter

这里的逻辑很清晰:

  1. 对于自定义或层次化处理模式,使用FixedRecursiveCharacterTextSplitter,允许指定固定分隔符
  2. 对于自动处理模式,使用EnhanceRecursiveCharacterTextSplitter,采用预设的参数

特殊索引处理器的分词特点

问答索引处理器

QAIndexProcessor针对问答格式的文本有特殊的处理逻辑:

def _format_split_text(self, text):regex = r"Q\d+:\s*(.*?)\s*A\d+:\s*([\s\S]*?)(?=Q\d+:|$)"matches = re.findall(regex, text, re.UNICODE)return [{"question": q, "answer": re.sub(r"\n\s*", "\n", a.strip())} for q, a in matches if q and a]

它使用正则表达式识别问题和答案的模式,将文本转换为结构化的问答对。

父子索引处理器

ParentChildIndexProcessor实现了层次化的文档处理,它会先将文档分割成父节点,然后对每个父节点进一步分割成子节点:

def _split_child_nodes(self,document_node: Document,rules: Rule,process_rule_mode: str,embedding_model_instance: Optional[ModelInstance],) -> list[ChildDocument]:# ... 获取子块分割规则 ...child_splitter = self._get_splitter(processing_rule_mode=process_rule_mode,max_tokens=rules.subchunk_segmentation.max_tokens,chunk_overlap=rules.subchunk_segmentation.chunk_overlap,separator=rules.subchunk_segmentation.separator,embedding_model_instance=embedding_model_instance,)# 解析文档为子节点child_nodes = []child_documents = child_splitter.split_documents([document_node])# ... 处理子节点 ...

这种层次化的处理方式特别适合处理结构复杂的长文档,可以保留文档的层次关系。

分词技术的应用场景

Dify中的分词技术主要应用于以下场景:

  1. 文档索引:将长文档分割成适合检索的小块
  2. 问答生成:识别和提取文本中的问答对
  3. 层次化处理:保留文档的层次结构,提高检索精度
  4. 自定义分割:根据用户需求定制分割策略

技术亮点与优势

  1. 灵活的工厂模式:通过工厂模式实现了索引处理器的灵活创建和管理
  2. 多级分隔符策略:采用优先级排序的分隔符列表,适应不同类型的文本
  3. 递归分割算法:确保分割后的文本块不超过指定的token限制
  4. 模型无关的token计算:支持使用不同的embedding模型计算token数量
  5. 自定义与自动模式:同时支持用户自定义分割规则和智能自动分割

总结

Dify的文本分词处理系统展示了一个设计良好的文本处理框架。它通过抽象接口、工厂模式和策略模式,实现了高度灵活和可扩展的文本分割功能。这些技术不仅提高了RAG系统的检索精度,也为开发者提供了丰富的自定义选项。

对于需要构建自己的RAG系统的开发者来说,Dify的分词处理技术提供了很好的参考和借鉴。特别是其递归分割算法和多级分隔符策略,是解决长文本处理问题的有效方案。

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

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

相关文章

如何打包python程序为可执行文件

将 Python 程序打包为可执行文件是一个常见需求,尤其是在希望将应用程序分享给不具备 Python 环境的用户时。以下是使用 PyInstaller 工具将 Python 程序打包为可执行文件的步骤。 步骤 1:安装 PyInstaller 如果您还没有安装 PyInstaller,请…

美团Java后端二面面经!

场景题是面试的大头,建议好好准备 Q. [美团]如何设计一个外卖订单的并发扣减库存系统? Q.[美团]为啥初始标记和重新标记需要STW? Q.[美团]骑手位置实时更新,如何保证高并发写入? Q.[美团]订单表数据量过大导致查询…

在应用运维过程中,业务数据修改的证据留存和数据留存

在应用运维过程中,业务数据修改的证据留存和数据留存至关重要,以下是相关介绍: 一、证据留存 操作日志记录 : 详细记录每一次业务数据修改的操作日志,包括操作人员、操作时间、修改内容、修改前后数据的对比等。例如,某公司业务系统中,操作日志会精确记录员工小张在 2…

Eigen迭代求解器类

1. 迭代求解器核心类概览 Eigen 提供多种迭代法求解稀疏线性方程组 AxbAxb,适用于大规模稀疏矩阵: 求解器类适用矩阵类型算法关键特性ConjugateGradient对称正定(SPD)共轭梯度法(CG)高精度,内…

ORACLE数据库备份入门:第四部分:2-备份场景举例

下面以4个常见的场景为例,介绍如何规划备份方案。备份方案没有标准答案,需要根据实现情况来制定,也和管理员的个人使用习惯有很大相关性。 1 交易型数据库备份 以银行的交易系统为例,除了前一章节提到的关于RPO和RTO的指标外&am…

小白如何学会完整挪用Github项目?(以pix2pix为例)

[目录] 0.如何完整地复现/应用一个Github项目 1.建立适用于项目的环境 2.数据准备与模型训练阶段 3.训练过程中的一些命令行调试必备知识0.如何完整地复现/应用一个Github项目 前日在健身房的组间同一位好友交流时,得到了一个一致结论—— ** Github \texttt{Githu…

蓝桥杯 5. 交换瓶子

交换瓶子 原题目链接 题目描述 有 N 个瓶子,编号为 1 ~ N,放在架子上。 例如有 5 个瓶子,当前排列为: 2 1 3 5 4每次可以拿起 2 个瓶子,交换它们的位置。 要求通过若干次交换,使得瓶子的编号从小到大…

Linux 系统渗透提权

Linux 系统渗透提权 比赛题库-Linux 系统渗透提权 文章目录 Linux 系统渗透提权比赛题库-Linux 系统渗透提权 前言一、解题过程1.使用渗透机对服务器信息收集,并将服务器中 SSH 服务端口号作为 flag 提 交;2.使用渗透机对服务器信息收集,并将…

华为OD机试真题——查找接口成功率最优时间段(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录…

华为OD机试真题——绘图机器(2025A卷:100分)Java/python/JavaScript/C++/C/GO最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录全流程解析/备考攻略/经验…

基于 Python(selenium) 的百度新闻定向爬虫:根据输入的关键词在百度新闻上进行搜索,并爬取新闻详情页的内容

该项目能够根据输入的关键词在百度新闻上进行搜索,并爬取新闻详情页的内容。 一、项目准备 1. 开发环境配置 操作系统:支持 Windows、macOS、Linux 等主流操作系统,本文以 Windows 为例进行说明。Python 版本:建议使用 Python 3.8 及以上版本,以确保代码的兼容性和性能。…

MySQL表的操作 -- 表的增删改查

目录 1. 表的创建2. 表的查看3. 表的修改4. 表的删除5. 总结 1. 表的创建 1.查看字符集及效验规则 2. 表的创建 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;创建用户表1 创建用…

如何解决极狐GitLab 合并冲突?

极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 合并冲突 (BASIC ALL) 合并冲突发生在合并请求的两个分支(源分支和目标分支)对相同代码行进行了不同…

oracle不同数据库版本的自增序列

-- 查看数据库版本 SELECT * FROM v$version WHERE banner LIKE Oracle%; 1. Oracle 12c及以上版本支持 id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1) PRIMARY KEY, -- 语法 id NUMBER GENER…

VIC-3D非接触全场应变测量系统用于小尺寸测量之电子元器件篇—研索仪器DIC数字图像相关技术

在5G通信、新能源汽车电子、高密度集成电路快速迭代的今天,电子元件的尺寸及连接工艺已进入亚毫米级竞争阶段,这种小尺寸下的力学性能评估对测量方式的精度有更高的要求,但传统应变测量手段常因空间尺寸限制及分辨率不足难以捕捉真实形变场。…

pod 创建私有库指南

步骤 参考:iOS Pod 私有库创建指南-百度开发者中心 下面主要是对参考链接里面的解释: 创建两个仓库: 一个叫podframe.git,用来存放自定义的framework,比如TestPodFrame.framework一个叫podspec.git,用来…

【JavaEE】Spring AOP的注解实现

目录 一、AOP 与 Spring AOP二、Spring AOP简单实现三、详解Spring AOP3.1 Spring AOP 核心概念3.1.1 切点(Pointcut)3.1.2 连接点(Join Point)3.1.3 通知(Advice)3.1.4 切面(Aspect&#xff09…

协作开发攻略:Git全面使用指南 — 结语

协作开发攻略:Git全面使用指南 — 结语 Git 是一种分布式版本控制系统,用于跟踪文件和目录的变更。它能帮助开发者有效管理代码版本,支持多人协作开发,方便代码合并与冲突解决,广泛应用于软件开发领域。 文中内容仅限技…

如何用AI主动突出画面主体!涂鸦新方案助剪辑、工业巡检、医疗影像等领域,实现自动追踪+智能放大

随着智能 IPC 设备(如安防摄像头、宠物陪伴机器人、婴儿监视器等)日益普及,越来越多的生活场景被实时记录。然而在实际使用中,由于设备安装位置不当、广角镜头视野过大等原因,经常会出现拍摄主体占比过小的问题&#x…

数据湖DataLake和传统数据仓库Datawarehouse的主要区别是什么?优缺点是什么?

数据湖和传统数据仓库的主要区别 以下是数据湖和传统数据仓库的主要区别,以表格形式展示: 特性数据湖传统数据仓库数据类型支持结构化、半结构化及非结构化数据主要处理结构化数据架构设计扁平化架构,所有数据存储在一个大的“池”中多层架…