【可能是全网最丝滑的LangChain教程】十三、LangChain进阶之Document loaders

不要让别人告诉你你能做什么,不能做什么。如果你有一个梦想,就去捍卫它。

01 介绍

Document loader(文档加载器),使用文档加载器将数据从数据源加载为 Document 对象的数据。

Document 是 LangChain 提供的一个类,包含一段文本和与文本关联的元数据。我们读取数据后就会转换成 Document 对象。

例如,有一些文档加载器用于加载简单的 .txt 文件、加载任何网页的文本内容,甚至用于加载 YouTube 视频。文档加载器提供了“ load ”方法,用于从配置的源加载数据,转换为 Document。它们还可以选择实现“延迟加载”,以便将数据延迟加载到内存中。

基于 LLM 的应用程序经常需要从数据库或文件(如 PDF)中提取数据,并将其转换为 LLM 可以使用的格式。在LangChain中,这通常涉及创建 Document 对象,这些对象封装了提取的文本(page_content)以及元数据(metadata 包含文档详细信息的字典),例如作者姓名或出版日期。

Document 对象通常被格式化为提示,这些提示被输入到 LLM 中,允许 LLM 使用 Document 中的信息来生成所需的响应(例如,总结文档)。Documents 既可以立即使用,也可以索引到向量存储中,以备将来检索和使用。

02 基础使用

这里以TextLoader为例

python复制代码from langchain_community.document_loaders import TextLoader

"""
file_path:要加载的文件的路径。
encoding:要使用的文件编码。如果“无”,将加载文件使用默认的系统编码。
autodetect_encoding:是否尝试自动检测文件编码如果指定的编码失败。
"""
loader = TextLoader("./index.txt",encoding='utf-8',autodetect_encoding=True)
loader.load()

"""
[
    Document(
        page_content='电影中的经典台词往往能够深入人心,成为人们记忆中的一部分。以下是一些电影中的经典台词:\n\n"生活就像一盒巧克力,你永远不知道你会得到什么。" ——《阿甘正传》(Forrest Gump, 1994)\n\n"永远不要小看自己,因为你永远不知道自己有多强大。" ——《狮子王》(The Lion King, 1994)\n\n"我会回来的。" ——《终结者》(The Terminator, 1984)\n\n"你不能改变过去,但你可以改变未来。" ——《回到未来》(Back to the Future, 1985)\n\n"即使世界末日来临,我也要和你一起度过。" ——《泰坦尼克号》(Titanic, 1997)\n\n"不要让别人告诉你你能做什么,不能做什么。如果你有一个梦想,就去捍卫它。" ——《当幸福来敲门》(The Pursuit of Happyness, 2006)\n\n"我将永远爱你。" ——《保镖》(The Bodyguard, 1992)\n\n"这就是生活,小家伙。" ——《美丽人生》(Life is Beautiful, 1997)\n\n"我有一个梦想。" ——《我有一个梦想》(I Have a Dream, 1963)\n\n"你只需要跟随你的黄砖路。" ——《绿野仙踪》(The Wizard of Oz, 1939)\n\n这些台词不仅在电影中令人难忘,也在现实生活中激励着许多人。',
         metadata={'source': './index.txt'}
    )
]
"""

通过以上代码我们可以看到:

  1. load方法返回的是 Document 列表。
  2. Document 里面主要包含两个元素,page_content 和 metadata。
  3. metadata 是字典类型,这里包含 source 键,表示数据源位置。metadata内容和 Loader 对象有关,不同的对象,生成的 metadata 内容不同。

03 自定义Document Loader

在自定义之前,先了解几个组件

组件描述
Document包含文本内容和元数据
BaseLoader用于将源数据转换成Document
Blob用于二进制数据的处理
BaseBlobParser将二进制数据转换成Document

Standard Document Loader

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

自定义标准文档加载器必须实现的方法是:lazy_load

你说你还想实现其他方法,能不能行?当然能行!建议这样做吗?肯定不建议!为什么不建议这样做?源码里面有解释,大家可以自己跟一下源码,我这里就不贴了!😂

这里我会创建一个自定义的标准文档加载器,该加载器每次从文档中读取一行数据并创建 Document 对象返回。代码来源于官方文档,如下:

Python复制代码from typing import AsyncIterator, Iterator

from langchain_core.document_loaders import BaseLoader
from langchain_core.documents import Document


