环境准备
1 安装依赖
我们在《第02章-模型调用》章节已经通过requirements.txt 文件安装过课程的依赖。当时考虑到本章 RAG模块涉及的依赖较多且大,所以不在之前的依赖文件中。所以,这里大家需要补充安装RAG涉及到 的依赖。完整版文件见《02-资料\requirements_full.txt》
pip install -r requirements_full.txt
检查冲突
pip check
如果环境安装正确,则日志如下

2 准备数据
将 knowledge.txt 置于项目根目录下
将 asset文件夹 解压后置于项目根目录下
文档加载器 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 对象 。
1 加载txt
from langchain_community.document_loaders import TextLoader
loader = TextLoader(
file_path="../asset/load/01-langchain-utf-8.txt",
encoding="utf-8",
)
docs = loader.load()
print(docs)
Document(metadata={'source': '../asset/load/01-langchain-utf-8.txt'}, page_content='LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务')
Documment对象中有两个重要的属性
-
page_content:真正的文档内容,字符串类型
-
metadata:文档内容的原数据,字典类型
print(type(docs[0]))
print(docs[0].metadata)
print(docs[0].page_content)
2 加载CSV
举例:加载csv所有列
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader(
file_path="../asset/load/02-load.csv",
)
docs = loader.load()
print(docs)
3 加载JSON
LangChain提供的JSON格式的文档加载器是 JSONLoader 。在实际应用场景中,JSON格式的数据占有 很大比例,而且JSON的形式也是多样的。我们需要特别关注。
JSONLoader 使用指定的 jq结构 来解析 JSON 文件。jq是一个轻量级的命令行 JSON 处理器 ,可以对 JSON 格式的数据进行各种复杂的处理,包括数据过滤、映射、减少和转换,是处理 JSON 数据的 首选 工具之一 。
# 在requirements_full.txt中已经安装
pip install jq
常见 jq schema 参考:

详细用法可参考 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=".", #直接提取完整的JSON对象(包括所有字段)
# 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)
举例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)
4 加载pdf
PDF 存在多种来源格式,包括扫描版(图片 PDF)、电子文本版、混合版。并且布局格式也多种多样, 包括单列布局、双列布局甚至竖排文本布局。并且包含段落、标题、页眉页脚、表格、数学公式、化学 式、特殊符号、图片等各种元素
因此,PDF 解析存在很多挑战。对于复杂 PDF,需要进行文本提取、布局检测、表格解析、公式识别等 处理。
LangChain加载PDF文件使用的是pypdf,先安装
# 在requirements_full.txt中已经安装
pip install pypdf
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))
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)
6 加载Markdown
可使用 UnstructuredMarkdownLoader 加载 Markdown 文件,需要 unstructured 包。(已在 requirements_full.txt文件中安装)
# 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(docs)
举例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",
# 解析策略:
# "fast"(快速模式),它会以最快的速度提取文本,不进行复杂的版面分析
# "hi_res" 高分辨率模式
strategy="fast"
)
# 3.加载
docs = md_loader.load()
print(len(docs))
# 4.打印
for doc in docs:
# pprint(doc)
pprint(doc.page_content)
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)
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)
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方法。
from abc import ABC
from typing import List, Optional
from langchain_core.documents import Document
from langchain_text_splitters import TextSplitter, RecursiveCharacterTextSplitter
class BaseLoader(ABC):
"""
文档加载器基类。
作用:
将各种来源的数据加载成 LangChain 的 Document 对象。
设计要求:
子类应该实现 lazy_load(),通过生成器的方式逐个返回 Document,
而不是一次性把所有文档加载到内存中。
注意:
load() 和 load_and_split() 主要是为了方便用户使用,
子类一般不应该重写这两个方法。
"""
def load(self) -> List[Document]:
"""
将数据加载为 Document 对象列表。
实际上调用的是 lazy_load(),
然后通过 list() 一次性将生成器中的所有 Document 转换成列表。
Returns:
List[Document]: 文档对象列表。
"""
return list(self.lazy_load())
async def aload(self) -> list[Document]:
"""
异步加载数据。
是 load() 的异步版本。
Returns:
list[Document]: 文档对象列表。
"""
return [document async for document in self.alazy_load()]
def load_and_split(
self,
text_splitter: Optional[TextSplitter] = None,
) -> List[Document]:
"""
加载文档并将文档切分成多个文本块。
参数:
text_splitter:
文本切分器。
如果没有传入,则默认使用
RecursiveCharacterTextSplitter。
Returns:
List[Document]:
切分后的 Document 对象列表。
注意:
这个方法主要是为了方便使用,
不建议子类重写。
"""
# 如果用户没有传入文本切分器,
# 默认使用 RecursiveCharacterTextSplitter
if text_splitter is None:
_text_splitter = RecursiveCharacterTextSplitter()
else:
_text_splitter = text_splitter
# 第一步:加载原始文档
docs = self.load()
# 第二步:对 Document 进行文本切分
return _text_splitter.split_documents(docs)
BaseLoader 把数据加载成 Documents object ,存到 Documents 类中的 page_content 中。
Document类分析
Document 允许用户与文档的内容进行交互,可以查看文档内容。
其继承体系如下
Serializable
↑
BaseMedia
├── id
├── metadata
↑
Document
├── page_content
├── type = "Document"
from typing import Any, Literal
from langchain_core.documents import BaseMedia
class Document(BaseMedia):
"""
用于存储一段文本以及与文本相关的元数据。
Document 主要用于:
- 文档加载
- 文档切分
- 向量检索
- RAG
注意:
Document 主要用于「检索工作流」,
而不是用于和 LLM 进行聊天时的输入输出。
如果需要给 LLM 发送聊天消息,
应该使用 langchain.messages 中的消息类型。
Example:
from langchain_core.documents import Document
document = Document(
page_content="Hello, world!",
metadata={
"source": "https://example.com"
}
)
"""
# 文档的正文内容
page_content: str
# Document 的类型标识
type: Literal["Document"] = "Document"
def __init__(
self,
page_content: str,
**kwargs: Any,
) -> None:
"""
创建 Document 对象。
参数:
page_content:
文档正文。
**kwargs:
其他字段,例如 metadata、id 等。
例如:
Document(
page_content="Hello",
metadata={"source": "test.txt"}
)
"""
# 调用父类 BaseMedia 的初始化方法。
#
# BaseMedia 基于 Pydantic,
# 会负责字段校验以及对象初始化。
super().__init__(
page_content=page_content,
**kwargs,
)
class BaseMedia:
"""
LangChain 媒体对象的基础类。
"""
@classmethod
def is_lc_serializable(cls) -> bool:
"""
判断当前类是否支持 LangChain 序列化。
Returns:
True:表示可以被 LangChain 序列化。
"""
return True
@classmethod
def get_lc_namespace(cls) -> list[str]:
"""
获取 LangChain 对象的命名空间。
Returns:
LangChain 对象所属的命名空间。
"""
return [
"langchain",
"schema",
"document",
]
def __str__(self) -> str:
"""
将 Document 转换成字符串。
这里只展示:
- page_content
- metadata
不展示其他字段。
"""
# 如果存在 metadata
if self.metadata:
return (
f"page_content='{self.page_content}' "
f"metadata={self.metadata}"
)
# 如果没有 metadata,
# 只显示 page_content
return f"page_content='{self.page_content}'"