Advertisement

(17-2-01)检索增强生成(RAG):文档加载器(1)

阅读量:

5.2 文档加载器(Document loaders)

Document loaders are tools designed to acquire data from various data sources and convert it into documents (documents), which consist of a segment of text along with accompanying metadata. For instance, document loaders can handle simple ".txt" files, any webpage’s text content, or video’s textual notes.

5.2.1 加载数据的方法

文档加载器负责加载并处理数据,并为此系统提供了必要的基础支持以完成后续的工作。在LangChain框架中,文档加载器通过多种方法来进行数据的获取与管理。

  1. 加载(Load)过程:通过预先设置的数据来源获取文档内容。
  2. 加载与分割(Load and split)功能:除常规的文档导入外,还支持借助提供的文本分隔器将内容划分为多个片段。
  3. 延迟加载机制(Lazy load):此功能可选,并使系统按需将文档内容导入内存。

借助该方法, 开发者可以根据具体需求选择适当的加载方案。这类工具能够有效地帮助开发者实现数据处理与准备的规范化流程, 从而有助于后续检索与生成任务的有效开展。借助该文档loader能够更加方便地将各类数据转化为LangChain兼容的形式, 这样就能在构建基于语言模型的应用程序时提供支持。

假设有一个名为example.txt的文本文件,内容如下:

复制代码
 Load

    
 Load
    
 Load
    
 Load

在下面的代码中查看,在库LangChain中调用TextLoader类以从名为example.txt的文本文件中加载内容,并打印该文件的内容到控制台。

复制代码
 from langchain_community.document_loaders import TextLoader

    
 loader = TextLoader("example.txt")
    
 # 打印加载的文本内容
    
 print(loader.load())

执行后会输:

复制代码
    [Document(page_content='Load\nLoad\nLoad\nLoad', metadata={'source': 'example.txt'})]

总结来说,在代码中展示了如何利用库LangChain中的文档加载器来读取并输出文本文件的过程。该操作在实际应用中常被采用作为基础操作,在涉及多个领域如数据处理和分析等的应用场景下尤为常见。除了打印文档内容之外,在实际应用中还能够进一步应用于后续的任务如文本分类、情感分析以及生成等用途。特别提醒:在进行此操作时,请确保路径正确无误且所选文件与程序处于同一目录空间或已提供适当权限以保证正常运行。

5.2.2 自定义文档加载器

在大型语言模型的应用程序中经常需要从数据库或者文件(特别是PDF格式的文件)中提取必要的信息并将这些信息转化为适合该模型使用的特定格式。该框架提供了一种方法用于实现这一过程具体而言就是在LangChain框架下我们通常会创建文档对象并进行操作这些对象整合了提取的内容以及相关的元数据部分其中元数据部分是一个包含书籍或其他类型资料详细信息的关键组件例如书籍的相关信息如作者、出版年份等详细内容。

文档对象常被编码为提示信息后输入至LLM。
其目标是使LLM提取和利用文档中的信息生成所需响应。
这些操作包括对文档进行总结或回答特定问题。
此外这些处理既可以即时应用也可存入向量数据库备查以便未来检索与运用。

文档加载的主要抽象概念如下所示。

  1. 文件结构(File Structure):包含了文本及其相关元数据信息。
  2. 数据转换模块(Data Conversion Module):负责将原始数据转换为符合系统规范的文件结构。
  3. 大型二进制块(Large Binary Block):代表系统中存储的连续二进制数据内容。
  4. 大型块解析引擎(Large Block Parser Engine):负责对大型二进制块进行解码并构建相应的文件结构。

1. 标准文档加载器

在LangChain框架中,我们能够采用基类加载器BaseLoader的继承模式来实现标准文档的导入过程。具体来说,在LangChain中可以通过基类加载器BaseLoader的子类化方式来完成标准文档的导入功能。该框架提供了一系列用于规范文档导入的标准接口。

  1. deliberate_loading: 该术语指代一种按需逐步导入文件的方法,在开发环境中使用。
  2. async_deliberate_loading: 作为deliberate_loading的一种增强版。
  3. import_all_documents: 该函数旨在一次性导入全部文件至内存环境,并被应用于原型设计及交互式操作场景。
  4. abrupt_import_all_documents: 在deliberate_loading的基础上实现了快速导入机制,并被应用于原型设计及交互式操作场景。这一功能于LangChain平台于2024年4月首次推出。