class CustomDocumentLoader(BaseLoader):
    """An example document loader that reads a file line by line."""

    def __init__(self, file_path: str) -> None:
        """Initialize the loader with a file path.

        Args:
            file_path: The path to the file to load.
        """
        self.file_path = file_path
    
    def lazy_load(self) -> Iterator[Document]:  # <-- Does not take any arguments
        """A lazy loader that reads a file line by line.

        When you're implementing lazy load methods, you should use a generator
        to yield documents one by one.
        """
        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

    # alazy_load is OPTIONAL.
    # If you leave out the implementation, a default implementation which delegates to lazy_load will be used!
    async def alazy_load(
        self,
    ) -> AsyncIterator[Document]:  # <-- Does not take any arguments
        """An async lazy loader that reads a file line by line."""
        # Requires aiofiles
        # Install with `pip install aiofiles`
        # https://github.com/Tinche/aiofiles
        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

使用方式如下:

python复制代码loader = CustomDocumentLoader("./custom_loader.txt")
loader.load()

打印结果如下:

python复制代码"""
[
    Document(
        page_content='这是第一行\n',
        metadata={'line_number': 0, 'source': './custom_loader.txt'}
    ),
    Document(
        page_content='\n',
        metadata={'line_number': 1, 'source': './custom_loader.txt'}
    ),
    Document(
        page_content='第二行是空白行,这是第三行',
        metadata={'line_number': 2, 'source': './custom_loader.txt'})
]
"""

以上就是一个完整的自定义标准文档加载器实现,我们实现了lazy_load方法,同时也实现了alazy_load方法。这就呼应了我前面写的“可以全部方法都实现,但是不建议!”这里仅作示例说明。实际开发中如果能通过实现lazy_load一个方法解决问题的,就不要实现多个方法!

BaseBlobParser

BaseBlobParser 提供了接受 Blob 并输出 Document 对象列表的接口。

这里要说明一点:BaseBlobParser 与 BaseLoader 之间并不是割裂的,而是为了“组合复用”才有的 Blob 与 BaseBlobParser。 源码中很多的 XxxxLoader 内部的实现就是这样,下面贴出 PyPDFLoader 的源码图片用以说明。

image.png

我们将上面 Standard Document Loader 中的自定义示例做修改,用 Blob + BaseBlobParser 实现。代码来源官方,如下:

python复制代码from langchain_core.document_loaders import BaseBlobParser, Blob
from typing import Iterator
from langchain_core.documents import Document

class MyParser(BaseBlobParser):
    """A simple parser that creates a document from each line."""

    def lazy_parse(self, blob: Blob) -> Iterator[Document]:
        """Parse a blob into a document line by line."""
        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},
                )

使用方式如下:

python复制代码blob = Blob.from_path("./custom_loader.txt")
parser = MyParser()
list(parser.lazy_parse(blob))

打印结果如下:

bash复制代码"""
[
    Document(
        page_content='这是第一行\r\n', 
        metadata={'line_number': 1, 'source': './custom_loader.txt'}),
    Document(
        page_content='\r\n', 
        metadata={'line_number': 2, 'source': './custom_loader.txt'}),
    Document(
        page_content='第二行是空白行,这是第三行', 
        metadata={'line_number': 3, 'source': './custom_loader.txt'})
]
"""

使用 Blob 还支持从内存中加载内容,而无需从文件中读取!示例如下:

python复制代码blob = Blob(data=b"one\n\nthree")
list(parser.lazy_parse(blob))

"""
[
    Document(
        page_content='one\n', 
        metadata={'line_number': 1, 'source': None}),
    Document(
        page_content='\n', 
        metadata={'line_number': 2, 'source': None}),
    Document(
        page_content='three', 
        metadata={'line_number': 3, 'source': None})
]
"""

Blob

这里对Blob常用API做简单介绍。

python复制代码blob = Blob.from_path("./custom_loader.txt", metadata={"name": "张三"})

print(blob.encoding)
"""
utf-8
"""

print(blob.as_bytes())
"""
b'\xe8\xbf\x99\xe6\x98\xaf\xe7\xac\xac\xe4\xb8\x80\xe8\xa1\x8c\r\n\r\n\xe7\xac\xac\xe4\xba\x8c\xe8\xa1\x8c\xe6\x98\xaf\xe7\xa9\xba\xe7\x99\xbd\xe8\xa1\x8c\xef\xbc\x8c\xe8\xbf\x99\xe6\x98\xaf\xe7\xac\xac\xe4\xb8\x89\xe8\xa1\x8c'
"""

print(blob.as_string())
"""
这是第一行

第二行是空白行,这是第三行
"""

print(blob.as_bytes_io())
"""
print(blob.as_bytes_io())
"""

print(blob.metadata)
"""
{'name': '张三'}
"""

print(blob.source)
"""
print(blob.source)
"""

Blob Loaders

BlobLoader 用于从指定位置加载 Blob,返回一个 Blob迭代器对象。目前 LangChain 中仅支持 FileSystemBlobLoader 与 YoutubeAudioLoader。前者用于文件系统,后者用于从 YouTube 视频网址提取音频文件。

