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
# 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)[Document(metadata={'source': '../asset/load/01-langchain-utf-8.txt'},
page_content='LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮
助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模
块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务')]Documment对象中有两个重要的属性:
page_content:真正的文档内容,字符串类型。
metadata:文档内容的原数据,字典类型。
print(type(docs[0])) #langchain_core.documents.base.Documentprint(docs[0].page_content)
'''
LangChain 是一个用于开发由大型语言模型 (LLMs) 驱动的应用程序的框架。LangChain简化了LLM
应用程序生命周期的每个阶段。\nLangChain 已经成为了我们每一个大模型开发工程师的标配。
'''print(docs[0].metadata) # {'source': './data/langchain.txt'}2.2.2 加载CSV
举例:加载csv所有列
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 ...[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 Doe2.2.3 加载JSON
LangChain提供的JSON格式的文档加载器是JSONLoader 。在实际应用场景中,JSON格式的数据占有很大比例,而且JSON的形式也是多样的。我们需要特别关注。
JSONLoader 使用指定的 jq结构来解析 JSON 文件。jq是一个轻量级的命令行 JSON 处理器 ,可以对JSON 格式的数据进行各种复杂的处理,包括数据过滤、映射、减少和转换,是处理 JSON 数据的首选工具之一。
# 在requirements_full.txt中已经安装
pip install jq常见 jq schema 参考:
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文档加载器加载
# 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)[
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文件中指定的文本
# 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)[
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,先安装
# 在requirements_full.txt中已经安装
pip install pypdffrom 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。可以从本地批量上传文件进行解析,并接收解析结果。
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}" 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文件中提供:
#MinerU的API_TOKEN
MINERU_API_TOKEN=<你的API TOKEN>2.2.5 加载word
可使用 UnstructuredWordDocumentLoader加载 Word 文件,需要 unstructured 包。(已在requirements_full.txt文件中安装)
举例:
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文件
# 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)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" 轻松保持这种分离。
# 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)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(了解)
举例:
# 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(了解)
除了上述的单个文件加载,我们也可以批量加载一个文件夹内的所有文件。
举例:
# 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)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方法。
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 允许用户与文档的内容进行交互,可以查看文档内容。
其继承体系如下
Serializable
↑
BaseMedia
├── id
├── metadata
↑
Document
├── page_content
├── type = "Document"Document源码:
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源码:
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)
"""与内容关联的任意元数据。"""