在上述方法中,load方法适合原型设计工作,因为它可以快速加载所有文档到内存中,但在生产环境中不推荐使用,因为可能会导致内存不足。alazy_load是一个异步方法,通常用于I/O密集型任务。alazy_load方法有一个默认实现,委托给lazy_load,但如果应用程序是异步的,建议覆盖默认实现,提供一个原生的异步实现,以便更有效地利用异步编程的优势。

请看下面的实例,演示了创建一个自定义的标准文档加载器的过程。

实例5-1****:创建一个自定义的标准文档加载器(源码路径:codes*5**biao*.py********)****

实例文件biao.py的具体实现代码如下所示。

复制代码
 from langchain_core.document_loaders import BaseLoader

    
 from langchain_core.documents import Document
    
 import aiofiles
    
  
    
 class CustomDocumentLoader(BaseLoader):
    
     def __init__(self, file_path: str):
    
     self.file_path = file_path
    
  
    
     def lazy_load(self):
    
     with open(self.file_path, 'r', encoding='utf-8') as file:
    
         for line_number, line in enumerate(file, start=1):
    
             yield Document(
    
                 page_content=line.strip(),
    
                 metadata={"line_number": line_number, "source": self.file_path}
    
             )
    
  
    
     async def alazy_load(self):
    
     async with aiofiles.open(self.file_path, 'r', encoding='utf-8') as file:
    
         line_number = 0
    
         async for line in file:
    
             line_number += 1
    
             yield Document(
    
                 page_content=line.strip(),
    
                 metadata={"line_number": line_number, "source": self.file_path}
    
             )

上述代码的实现流程如下所示:

  1. 首先,定义了一个名为 CustomDocumentLoader 的类,它继承自 BaseLoader,这是LangChain提供的标准加载器基类。这个加载器可以用于从文件中逐行加载文档并生成相应的文档对象。
  2. 在 CustomDocumentLoader 类中,我们定义了两个方法:lazy_load 和 alazy_load。其中,lazy_load 方法用于按需地逐行加载文档,而 alazy_load 方法是其异步版本,用于在异步环境中加载文档。
  3. 在 lazy_load 方法中,使用 Python 的内置 open 函数来打开文件,并通过 enumerate 函数逐行读取文件内容。然后,我们创建 Document 对象来表示每一行文本,并将其作为生成器的输出。
  4. 在 alazy_load 方法中,使用 库aiofiles来异步地打开文件,并通过异步循环 async for 来逐行读取文件内容。同样地,我们创建 Document 对象来表示每一行文本,并将其作为异步生成器的输出。
  5. 最后,展示了如何使用这个加载器来加载文档,并打印文档内容以及相应的元数据。执行后会输出:
复制代码
 文档内容: Load, 元数据: {'line_number': 1, 'source': 'example.txt'}

    
 文档内容: Load, 元数据: {'line_number': 2, 'source': 'example.txt'}
    
 文档内容: Load, 元数据: {'line_number': 3, 'source': 'example.txt'}
    
 文档内容: Load, 元数据: {'line_number': 4, 'source': 'example.txt'}

2. 基础二进制大对象解析器(BaseBlobParser)

在文档加载过程中,多个加载器需执行文件解析步骤,并非基于不同的加载策略展开。实际上,在此过程中不同之处主要体现在文件解析策略上,并非在于文件加载机制本身。例如,在此场景下如可借助 open 函数获取 PDF 或者 Markdown 文件的二进制数据,并必须通过特定的解析逻辑将该二进制数据转化为可读文本形式。因此将解析逻辑与加载逻辑分离能够带来诸多便利,在此情形下这使得对现有解析器进行复用更为便捷。在后续讨论的部分中将首先阐述LangChain如何将基础二进制大对象(Blob)转化为文档对象的方法接口或基类实现过程