以 FileSystemBlobLoader 为例,示例代码如下:

python复制代码from langchain_community.document_loaders.blob_loaders import FileSystemBlobLoader

# 当前文件目录下,所有以ipynb结尾的文件,进行解析
blob_loader = FileSystemBlobLoader(path=".", glob="*.ipynb", show_progress=True)

parser = MyParser()
for blob in blob_loader.yield_blobs():
    for doc in parser.lazy_parse(blob):
        # 因为我们的逻辑是按照行读取并处理成 Document,这里只打印第一行结果看效果
        # 大家可以自行全部打印
        print(doc)
        break
        
"""
  0%|          | 0/9 [00:00<?, ?it/s]
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Agent.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Chat Model.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Conversation Retrieval Chain.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Document loader.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'LCEL.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Model.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': 'Output parsers.ipynb'}
page_content='{\n' metadata={'line_number': 1, 'source': 'Prompt.ipynb'}
page_content='{\r\n' metadata={'line_number': 1, 'source': '【可能是全网最丝滑的LangChain教程】三、快速入门.ipynb'}
"""

Generic Loader

LangChain 有一个 GenericLoader 抽象,它由 BlobLoader 和 BaseBlobParser 组成。GenericLoader 旨在提供标准化的类方法,使现有 BlobLoader 实现易于使用。目前仅支持 FileSystemBlobLoader,因为支持单一,这里不做详细介绍,简单说下用法。

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

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

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

"""
page_content='这是第一行\r\n' metadata={'line_number': 1, 'source': 'custom_loader.txt'}
page_content='\r\n' metadata={'line_number': 2, 'source': 'custom_loader.txt'}
page_content='第二行是空白行,这是第三行' metadata={'line_number': 3, 'source': 'custom_loader.txt'}
page_content='电影中的经典台词往往能够深入人心,成为人们记忆中的一部分。以下是一些电影中的经典台词:\r\n' metadata={'line_number': 1, 'source': 'index.txt'}
page_content='\r\n' metadata={'line_number': 2, 'source': 'index.txt'}
page_content='"生活就像一盒巧克力,你永远不知道你会得到什么。" ——《阿甘正传》(Forrest Gump, 1994)\r\n' metadata={'line_number': 3, 'source': 'index.txt'}
"""

04 常用 Loader 介绍

上部分我们已经知道了如何自定义 Loader,这里我们再看 LangChain 中的内置 Loader 那就是小儿科了。

因为我们已经知道了 Loader 的本质!

全部内置 Loader 如下:

