主页 > 软件开发  > 

dify实现分析-rag-文档内容提取

dify实现分析-rag-文档内容提取
dify实现分析-rag-文档内容提取 概述

在文章《dify实现原理分析-上传文件创建知识库总体流程》中已经介绍了,文件上传后索引构建的总体流程,本文介绍其中的“Extract: 提取文档内容:这里会按段落或整页来获取文档内容”步骤的实现。

这一步的主要功能是:从不同格式的文档中提取文本内容,这里的格式包括:pdf、word、csv、html、txt、markdown、ppt等等。不同格式的文本,需要使用的文本获取的类和对象不同。

文档的内容获取是在IndexingRunner._extract函数中实现。该函数的声明如下:

def _extract( self, index_processor: BaseIndexProcessor, dataset_document: DatasetDocument, process_rule: dict ) -> list[Document]: IndexingRunner._extract函数 #mermaid-svg-lJeBrisUo0kHPE0l {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .error-icon{fill:#552222;}#mermaid-svg-lJeBrisUo0kHPE0l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lJeBrisUo0kHPE0l .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lJeBrisUo0kHPE0l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lJeBrisUo0kHPE0l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lJeBrisUo0kHPE0l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lJeBrisUo0kHPE0l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lJeBrisUo0kHPE0l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lJeBrisUo0kHPE0l .marker.cross{stroke:#333333;}#mermaid-svg-lJeBrisUo0kHPE0l svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lJeBrisUo0kHPE0l .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .cluster-label text{fill:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .cluster-label span{color:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .label text,#mermaid-svg-lJeBrisUo0kHPE0l span{fill:#333;color:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .node rect,#mermaid-svg-lJeBrisUo0kHPE0l .node circle,#mermaid-svg-lJeBrisUo0kHPE0l .node ellipse,#mermaid-svg-lJeBrisUo0kHPE0l .node polygon,#mermaid-svg-lJeBrisUo0kHPE0l .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lJeBrisUo0kHPE0l .node .label{text-align:center;}#mermaid-svg-lJeBrisUo0kHPE0l .node.clickable{cursor:pointer;}#mermaid-svg-lJeBrisUo0kHPE0l .arrowheadPath{fill:#333333;}#mermaid-svg-lJeBrisUo0kHPE0l .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lJeBrisUo0kHPE0l .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lJeBrisUo0kHPE0l .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-lJeBrisUo0kHPE0l .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-lJeBrisUo0kHPE0l .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lJeBrisUo0kHPE0l .cluster text{fill:#333;}#mermaid-svg-lJeBrisUo0kHPE0l .cluster span{color:#333;}#mermaid-svg-lJeBrisUo0kHPE0l div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lJeBrisUo0kHPE0l :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上传文件 Notion导入 网站爬取 验证数据源类型 数据源分类处理 处理文件上传 处理Notion数据 处理网站数据 更新文档状态为: splitting 为获取的段落文档数据添加元数据

详细的实现逻辑如下:

验证数据源类型,数据源类型必须是: {“upload_file”, “notion_import”, “website_crawl”}这三种中的一种。根据不同数据源类型来设置索引构建的参数,并调用索引构建器来构建索引,分为两步:

​ (1)设置文件内容提取的参数:也就是构建ExtractSetting对象

​ (2)调用对应索引构建器的extract函数:index_processor.extract来处理文档。

​ 这一步从文档中获取段落或页数据,并把文档段落内容保存到Document对象中。

更新数据库中文档的处理状态为:splitting为从文本中提取出来的每个段落内容添加元数据:添加文档id(document_id)和数据集id(dataset_id)。 索引处理器:index_processor.extract

有2种索引处理器(index_processor):

ParagraphIndexProcessor:段落索引处理器。QAIndexProcessor:文档索引处理器。 两种索引处理器的对比 分段处理方式 #mermaid-svg-2ftigA0vjsAh2g1O {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .error-icon{fill:#552222;}#mermaid-svg-2ftigA0vjsAh2g1O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2ftigA0vjsAh2g1O .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-2ftigA0vjsAh2g1O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2ftigA0vjsAh2g1O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2ftigA0vjsAh2g1O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2ftigA0vjsAh2g1O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2ftigA0vjsAh2g1O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2ftigA0vjsAh2g1O .marker.cross{stroke:#333333;}#mermaid-svg-2ftigA0vjsAh2g1O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2ftigA0vjsAh2g1O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .cluster-label text{fill:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .cluster-label span{color:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .label text,#mermaid-svg-2ftigA0vjsAh2g1O span{fill:#333;color:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .node rect,#mermaid-svg-2ftigA0vjsAh2g1O .node circle,#mermaid-svg-2ftigA0vjsAh2g1O .node ellipse,#mermaid-svg-2ftigA0vjsAh2g1O .node polygon,#mermaid-svg-2ftigA0vjsAh2g1O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2ftigA0vjsAh2g1O .node .label{text-align:center;}#mermaid-svg-2ftigA0vjsAh2g1O .node.clickable{cursor:pointer;}#mermaid-svg-2ftigA0vjsAh2g1O .arrowheadPath{fill:#333333;}#mermaid-svg-2ftigA0vjsAh2g1O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2ftigA0vjsAh2g1O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2ftigA0vjsAh2g1O .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-2ftigA0vjsAh2g1O .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-2ftigA0vjsAh2g1O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2ftigA0vjsAh2g1O .cluster text{fill:#333;}#mermaid-svg-2ftigA0vjsAh2g1O .cluster span{color:#333;}#mermaid-svg-2ftigA0vjsAh2g1O div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2ftigA0vjsAh2g1O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始文档 ParagraphIndexProcessor QAIndexProcessor 段落式分割 问答式分割 段落文档 QA对文档 主要特点对比 特性ParagraphIndexProcessorQAIndexProcessor分割粒度段落级别问答对级别处理方式直接分段分段后转QA检索效果适合段落匹配适合问答匹配使用场景文档检索问答系统索引结构段落向量QA对向量处理开销较低较高(需LLM) 适用场景

