文档加载器 Document Loader
文档加载器
一个demo
python
from langchain_community.document_loaders import TextLoader
DOC_PATH = "./docs/test.txt"
loader = TextLoader(DOC_PATH, encoding="utf-8")
# 读取全文
docs = loader.load()
for doc in docs:
print("doc:",doc)
1 为什么是 list 而不是单个字符串
loader.load() 返回的是 List[Document] ,原因有三层:
① 一个文件可能拆成多个"文档块"
text
data/docs/test.txt (1个文件)
↓ 加载
[Document(第1段), Document(第2段), Document(第3段)] (N个Document)
比如:
- PDF :每一页可以是一个
Document - CSV :每一行可以是一个
Document - Markdown :按标题层级切分成多个
Document - 大 txt:按 chunk size 切分
② Document 不是字符串,是带元数据的对象
python
Document(
page_content="这是正文内容...", # 真正的文本
metadata={ # 附加信息
"source": "data/docs/test.txt",
"page": 3,
"file_type": ".txt"
}
)
如果直接返回字符串,你就丢失了 source、page 这些溯源信息,做 RAG 时没法引用来源。
③ 统一接口:不管什么格式,输出结构一致
| 输入 | 输出 |
|---|---|
| 1 个 txt | [Document, Document, ...] |
| 1 个 pdf | [Document(page1), Document(page2), ...] |
| 1 个 csv | [Document(row1), Document(row2), ...] |
| 1 个文件夹 | [所有文件的Document...] |
用统一的 list 结构,下游代码不用关心源文件是什么格式。
2 docs 里到底是什么
打印出来大概长这样:
python
[
Document(page_content="第一段内容...", metadata={"source": "test.txt"}),
Document(page_content="第二段内容...", metadata={"source": "test.txt"}),
]
注意:Document 对象本身不是字符串,但可以转成字符串:
python
# 提取全部文本
full_text = "\n".join(doc.page_content for doc in docs)
# 看第一个文档
print(docs[0].page_content)
print(docs[0].metadata)
3 关键区分
| 你以为的 | 实际的 |
|---|---|
docs = 整个文件的内容字符串 |
docs = 一堆 Document 对象的列表 |
| 1 个文件 → 1 个结果 | 1 个文件 → N 个 Document |
| 内容就是纯文本 | 内容 + 元数据 |
4 为什么这么设计(RAG 场景)
做检索增强生成时:
python
# 用户问:"test.txt 第3页讲了什么?"
# 系统需要:定位到具体 chunk + 知道它来自哪
for doc in docs:
if doc.metadata.get("page") == 3:
answer_context = doc.page_content # 精确取用
source = doc.metadata["source"] # 精确溯源
如果 docs 只是一个巨大字符串,你没法定位、没法溯源、没法按块喂给 LLM。
5 为什么 TextLoader 加载的文档数量是 1
loader.load() 的行为取决于用的哪个 loader:
text
拿到一个文件
│
├─ 是 PDF? → load() 直接给 N 页 → N 个 Document
├─ 是 CSV? → load() 直接给 N 行 → N 个 Document
├─ 是目录? → load() 给 N 文件 → N 个 Document
│
└─ 是 txt / md / 纯文本? → load() → 1 个 Document(整篇)
注意:切分阶段会产生多个Document,以上只讨论加载阶段的情况。