该接口基于是二进制大型对象解析器(BaseBlobParser),其功能是接收二进制大型对象(Blob)并将其解码为文档对象列表。BaseBlobParser的作用在于将文件或内存中的二进制数据解码,并转换为便于处理的文档对象。

3. 二进制大对象(Blob)

Binary large objects (Blobs) represent binary data stored in files or memory. Blobs serve as intermediate representations during document loading. The content can be data read from files or generated within memory blocks. In LangChain, blobs are abstract concepts used to describe raw data stored in file systems or within memory. The blocks can encompass various forms of binary data types, including images, audio, video, and documents. These types of data are typically not directly usable for text processing.

Blob的核心特征是以原始字节序列(byte array)的形式来进行存储与传输,并避免任何解码或解释处理。其工作原理使其特别适合于文件传输、文件系统操作以及数据持久化等应用场景。

可以通过指定文件路径或直接提供内存中的数据来构造 Blob 对象。通过调用 Blob.from_path 方法并指定文件路径即可构造一个新的 Blob 实例;若需从内存中现有数据构造,则可以直接初始化 Blob 类。其属性及相关操作方法将在后续部分进行详细说明。

1.blob.encoding: 读取或设置用于表示 Blob 物体编码方式的功能。其默认值通常采用 "utf-8" 编码方案。
2.blobs.as_bytes(): 该函数能够生成一个包含当前 Blob 物体原始二进制数据块的对象。
3.blobs.as_string(): 通过调用此方法可以获得当前 Blob 物体以文本形式表示的内容,并采用当前配置所指定编码方式进行解码处理。
4.blobs.as_bytes_io(): 调用该方法将提供一个资源管理器接口(IO),允许像对待文件一样按行进行读写操作。
5.blobs.metadata: 获取或设置用于描述 Blob 物体属性和特征的一份补充资料结构化存储于字典中的内容。
6.blobs.source: 获取用于标识当前 Blob 物体来源的具体路径信息功能。

以下是一个示例说明如何利用基础二进制大对象解析器(BaseBlobParser)和二进制大对象(Blob)来逐行读取文本文件的内容,并将每一行作为一个文档对象进行处理。在这个案例中,首先创建了一个自定义的解析器类,并将文本文件表示为 Blob 对象;接着使用该解析器对 Blob 进行逐行解析,并生成相应的文档对象以供处理。

实例5-1****:逐行读取文本文件的内容(源码路径:codes*5**da*.py********)****

实例文件da.py的具体实现流程如下所示。

开发一个基于BaseBlobParser的定制化解析器类型,旨在分析和处理来自文本文件的数据内容

复制代码
 from langchain_core.document_loaders import BaseBlobParser, Blob

    
 from langchain_core.documents import Document
    
  
    
 class LineByLineParser(BaseBlobParser):
    
     def lazy_parse(self, blob: Blob):
    
     line_number = 0
    
     with blob.as_bytes_io() as f:
    
         for line in f:
    
             line_number += 1
    
             yield Document(
    
                 page_content=line.strip(),
    
                 metadata={"line_number": line_number, "source": blob.source}
    
             )

(2)生成一个名为example1.txt的文本文件,并将其以二进制大型对象的形式存在(Blob)。随后,我们可以运用我们自定义的解析器来解码该 Blob,并逐一处理其每一行数据。

复制代码
 # 创建一个文本文件

    
 with open("example1.txt", "w") as file:
    
     file.write("Hello, world!\nThis is the second line.\nAnd this is the third line.")
    
  
    
 # 将文本文件表示为 Blob
    
 blob = Blob.from_path("example1.txt")
    
  
    
 # 使用解析器解析 Blob
    
 parser = LineByLineParser()
    
 for document in parser.lazy_parse(blob):
    
     print(document.page_content)
    
     print(document.metadata)

通过巧妙地结合基础二进制大对象解析器与二进制大对象资源,在详细的过程中逐一分析文本文件的内容,并成功构建出相应的文档对象。当操作完成时,系统将输出详细的解析结果。