ParagraphIndexProcessor**

普通文档检索

相似段落查找

大规模文档索引

处理速度更快

QAIndexProcessor

问答系统构建

精确答案提取

专业领域QA

生成更精确的问答对

这两种索引处理器都会调用ExtractProcessor.extract函数来进行处理。

ExtractProcessor.extract

在该函数中实现多种数据源和文件格式的统一内容提取处理。该函数的总体实现逻辑如下:

#mermaid-svg-o2SE3fukUQGK5Ask {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .error-icon{fill:#552222;}#mermaid-svg-o2SE3fukUQGK5Ask .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o2SE3fukUQGK5Ask .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-o2SE3fukUQGK5Ask .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o2SE3fukUQGK5Ask .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o2SE3fukUQGK5Ask .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o2SE3fukUQGK5Ask .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o2SE3fukUQGK5Ask .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o2SE3fukUQGK5Ask .marker.cross{stroke:#333333;}#mermaid-svg-o2SE3fukUQGK5Ask svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o2SE3fukUQGK5Ask .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .cluster-label text{fill:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .cluster-label span{color:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .label text,#mermaid-svg-o2SE3fukUQGK5Ask span{fill:#333;color:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .node rect,#mermaid-svg-o2SE3fukUQGK5Ask .node circle,#mermaid-svg-o2SE3fukUQGK5Ask .node ellipse,#mermaid-svg-o2SE3fukUQGK5Ask .node polygon,#mermaid-svg-o2SE3fukUQGK5Ask .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o2SE3fukUQGK5Ask .node .label{text-align:center;}#mermaid-svg-o2SE3fukUQGK5Ask .node.clickable{cursor:pointer;}#mermaid-svg-o2SE3fukUQGK5Ask .arrowheadPath{fill:#333333;}#mermaid-svg-o2SE3fukUQGK5Ask .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o2SE3fukUQGK5Ask .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o2SE3fukUQGK5Ask .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-o2SE3fukUQGK5Ask .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-o2SE3fukUQGK5Ask .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o2SE3fukUQGK5Ask .cluster text{fill:#333;}#mermaid-svg-o2SE3fukUQGK5Ask .cluster span{color:#333;}#mermaid-svg-o2SE3fukUQGK5Ask div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-o2SE3fukUQGK5Ask :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文件类型 Notion 网站 Unstructured 默认 接收提取设置 判断数据源类型 文件处理 Notion处理 网站处理 判断ETL类型 Unstructured文档处理 标准文档处理 选择文档内容提取器 执行提取

不同类型文档对应的文档内容提取器如下:

# ETL类型判断 if etl_type == "Unstructured": # Unstructured提取器映射 extractors = { ".xlsx": ExcelExtractor, ".pdf": PdfExtractor, ".md": UnstructuredMarkdownExtractor if is_automatic else MarkdownExtractor, ".docx": WordExtractor, ".csv": CSVExtractor, ".msg": UnstructuredMsgExtractor, # ...更多格式支持 } else: # 标准提取器映射 extractors = { ".xlsx": ExcelExtractor, ".pdf": PdfExtractor, ".md": MarkdownExtractor, # ...默认提取器 }

说明:这些文档内容提取器按提取内容的粒度大部分是以页为单位来进行提取并保存,其中csv是以行为单位来提取。提取到的内容保存到Document.page_content变量中,然后返回一个Document对象的列表,供后面的切割使用。

举例:pdf文本内容提取

PdfExtractor 类的的主要功能是从 PDF 文件中提取文本内容,并将其转换为一系列 Document 对象。实现的主要功能如下:

加载文件: 使用 __init__ 方法初始化,接受一个 file_path 参数来指定 PDF 文件的路径,并可以接受一个可选的 file_cache_key 来指定缓存键。 提取文本: extract 方法首先尝试从缓存中加载文件,如果存在则直接返回文档;否则读取并解析 PDF 文件。使用 load 方法惰性加载 PDF 文件为页面流,并将每个页面传递给 parse 方法进行解析。 解析文件内容: parse 方法使用 pypdfium2 库来惰性解析 PDF 页面,提取文本并将其包装成 Document 对象。每个 Document 对象包含一个页码和页面内容。 缓存处理: 如果文件未在缓存中找到,则将解析后的文本保存到缓存中以供后续使用。 代码结构说明 __init__: 初始化 file_path 和可选的 file_cache_key。extract: 主要提取逻辑,优先尝试从缓存加载文件,如果没有则读取并解析。load: 懒惰加载 PDF 文件为页面流。parse: 解析每个 PDF 页面,生成包含文本和元数据的 Document 对象。

示例:

# 初始化 PdfExtractor 并提取文档 pdf_extractor = PdfExtractor(file_path="example.pdf", file_cache_key="cache_key") documents = pdf_extractor.extract() # 打印所有文档的内容 for doc in documents: print(doc.page_content)

这段代码展示如何初始化 PdfExtractor 类并从指定的 PDF 文件中提取文档内容。

总结

本文分析了索引构建的文件内容提取部分的实现逻辑。通过本文的分析可知,dify会先对文档内容进行提取,然后再对提取的内容进行处理,最后再构建索引。对文件内容进行提取时,会根据文件格式选择不同的文件内容提取器,提取文件内容时,一般是按页来读取然后保存到Document对象的page_content变量中。最后,返回Document的列表供后续对内容进行进一步处理。

标签:

dify实现分析-rag-文档内容提取由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“dify实现分析-rag-文档内容提取