Skip to content

RAG:文档加载器 Document Loaders ​

2.2 文档加载器 Document Loaders ​

数据源可能包含多种格式的文件,如文本文档、Markdown,PDF 等。LangChain 实现和集成了众多文档加载器(https://docs.langchain.com/oss/python/integrations/document_loaders ),方便从不同格式的文件中加载数据。

常用 Loaders:

  • TextLoader - 文本文件

  • CSVLoader - CSV 文件

  • PyPDFLoader - PDF 文件

  • WebBaseLoader - 网页

LangChain的设计:对于Source 中多种不同的数据源,我们可以用一种统一的形式读取、调用。上述每一个文档加载器,都要继承自 BaseLoader 基类,此类提供了通用的 load (一次加载所有文档)与 lazy_load (以延迟方式加载文档) 方法,用于从数据源加载数据并处理为Document 对象。

2.2.1 加载txt ​

python
# 1.导入相关依赖
from langchain_community.document_loaders import TextLoader

# 2.定义TextLoader对象,file_path=".txt的位置"
text_loader = TextLoader(file_path="../asset/load/01-langchain-utf-8.txt",
encoding="utf-8")

# 3.加载
docs = text_loader.load()  #返回List列表(Document对象)

# 4.打印
print(docs)
text
[Document(metadata={'source': '../asset/load/01-langchain-utf-8.txt'},
page_content='LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮
助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模
块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务')]

Documment对象中有两个重要的属性:

  • page_content:真正的文档内容,字符串类型。

  • metadata:文档内容的原数据,字典类型。

python
print(type(docs[0])) #langchain_core.documents.base.Document
python
print(docs[0].page_content)
'''
LangChain 是一个用于开发由大型语言模型 (LLMs) 驱动的应用程序的框架。LangChain简化了LLM
应用程序生命周期的每个阶段。\nLangChain 已经成为了我们每一个大模型开发工程师的标配。
'''
python
print(docs[0].metadata)  # {'source': './data/langchain.txt'}

2.2.2 加载CSV ​

举例:加载csv所有列

python
from langchain_community.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path="asset/load/04-load.csv")
data = loader.load()
print(data)

print(type(data))  # <class 'list'>
print(type(data[0]))  # <class 'langchain_core.documents.base.Document'>
print(len(data))  # 4
print(data[0].page_content)  # id: 1  title: Introduction to Python ...
text
[Document(metadata={'source': 'asset/load/04-load.csv', 'row': 0},
page_content='id: 1\ntitle: Introduction to Python\ncontent: Python is a
popular programming language.\nauthor: John Doe'), Document(metadata=
{'source': 'asset/load/04-load.csv', 'row': 1}, page_content='id:
2\ntitle: Data Science Basics\ncontent: Data science involves statistics
and machine learning.\nauthor: Jane Smith'), Document(metadata=
{'source': 'asset/load/04-load.csv', 'row': 2}, page_content='id:
3\ntitle: Web Development\ncontent: HTML, CSS and JavaScript are core
web technologies.\nauthor: Mike Johnson'), Document(metadata={'source':
'asset/load/04-load.csv', 'row': 3}, page_content='id: 4\ntitle:
Artificial Intelligence\ncontent: AI is transforming many
industries.\nauthor: Sarah Williams')]
<class 'list'>
<class 'langchain_core.documents.base.Document'>
4
id: 1
title: Introduction to Python
content: Python is a popular programming language.
author: John Doe

2.2.3 加载JSON ​

LangChain提供的JSON格式的文档加载器是JSONLoader 。在实际应用场景中,JSON格式的数据占有很大比例,而且JSON的形式也是多样的。我们需要特别关注。

JSONLoader 使用指定的 jq结构来解析 JSON 文件。jq是一个轻量级的命令行 JSON 处理器 ,可以对JSON 格式的数据进行各种复杂的处理,包括数据过滤、映射、减少和转换,是处理 JSON 数据的首选工具之一。

bash
# 在requirements_full.txt中已经安装
pip install jq

常见 jq schema 参考:

text
JSON        -> ["...", "...", "..."]
jq_schema   -> ".[]"

JSON        -> [{"text": ...}, {"text": ...}, {"text": ...}]
jq_schema   -> ".[].text"

JSON        -> {"key": [{"text": ...}, {"text": ...}, {"text": ...}]}
jq_schema   -> ".key[].text"

详细用法可参考 https://jqlang.org/manual/#basic-filters。

举例1:使用JSONLoader文档加载器加载

python
# 1.导入依赖
from  langchain_community.document_loaders import JSONLoader
from rich import print as rprint

# 2.定义JSONLoader对象
# 情况1
json_loader=JSONLoader(
 file_path="../asset/load/03-load.json",
 jq_schema=".", ## 提取所有字段
 text_content=False #保持原始 JSON 结构,将提取的数据转换为JSON字符串存入
page_content字段中
)

# 情况2
# .messages[].content:遍历.messages[]中所有元素 从每一个元素中提取.content字段
# json_loader=JSONLoader(
#     file_path="../asset/load/03-load.json",
#     jq_schema=".messages[].content"
# )

# 3.加载
docs = json_loader.load()
rprint(docs)
json
[
Document(
  metadata={
      'source':
'D:\\code\\workspace_pycharm_test\\langchain1.2_beike\\asset\\load\\03-
load.json',
      'seq_num': 1
  },
  page_content='{"messages": [{"sender": "Alice", "content": "Hello,
how are you today?", "timestamp":
"2023-05-15T10:00:00"}, {"sender": "Bob", "content": "I\'m doing well,
thanks for asking!", "timestamp":
"2023-05-15T10:02:00"}, {"sender": "Alice", "content": "Would you like
to meet for lunch?", "timestamp":
"2023-05-15T10:05:00"}, {"sender": "Bob", "content": "Sure, that sounds
great!", "timestamp":
"2023-05-15T10:07:00"}], "conversation_id": "conv_12345",
"participants": ["Alice", "Bob"]}'
)
]

