一、什么是Doument?
Document 是 LangChain 统一的文档标准载体。 简单理解:把一段文本 + 附属信息打包成一个标准化对象,整个 LangChain 生态(加载器、文本分割、向量库、检索、RAG)全部统一使用这个格式。
二、两大核心属性
三、核心作用(RAG 流程里至关重要)
1. 统一标准,打通各个组件
文件加载器(TextLoader/PyPDFLoader)输出 List[Document]
↓
文本分割器接收 List[Document],输出切分后的 List[Document]
↓
向量数据库接收 List[Document],存入向量库
如果没有 Document,文本只是零散字符串,文件来源、页码信息很容易丢失。
2. 保护溯源信息(metadata)
举个场景:用户提问,向量库检索到一段相关文本。 只拿到文本,你不知道这段文字来自哪个文件第几页; 依靠 metadata,你可以在回答末尾输出。
3. 支持检索过滤
存入向量库后,可以基于 metadata 做筛选: 只检索某一类文件、某个时间段文档。
四、加载器及文档切割的简单应用
1、实例代码:TextLoader 加载.txt和.md文件
from langchain_community.document_loaders import TextLoader
loader = TextLoader(
file_path="data/text.txt", # .txt和.md文件均可
encoding="utf-8",
)
documents = loader.load()
print(f"文档数量:{len(documents)}")
print(f"文档内容:\\n{documents[0].page_content}")
print(f"元数据:{documents[0].metadata}")
2、实例代码:PyPDFLoader 加载.pdf文件
from langchain_community.document_loaders import PyPDFLoader
loader=PyPDFLoader(
file_path="data/text.pdf"
)
documents=loader.load()
print("切割成了几个文档:",len(documents))
# 文档的内容
for document in documents:
print(document.page_content[-20:])
# 文档的元数据
print(document.metadata)
print("当前页码:",document.metadata['page'])
五、文本分割器RecursiveCharacterTextSplitter递归字符文本分割器
1、是什么?
它的核心作用:按优先级递归使用分隔符切割文本
默认分隔符优先级从高到低: \\n\\n(段落) → \\n(换行) → (空格) → (空字符)
可自定义,区别于普通字符分割器:不会粗暴一刀切,尽可能保证语义完整性。
2、作用?
RAG 中必不可少的一环,用来把长 Document 拆成多个小块 Document。
为什么必须分割?
3、核心参数(常用)
文本切分最重要的两个参数是:
|
参数 |
作用 |
|
chunk_size |
每个文档块允许的最大长度 |
|
chunk_overlap |
相邻文档块之间重复保留的长度 |
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=300, # 每个文本块最大字符长度
chunk_overlap=50, # 相邻块重叠字符数量
length_function=len, # 用什么统计长度,默认统计字符数
separators=["\\n\\n", "\\n", " ", ""] # 自定义分隔符优先级
)
chunk_overlap 重叠作用:防止段落被切断、上下文丢失,保证块之间信息连贯。
4、简单实例应用
from idlelib.iomenu import encoding
from pathlib import Path
from langchain_community.document_loaders import TextLoader, PyPDFLoader
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
def split_documents(ducoments:list[Document])->list[Document]:
splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=30,
add_start_index=True, # 每一个大文档的起始位置,假如一个文件被切割成了多个document,它指的是一个document
separators=["\\n\\n", "\\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = splitter.split_documents(ducoments)
for index,chunk in enumerate(chunks,start=1):
chunk.metadata['chunk_id']=index
return chunks
def load_knowledge_base(dictory)->list[Document]:
all_documents=[]
for f in dictory.rglob("*"):
if f.is_dir():
continue
file_suffix=f.suffix
file_path=dictory.name+"/"+f.name
if file_suffix in ['.txt','.md']:
loader=TextLoader(
file_path=file_path,
encoding="utf-8"
)
docs=loader.load()
for document in docs:
document.metadata["file_name"] = file_path
document.metadata["file_type"] = file_suffix
all_documents.extend(docs)
elif file_suffix=='.pdf':
loader=PyPDFLoader(
file_path=file_path
)
docs = loader.load()
for document in docs:
document.metadata["file_name"] = file_path
document.metadata["file_type"] = file_suffix
all_documents.extend(docs)
return all_documents
def main():
path=Path("data")
# 先将文档切分
documents=load_knowledge_base(path)
print("文档切分的个数:",len(documents))
#再将文档继续切分
chunks=split_documents(documents)
for chunk in chunks:
print("~"*30)
metadata=chunk.metadata
print(f"当前是第{metadata['chunk_id']}片段,内容是{chunk.page_content[:20]}")
print("当前的片段来自于"+chunk.metadata.get("file_name"))
if __name__ == '__main__':
main()