bash复制代码class BaseLoader
  class GenericLoader
    class ConcurrentLoader
  class WebBaseLoader
    class AZLyricsLoader
    class BlackboardLoader
    class CollegeConfidentialLoader
    class SitemapLoader
      class DocusaurusLoader
    class GitbookLoader
    class HNLoader
    class IMSDbLoader
  class AcreomLoader
  class AirbyteCDKLoader
    class AirbyteHubspotLoader
    class AirbyteStripeLoader
    class AirbyteTypeformLoader
    class AirbyteZendeskSupportLoader
    class AirbyteShopifyLoader
    class AirbyteSalesforceLoader
    class AirbyteGongLoader
  class AirbyteJSONLoader
  class AirtableLoader
  class UnstructuredBaseLoader
    class UnstructuredFileLoader
      class UnstructuredAPIFileLoader
      class UnstructuredPDFLoader
      class UnstructuredCSVLoader
      class UnstructuredWordDocumentLoader
      class UnstructuredEmailLoader
      class UnstructuredCHMLoader
      class UnstructuredEPubLoader
      class UnstructuredExcelLoader
      class UnstructuredHTMLLoader
      class UnstructuredImageLoader
      class UnstructuredMarkdownLoader
      class UnstructuredODTLoader
      class UnstructuredOrgModeLoader
      class UnstructuredPowerPointLoader
      class UnstructuredRSTLoader
      class UnstructuredRTFLoader
      class UnstructuredTSVLoader
      class UnstructuredXMLLoader
    class UnstructuredFileIOLoader
      class UnstructuredAPIFileIOLoader
    class UnstructuredLakeFSLoader
    class S3FileLoader
  class BasePDFLoader
    class OnlinePDFLoader
    class PyPDFLoader
    class PyPDFium2Loader
    class PDFMinerLoader
    class PDFMinerPDFasHTMLLoader
    class PyMuPDFLoader
    class MathpixPDFLoader
    class PDFPlumberLoader
    class AmazonTextractPDFLoader
    class DocumentIntelligenceLoader
  class PyPDFDirectoryLoader
  class ApifyDatasetLoader
  class ArcGISLoader
  class ArxivLoader
  class AssemblyAIAudioTranscriptLoader
  class AssemblyAIAudioLoaderById
  class AstraDBLoader
  class AsyncChromiumLoader
  class AsyncHtmlLoader
  class AthenaLoader
  class AzureAIDataLoader
  class AzureAIDocumentIntelligenceLoader
  class AzureBlobStorageFileLoader
  class AzureBlobStorageContainerLoader
  class BSHTMLLoader
  class BibtexLoader
  class BigQueryLoader
  class BiliBiliLoader
  class CSVLoader
  class TextLoader
    class PythonLoader
  class DirectoryLoader
  class BlockchainDocumentLoader
  class BraveSearchLoader
  class BrowserbaseLoader
  class BrowserlessLoader
  class CassandraLoader
  class ChatGPTLoader
  class CoNLLULoader
  class ConfluenceLoader
  class CouchbaseLoader
  class CubeSemanticLoader
  class BaseDataFrameLoader
    class DataFrameLoader
    class PolarsDataFrameLoader
    class XorbitsLoader
  class DatadogLogsLoader
  class DiffbotLoader
  class DiscordChatLoader
  class DocugamiLoader
  class Docx2txtLoader
  class DropboxLoader
  class DuckDBLoader
  class EtherscanLoader
  class EverNoteLoader
  class FacebookChatLoader
  class FaunaLoader
  class FigmaFileLoader
  class FireCrawlLoader
  class GCSFileLoader
  class GCSDirectoryLoader
  class GlueCatalogLoader
  class GeoDataFrameLoader
  class BaseGitHubLoader
    class GitHubIssuesLoader
    class GithubFileLoader
  class GitLoader
  class YoutubeLoader
  class GoogleApiYoutubeLoader
  class GoogleDriveLoader
  class GoogleSpeechToTextLoader
  class GutenbergLoader
  class HuggingFaceDatasetLoader
  class HuggingFaceModelLoader
  class IFixitLoader
  class ImageCaptionLoader
  class IuguLoader
  class JoplinLoader
  class JSONLoader
  class KineticaLoader
  class LakeFSLoader
  class LarkSuiteDocLoader
    class LarkSuiteWikiLoader
  class LLMSherpaFileLoader
  class MastodonTootsLoader
  class MHTMLLoader
  class MWDumpLoader
  class MaxComputeLoader
  class MergedDataLoader
  class ModernTreasuryLoader
  class MongodbLoader
  class NewsURLLoader
  class NotebookLoader
  class NotionDBLoader
  class NotionDirectoryLoader
  class OBSFileLoader
  class OBSDirectoryLoader
  class ObsidianLoader
  class OneDriveFileLoader
  class O365BaseLoader
    class OneDriveLoader
    class SharePointLoader
  class OpenCityDataLoader
  class OracleAutonomousDatabaseLoader
  class OracleDocLoader
  class OutlookMessageLoader
  class PebbloSafeLoader
  class PlaywrightURLLoader
  class PsychicLoader
  class PubMedLoader
  class PySparkDataFrameLoader
  class RSSFeedLoader
  class ReadTheDocsLoader
  class RecursiveUrlLoader
  class RedditPostsLoader
  class RoamLoader
  class RocksetLoader
  class S3DirectoryLoader
  class SQLDatabaseLoader
  class SRTLoader
  class SeleniumURLLoader
  class SlackDirectoryLoader
  class SnowflakeLoader
  class SpiderLoader
  class SpreedlyLoader
  class StripeLoader
  class SurrealDBLoader
  class TelegramChatFileLoader
  class TelegramChatApiLoader
  class TencentCOSFileLoader
  class TencentCOSDirectoryLoader
  class TensorflowDatasetLoader
  class TiDBLoader
  class ToMarkdownLoader
  class TomlLoader
  class TrelloLoader
  class TwitterTweetLoader
  class UnstructuredURLLoader
  class VsdxLoader
  class WeatherDataLoader
  class WhatsAppChatLoader
  class WikipediaLoader
  class YuqueLoader

这些 Loader 可以加载哪些数据?基本我们生活中常见的数据形式都可以!

加载B站视频、加载百度搜索结果、加载URL数据、加载word、ppt、csv、pdf、html等常见文档、加载常见数据库文件、为图片配文字、为视频加字幕、加载视频内容…

写到这,我只能用一个字形容:

05 总结

我们知道了 LangChain 中的 Loader,我们会用、会自定义,那么问题来了,我们学习 Loader 有什么用?大家总不能为了学 Loader 而学吧?!

既然最终的 Document 中包含 page_content 和 metadata,我们想办法把这些数据给到 LLM,然后让 LLM 根据这些数据做总结、做客服、做分析,这是不是就闭环了?!

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值