举例2:提取03-response.json文件中指定的文本

python
# 1.导入相关依赖
from langchain_community.document_loaders import JSONLoader
from rich import print as rprint

# 2.定义json文件的路径
file_path = '../asset/load/03-response.json'

# 3.定义JSONLoader对象
# 需求1:提取data.items中的数据
# loader = JSONLoader(
#     file_path=file_path,  # 文件路径
#     jq_schema=".data.items[]",
#     text_content=False,  # 提取内容是否为字符串格式
# )


# 需求2:提取data.items[].content中的数据
# loader = JSONLoader(
#     file_path=file_path,  # 文件路径
#     jq_schema=".data.items[].content",
# )


# 需求3:提取data.items中指定字段的数据
loader = JSONLoader(
 file_path=file_path,  # 文件路径
 jq_schema="""
     .data.items[] | {
         author,
         created_at,
         content: (.title + "\n" + .content)
     }
 """,
 text_content=False,  # 提取内容是否为字符串格式
)


# 4.加载
data = loader.load()
rprint(data)
json
[
Document(
  metadata={
      'source':
'D:\\code\\workspace_pycharm_test\\langchain1.2_beike\\asset\\load\\03-
response.json',
      'seq_num': 1
  },
  page_content='{"author": {"id": "user_1", "name": "Alice"},
"created_at": "2023-10-05T08:12:33Z",
"content": "Understanding JSONLoader\\nThis article explains how to
parse API responses..."}'
),
Document(
  metadata={
      'source':
'D:\\code\\workspace_pycharm_test\\langchain1.2_beike\\asset\\load\\03-
response.json',
      'seq_num': 2
  },
  page_content='{"author": {"id": "user_2", "name": "Bob"},
"created_at": "2023-10-05T09:15:21Z", "content":
"Advanced jq Schema Patterns\\nLearn to handle nested structures
with..."}'
),
Document(
  metadata={
      'source':
'D:\\code\\workspace_pycharm_test\\langchain1.2_beike\\asset\\load\\03-
response.json',
      'seq_num': 3
  },
  page_content='{"author": {"id": "user_3", "name": "Charlie"},
"created_at": "2023-10-05T10:03:47Z",
"content": "LangChain Metadata Handling\\nBest practices for preserving
metadata..."}'
)
]

2.2.4 加载pdf ​