复制代码
 Hello, world!

    
 {'line_number': 1, 'source': 'example1.txt'}
    
 This is the second line.
    
 {'line_number': 2, 'source': 'example1.txt'}
    
 And this is the third line.
    
 {'line_number': 3, 'source': 'example1.txt'}

此时文本文件'example1.txt的内容如下:

复制代码
 Hello, world!

    
 This is the second line.
    
 And this is the third line.

注意:二进制大对象解析器主要用于解析位于Blob内的数据,并作为数据的一种表达方式存在。

4. Blob加载器

解析器主要承担将二进制数据解码为文档内容的任务;同时用于从指定存储位置获取相应的 blob 对象;截至目前, LangChain 仅支持 FileSystem_blob_loader 作为其内置的 blob 加载模块;一般而言, file_system blob_loader 常与 blob 解析器协同工作;从而实现对原始 blob 中的数据进行解码和构建相关文档的过程。

在使用FileSystemBlobLoader时,开发者能够集成使用LangChain所提供的诸如BaseBlobParser等其他工具与接口。例如,在加载文件内容时可配合利用这些组件进行进一步解析与分析。这种模块化架构使得LangChain具备高度灵活性,并可轻易扩展以支持更多元的数据源及相应的加载机制。

请参考下面的示例:假设有一个位于本地存储中的文件夹。
其中包含了许多文本文件。
我们打算使用Blob加载器将这些文件加载为Blob对象。
并通过自定义的Blob解析器将其解析为文档对象。

实例5-1****:从指定文件夹加载所有的文本文件(源码路径:codes*5**bl*.py********)****

实例文件bl.py的具体实现代码如下所示。

复制代码
 from langchain_core.document_loaders import BaseBlobParser, Blob

    
  
    
 class CustomBlobParser(BaseBlobParser):
    
     """A custom parser that creates a document from each line."""
    
  
    
     def lazy_parse(self, blob: Blob):
    
     """Parse a blob into a document line by line."""
    
     with blob.as_bytes_io() as f:
    
         for line_number, line in enumerate(f, start=1):
    
             yield {"page_content": line.decode("utf-8"), "metadata": {"line_number": line_number}}
    
 from langchain_community.document_loaders.blob_loaders import FileSystemBlobLoader
    
  
    
 # 创建一个文件系统Blob加载器实例,指定要加载的文件夹路径
    
 blob_loader = FileSystemBlobLoader(path="../5", glob="*.txt")
    
  
    
 # 创建一个自定义Blob解析器实例
    
 parser = CustomBlobParser()
    
  
    
 # 使用Blob加载器迭代获取Blob对象
    
 for blob in blob_loader.yield_blobs():
    
     # 使用自定义解析器解析Blob对象,并获取文档对象
    
     for doc in parser.lazy_parse(blob):
    
     print(doc)

上述代码的实现流程如下所示:

首先创建了一个名为 CustomBlobParser 的自定义 Blob 解析器类,并负责处理 lazy_parse 方法以实现逐行解析功能。
随后导入 FileSystemBlobLoader 类并创建实例 blob_loader。
然后随后创建了自定义解析器实例 parser。
最后通过循环遍历文件系统加载器迭代生成的所有 Blob 对象进行处理。
执行后会输出:

复制代码
 {'page_content': 'Load\r\n', 'metadata': {'line_number': 1}}

    
 {'page_content': 'Load\r\n', 'metadata': {'line_number': 2}}
    
 {'page_content': 'Load\r\n', 'metadata': {'line_number': 3}}
    
 {'page_content': 'Load\r\n', 'metadata': {'line_number': 4}}
    
 {'page_content': 'Hello, world!\r\n', 'metadata': {'line_number': 1}}
    
 {'page_content': 'This is the second line.\r\n', 'metadata': {'line_number': 2}}
    
 {'page_content': 'And this is the third line.', 'metadata': {'line_number': 3}}