PDF 存在多种来源格式,包括扫描版(图片 PDF)、电子文本版、混合版。并且布局格式也多种多样,包括单列布局、双列布局甚至竖排文本布局。并且包含段落、标题、页眉页脚、表格、数学公式、化学式、特殊符号、图片等各种元素。

因此,PDF 解析存在很多挑战。对于复杂 PDF,需要进行文本提取、布局检测、表格解析、公式识别等处理。

方式1:PyPDFLoader ​

LangChain加载PDF文件使用的是pypdf,先安装

bash
# 在requirements_full.txt中已经安装
pip install pypdf
python
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader(
 # 文件路径,支持本地文件和在线文件链接
 # file_path="../asset/load/04-sample.pdf",
 file_path="https://arxiv.org/pdf/alg-geom/9202012",
 # 提取模式:控制如何从 PDF 文件中解析和提取文本结构。
 #   plain 提取文本,默认值
 #   layout 布局感知提取模式,通常会通过插入大量的空格、换行符,来模拟原文档中的多栏、
缩进和间距(适用场景:学术论文(如 arXiv 论文)、多栏报刊杂志、带有左右分栏的合同)
 extraction_mode="plain",
)

docs = loader.load()

print(docs)
print(len(docs))

输出:略

方式2:MinerU ​

MinerU 提供了 PDF、Word、PPT、图片等文件的解析,支持图像提取、OCR、公式、表格解析等功能。

调用在线服务:https://mineru.net/apiManage/docs。可以从本地批量上传文件进行解析,并接收解析结果。

python
import os
import time
import requests
from dotenv import load_dotenv

load_dotenv(override=True)


def upload_files(file_paths: list[str]) -> str:
 """批量上传文件"""
 url = "https://mineru.net/api/v4/file-urls/batch"
 api_token = os.getenv("MINERU_API_TOKEN")
 header = {
     "Content-Type": "application/json",
     "Authorization": f"Bearer {api_token}",
 }

 files_info = [
     {
         "name": os.path.basename(file_path),
         "is_ocr": True,
         "data_id": f"file_{i}",
     }
     for i, file_path in enumerate(file_paths)
 ]

 data = {
     "enable_formula": True,
     "enable_table": True,
     "language": "ch",
     "files": files_info,
 }

 try:
     response = requests.post(url, headers=header, json=data)
     if response.status_code == 200:
         result = response.json()
         print("response success. result:{}".format(result))

         if result["code"] == 0:
             batch_id = result["data"]["batch_id"]
             urls = result["data"]["file_urls"]
             print("batch_id:{}\nurls:{}".format(batch_id, urls))

             for i in range(0, len(urls)):
                 with open(file_paths[i], "rb") as f:
                     res_upload = requests.put(urls[i], data=f)
                     if res_upload.status_code == 200:
                         print(f"{urls[i]} upload success")
                     else:
                         print(f"{urls[i]} upload failed")
                         return None

             return batch_id
         else:
             print("apply upload url failed, reason:
{}".format(result.get("msg")))
             return None
     else:
         print(
             "response not success. status:{} ,result:{}".format(
                 response.status_code, response.text
             )
         )
         return None

 except Exception as err:
     print(err)
     return None


def download_files(batch_id):
 """批量获取任务结果"""
 if not batch_id:
     print("batch_id为空,跳过下载")
     return

 os.makedirs("parsed_files", exist_ok=True)

 url = f"https://mineru.net/api/v4/extract-results/batch/{batch_id}"
python
 api_token = os.getenv("MINERU_API_TOKEN")
 header = {
     "Content-Type": "application/json",
     "Authorization": f"Bearer {api_token}",
 }

 failed_files = set()
 done_files = set()

 while True:
     res = requests.get(url, headers=header)
     result_json = res.json()

     if res.status_code != 200 or result_json.get("code") != 0:
         print("get result failed:", result_json)
         break

     extract_results = result_json["data"]["extract_result"]

     for result in extract_results:
         data_id = result["data_id"]

         if result["state"] == "failed":
             failed_files.add(data_id)

         elif result["state"] == "done" and data_id not in done_files:
             done_files.add(data_id)

             full_zip_url = result["full_zip_url"]
             res_download = requests.get(full_zip_url, stream=True)

             with open(

f"parsed_files/{result['file_name']}_{result['data_id']}.zip", "wb"
             ) as f:
                 for chunk in
res_download.iter_content(chunk_size=1024):
                     if chunk:
                         f.write(chunk)

     if len(failed_files) + len(done_files) == len(extract_results):
         break

     time.sleep(5)

 for i in failed_files:
     print("failed:", i)

 for i in done_files:
     print("done:", i)


file_paths = ["../asset/load/04-sample.pdf"]
batch_id = upload_files(file_paths)

if batch_id:
 download_files(batch_id)

说明:需要在.env文件中提供:

dotenv
#MinerU的API_TOKEN
MINERU_API_TOKEN=<你的API TOKEN>

2.2.5 加载word ​

可使用 UnstructuredWordDocumentLoader加载 Word 文件,需要 unstructured 包。(已在requirements_full.txt文件中安装)

举例:

python
from langchain_community.document_loaders import
UnstructuredWordDocumentLoader

loader = UnstructuredWordDocumentLoader(
 # 文件路径
 file_path="../asset/load/05-sgg_chat.docx",
 # 加载模式:
 #   single 返回单个Document对象
 #   elements 按标题等元素切分文档
 mode="single",
)

docs = loader.load()

print(len(docs))
print(docs)

输出:略

2.2.6 加载Markdown ​

可使用 UnstructuredMarkdownLoader 加载 Markdown 文件,需要 unstructured 包。(已在requirements_full.txt文件中安装)

举例1:使用UnstructuredMarkdownLoader加载md文件

python
# 1.导入相关的依赖
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from pprint import pprint

# 2.定义UnstructuredMarkdownLoader对象
loader = UnstructuredMarkdownLoader(
 file_path="../asset/load/06-load.md",
 # 加载模式:
 #   single 返回单个Document对象
 #   elements 按标题等元素切分文档
 mode= "single",
 # 解析策略:
 #   "fast"(快速模式),它会以最快的速度提取文本,不进行复杂的版面分析
 #   "hi_res" 高分辨率模式
 strategy="fast"
)
# 3.加载
docs = loader.load()

# 4.打印
print(len(docs))
pprint(doc)
python
1
Document(metadata={'source': '../asset/load/06-load.md'},
page_content='自然语言处理技术文档\n\n本文档用于测试UnstructuredMarkdownLoader
的中文处理能力。\n\n第一章:简介\n\n自然语言处理(NLP)是人工智能的重要分支,主要技术包
括:\n\n文本分类\n\n命名实体识别\n\n机器翻译\n\n情感分析\n\n问答系统\n\n第二章:关
键技术\n\n2.1 预训练模型\n\nBERT:双向Transformer编码器\n\nGPT:自回归语言模型
\n\nT5:文本到文本转换框架\n\n2.2 代码示例\n\n```python from transformers
import pipeline\n\n创建文本分类管道\n\nclassifier = pipeline("text-
classification", model="bert-base-chinese")\n\nresult = classifier("这家
餐厅的服务很棒!") print(result)')

举例2:精细分割文档,保留结构信息

将Markdown文档按语义元素(标题、段落、列表、表格等)拆分成多个独立的小文档(Element 对象),而不是返回单个大文档。通过指定mode="elements" 轻松保持这种分离。

python
# 1.导入相关的依赖
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from pprint import pprint

# 2.定义UnstructuredMarkdownLoader对象
md_loader = UnstructuredMarkdownLoader(
 file_path="../asset/load/06-load.md",
 # 加载模式:
 #   single 返回单个Document对象
 #   elements 按标题等元素切分文档
 mode= "elements",
 strategy="fast"
)

# 3.加载
docs = md_loader.load()

print(len(docs))
# 4.打印
for doc in docs:
 # pprint(doc)
 pprint(doc.page_content)
python
19
'自然语言处理技术文档'
'本文档用于测试UnstructuredMarkdownLoader的中文处理能力。'
'第一章:简介'
'自然语言处理(NLP)是人工智能的重要分支,主要技术包括:'
'文本分类'
'命名实体识别'
'机器翻译'
'情感分析'
'问答系统'
'第二章:关键技术'
'2.1 预训练模型'
'BERT:双向Transformer编码器'
'GPT:自回归语言模型'
'T5:文本到文本转换框架'
'2.2 代码示例'
'```python from transformers import pipeline'
'创建文本分类管道'
'classifier = pipeline("text-classification", model="bert-base-
chinese")'
'result = classifier("这家餐厅的服务很棒!") print(result)'

2.2.7 加载HTML(了解) ​

举例:

python
# 1.导入相关的依赖
from langchain_community.document_loaders import UnstructuredHTMLLoader

# 2.定义UnstructuredHTMLLoader对象
# strategy:
#   "fast" 解析加载html文件速度是比较快(但可能丢失部分结构或元数据)
#   "hi_res": (高分辨率解析) 解析精准(速度慢一些)
#   "ocr_only"  强制使用ocr提取文本,仅仅适用于图像(对HTML无效)

# mode :one of `{'paged', 'elements', 'single'}
#    "elements"  按语义元素(标题、段落、列表、表格等)拆分成多个独立的小文档

loader = UnstructuredHTMLLoader(
 file_path="../asset/load/07-load.html",
 mode="elements",
 strategy="fast"
)

# 3.加载
docs = loader.load()

print(len(docs))  # 16

# 4.打印
for doc in docs:
 pprint(doc)

输出:略

2.2.8 加载File Directory(了解) ​

除了上述的单个文件加载,我们也可以批量加载一个文件夹内的所有文件。

举例:

python
# 1.导入相关的依赖
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.document_loaders import PythonLoader
from pprint import pprint

# 2.定义DirectoryLoader对象,指定要加载的文件夹路径、要加载的文件类型和是否使用多线程
directory_loader = DirectoryLoader(
 path="../asset/load",
 glob="*.py", # 文件匹配模式(过滤器)。使用标准的 Unix 路径通配符。
 use_multithreading=True, # 是否启用多线程。填 True 意味着 LangChain 会同时并发
读取多个文件。
 show_progress=True, # 是否显示进度条。填 True 时,控制台在加载文件时会弹出一个进
度条
 loader_cls=PythonLoader # 指定底层核心加载器
)

# 3.加载
docs = directory_loader.load()

# 4.打印
print(len(docs))
for doc in docs:
 pprint(doc)
python
100%|██████████| 4/4 [00:00<00:00, 498.83it/s]
4
Document(metadata={'source': 'asset\\load\\07-fun.py'},
page_content='"""\n一 函数入门\n"""\n# 1.不使用函数\n# 打印欢迎信息
1\nprint("********************************")\nprint("*
        *")\nprint("*     欢迎来到Python世界       *")\nprint("*

*")\nprint("********************************")\n\n# 打印欢迎信息
2\nprint("********************************")\nprint("*
        *")\nprint("*     欢迎来到Python世界       *")\nprint("*

*")\nprint("********************************")\n\n# 打印欢迎信息
3\nprint("********************************")\nprint("*
        *")\nprint("*     欢迎来到Python世界       *")\nprint("*

*")\nprint("********************************")\n\n# 2.使用函数\ndef
print_welcome():\n    """打印欢迎信息"""\n
print("********************************")\n    print("*
          *")\n    print("*     欢迎来到Python世界       *")\n
print("*                              *")\n
print("********************************")\n\n# 多次调用函数打印欢迎信息
\nprint_welcome()\nprint_welcome()\nprint_welcome()')
Document(metadata={'source': 'asset\\load\\07-fun_param.py'},
page_content='"""\n二 函数参数\n"""\n\n\n# 1. 无参数版本 - 只能计算固定的购物车
\ndef calculate_total_no_params():\n    """计算固定购物车总价"""\n
prices = [100, 50, 30]  # 商品价格固定写死在函数内\n    total = 0\n    for
price in prices:\n        total += price\n    return total\n\n# 只能计算一
个固定的购物车\nprint(f"购物车总价:{calculate_total_no_params()}")\n\n# 2.有
参数版本 - 可以计算任意购物车\ndef calculate_total(prices):\n    """计算任意购
物车总价"""\n    total = 0\n    for price in prices:\n        total +=
price\n    return total\n\n# 可以计算任意购物车\ncart1 = [100, 50,
30]\ncart2 = [200, 80, 45, 60]\ncart3 = [75, 90, 120]\n\nprint("第一个购物
车总价:{calculate_total(cart1)}:")\nprint("第二个购物车总价:
{calculate_total(cart2)}")\nprint(f"第三个购物车总价:
{calculate_total(cart3)}")\n\n\n# 3.参数传递\n# 3.1 不可变类型 函数传递不可变
对象\n\ndef changeInt(a) :\n    print("函数体中未改变前a的内存地址",id(a))\n
a = 10   #底层会创建一个新对象 然后给新对象一个新值\n    print("函数体中改变后
a的内存地址",id(a))\n\na = 2 # 创建一个对象 然后给这个对象一个值
\nchangeInt(a)\nprint(a)\nprint("函数外b的内存地址",id(a))\n\n\n\n# 输出结果
\n# 函数体中未改变前a的内存地址 140729722661336\n# 函数体中改变后a的内存地址
140729722661592\n# 2\n# 函数外b的内存地址 140729722661336\n\n\n# 3.2 可变类
型 函数传递不可变对象\n\ndef changeList(myList) :\n    myList[1] = 50\n
print("函数内的值",myList) # [1,50,3]\n    print("函数内列表的内
存",id(myList)) # 0111111\n\nmlist = [1,2,3]  # 底层创建一个对象 地址
0111111\nchangeList(mlist)\nprint("函数外的值",mlist) # #
[1,50,3]\nprint("函数外列表的内存",id(mlist))\n\n# 输出结果\n# 函数内的值 [1,
50, 3]\n# 函数内列表的内存 1380193079680\n# 函数外的值 [1, 50, 3]\n# 函数外列
表的内存 1380193079680\n\n')
Document(metadata={'source': 'asset\\load\\07-fun_retun.py'},
page_content='"""\n四 函数的返回值\n"""\n# 1.返回表达式\n# 2.不带表达式的
return 语句,返回 None。\n# 3.函数中如果没有 return 语句,在函数运行结束后也会返回
None。\n# 4.用变量接收返回结果\n# 5.return 语句可以返回多个值,多个值会放在一个元
组中。\n\ndef f(a, b, c):\n    return a, b, c, [a, b, c]\nprint(f(1, 2,
3))  # (1, 2, 3, [1, 2, 3])\n')
Document(metadata={'source': 'asset\\load\\07-param_form.py'},
page_content='"""\n三 函数参数形式\n"""\n# 1.位置参数\n# 2.关键字参数\n# 3.默
认参数\n# 4.不定长参数\n# 4.1 带一个*\ndef printInfo(num,*vartuple):\n
print(num)\n    print(vartuple)\n\nprintInfo(70,60,50)\n\nprint("-" *
20)\n# 如果不定长的参数后面还有参数,必须通过关键字参数传参\ndef
printInfo1(num1,*vartuple,num) :\n    print(num)\n    print(num1)\n
print(vartuple)\n\nprintInfo1(10,20,num = 40)\n\nprint("-" * 20)\n# 如果
没有给不定长的参数传参,那么得到的是空元组\nprintInfo1(70,num = 60)\n# 4.2 带二个
*\ndef printInfo(num,**vardict):\n    print(num)\n    print(vardict)\n
# return\n\nprintInfo(10,key1 = 20,key2 = 30)')

2.2.9 了解:BaseLoader、Document类 ​

一方面:LangChain在设计时,要保证Source中多种不同的数据源,在接下来的流程中可以用一种统一的形式读取、调用。

另一方面:为什么PDFloader 和TextLoader 等Document Loader 都使用load() 去加载,且都使用.page_content 和.metadata 读取数据。

【解答】每一个在LangChain中集成的文档加载器,都要继承自BaseLoader(文档加载器) ,BaseLoader提供了一个名为"load"的公开方法,用于从配置的不同数据源加载数据,全部作为Document 对象。实现逻辑如下所示:

BaseLoader类分析

BaseLoader类定义了如何从不同的数据源加载文档,每个基于不同数据源实现的loader,都需要继承BaseLoader 。Baseloader要求不多,对于任何具体实现的loader,最少都要实现 load方法。

python
class BaseLoader(ABC):
 """文档加载器接口。

 实现应当使用生成器实现延迟加载方法,以避免一次性将所有文档加载进内存。

 `load` 方法仅供用户方便使用,不应被重写。
 """

 # 子类不应直接实现此方法。而应实现延迟加载方法。
 def load(self) -> List[Document]:
     """将数据加载为 Document 对象。"""
     return list(self.lazy_load())

 async def aload(self) -> list[Document]:
     """将数据加载为 Document 对象。load的异步版本"""
     return [document async for document in self.alazy_load()]

 def load_and_split(
     self, text_splitter: Optional[TextSplitter] = None
 ) -> List[Document]:
     """加载文档并将其分割成块。块以 Document 形式返回。
     不要重写此方法。它应被视为已弃用!
     参数:
         text_splitter: 用于分割文档的 TextSplitter 实例。默认为
RecursiveCharacterTextSplitter。
     返回:
         文档列表。
     """
             .....
             .....
         _text_splitter: TextSplitter = RecursiveCharacterTextSplitter()
     else:
         _text_splitter = text_splitter
     docs = self.load()
     return _text_splitter.split_documents(docs)

BaseLoader 把数据加载成Documents object ,存到 Documents 类中的page_content 中。

Document类分析

Document 允许用户与文档的内容进行交互,可以查看文档内容。

其继承体系如下

text
Serializable
↑
BaseMedia
├── id
├── metadata
↑
Document
├── page_content
├── type = "Document"

Document源码:

python
class Document(BaseMedia):
 """用于存储一段文本及其关联元数据的类。

 !!! note

     `Document` 用于 **检索工作流**,而不是聊天输入输出。
     如果要在对话中向 LLM 发送文本,请使用 `langchain.messages`
     中的消息类型。

 Example:
     ```python
     from langchain_core.documents import Document

     document = Document(
         page_content="Hello, world!", metadata={"source":
"https://example.com"}
     )
     """

 page_content: str
 """字符串文本。"""

 type: Literal["Document"] = "Document"

 def __init__(self, page_content: str, **kwargs: Any) -> None:
     """将 page_content 作为位置参数或命名参数传入。"""
     # mypy 会报怨说 page_content 没有在基类中定义。
     # 这里我们依赖 pydantic 基类来处理字段校验。
     super().__init__(page_content=page_content, **kwargs)  # type:
ignore[call-arg,unused-ignore]
 @classmethod
 def is_lc_serializable(cls) -> bool:
     """返回 `True`,表示该类是可序列化的。"""
     return True

 @classmethod
 def get_lc_namespace(cls) -> list[str]:
     """获取 LangChain 对象的命名空间。

     Returns:
         `["langchain", "schema", "document"]`
     """
     return ["langchain", "schema", "document"]

 def __str__(self) -> str:
     """重写 `__str__`,使其只展示 page_content 和 metadata。

     Returns:
         `Document` 的字符串表示形式。
     """
     # 该格式与 pydantic 的 __str__ 格式保持一致。
     #
     # 这样做的目的是:确保用户代码中那些直接把 Document 对象
     # 放入 prompt 的写法,不会因为新增 id 字段
     # 或未来新增其他字段而发生变化。
     #
     # 这个重写方法未来很可能会被移除,
     # 转而采用一种更通用的方案:
     # 在 prompt 内部直接格式化内容。
     if self.metadata:
         return f"page_content='{self.page_content}' metadata=
{self.metadata}"
     return f"page_content='{self.page_content}'"

BaseMedia源码:

python
class BaseMedia(Serializable):
 """用于检索和数据处理工作流中内容对象的基类。

 为那些需要被存储、索引或搜索的内容提供公共字段。

 !!! note

     对于 **聊天消息** 中的多模态内容
     例如发送给 LLM 或由 LLM 返回的图片、音频等,
     请使用 `langchain.messages` 中的内容块 content blocks。
 """

 # id 字段目前是可选的。
 # 在未来的某个主版本中,当足够多的 VectorStore 实现
 # 都采用它之后,它很可能会变成必填字段。
 id: str | None = Field(default=None, coerce_numbers_to_str=True)
 """文档的可选标识符。

 理想情况下,它应该在整个文档集合中保持唯一,
 并且格式最好是 UUID,但这一点不会被强制要求。
 """
 metadata: dict = Field(default_factory=dict)
 """与内容关联的任意元数据。"""