运行后将生成文件夹"5"中的每一个文件名及其相关元数据信息。这些元数据主要包括页面具体内容以及每条记录对应的行号信息。具体而言,在结果中将生成四个包含了关键词"Load"的相关文件名及相应元数据记录;而对于其他类型的数据,则会生成多个与之匹配的具体内容条目。随后生成三个包含具体文字资料的文件夹,则会对应于每条记录的具体文字描述;而针对其他类型的字段信息,则会根据具体情况分别生成相应的字段索引条目或字段值列表项等

5. 通用加载器(GenericLoader)

在LangChain框架中,通用加载器(GenericLoader)被定义为一种抽象概念。这种设计提供了规范化的手段来整合现有的Blob加载器(BlobLoader)与BaseBlobParser进行操作。该通用加载器的设计初衷是为了简化并统一来自多种数据源的加载与解析流程,并通过这种方式帮助开发者更轻松地构建并运用自定义的数据加载方案。

在LangChain中,通用加载器的特点如下所示。

  1. 标准化接口:该库采用了一种统一的标准接口。
  2. 灵活性体现于开发者可以根据具体需求选择合适的Blob加载器与解析工具。
  3. 类方法与API架构设计实现了简便易行的自定义加载器创建过程。

在LangChain中,通用加载器的工作流程如下所示。

建议使用Blob加载器:基于数据存储位置与格式的考虑,推荐选用合适的选项.例如FileSystemBlobLoader适用于从文件系统中加载文件.

配置特定数据格式的Blob解析器:为特定数据格式设计并配置Blob解析器。当处理的数据类型为文本文件时,请选用自定义的文本文件解析器以实现精准的数据读取。

(3)生成或建立GenericLoader实例:通过调用该类的方法,并传入所需的Blob加载器和解析器以及其他必要的参数来生成或建立一个GenericLoader实例。

(4)加载和解析数据:通过GenericLoader实例加载数据,并根据所选的Blob解析器完成数据解析。

如以下所示的是一个使用通用加载器(GenericLoader)的具体实现示例。该示例旨在演示如何通过该加载器访问特定文件夹中的所有文档对象,并详细展示每个文档对象及其相关的元数据信息。这些元数据包括页面内容以及对应的行号信息。

实例五号文件:通过通用加载器获取目录中每个文档对象的内容及元数据(源码路径:codes\5\tong.py

实例文件tong.py的具体实现代码如下所示。

复制代码
 from langchain_community.document_loaders.generic import GenericLoader

    
 from langchain_core.document_loaders import BaseBlobParser
    
 from langchain_core.documents import Document
    
  
    
 # 自定义解析器类,继承自BaseBlobParser
    
 class MyParser(BaseBlobParser):
    
     def lazy_parse(self, blob):
    
     with blob.as_bytes_io() as f:
    
         for line_number, line in enumerate(f, start=1):
    
             yield Document(page_content=line.decode("utf-8"), metadata={"line_number": line_number})
    
  
    
 # 使用通用加载器从文件系统中加载并解析文档
    
 def load_documents_from_filesystem():
    
     # 指定文件夹路径和文件类型
    
     loader = GenericLoader.from_filesystem(path="./data", glob="*.txt", show_progress=True, parser=MyParser())
    
     
    
     # 逐个获取文档对象
    
     for doc in loader.lazy_load():
    
     print(f"文档内容: {doc.page_content}, 元数据: {doc.metadata}")
    
  
    
 # 执行加载文档的函数
    
 load_documents_from_filesystem()

在上述代码中

复制代码
 100%|██████████| 2/2 [00:00<00:00, 48.79it/s]

    
 文档内容: Load
    
 , 元数据: {'line_number': 1}
    
 文档内容: Load
    
 , 元数据: {'line_number': 2}
    
 文档内容: Load
    
 , 元数据: {'line_number': 3}
    
 文档内容: Load
    
 , 元数据: {'line_number': 4}
    
 文档内容: Hello, world!
    
 , 元数据: {'line_number': 1}
    
 文档内容: This is the second line.
    
 , 元数据: {'line_number': 2}
    
 文档内容: And this is the third line., 元数据: {'line_number': 3}

未完待续

全部评论 (0)

还没有任何评论哟~