LangChain 学习笔记 · Day 8 文档加载器(Loader)
掌握 LangChain 文档加载器的使用方法,包括 PDF/TXT/网页加载,理解 Document 与 metadata 概念,并实现最小基于上下文回答的 Demo。
15 分钟阅读
LangChainPythonLoaderPDFTXTWebDocumentRAG
🎯 学习目标
- 学会使用 LangChain Loader 读取本地 PDF、TXT 和网页内容
- 理解 Document 与 metadata 的作用
- 使用 TextSplitter 切分文档,做好后续向量化准备
- 完成一个最小 Demo:基于上下文回答问题(非完整 RAG)
🔁 核心概念
- Document
LangChain 的统一文档格式:
Document(page_content: str, metadata: dict)page_content: 文本内容metadata: 来源信息(文件路径、页码、URL 等)
- Loader
把外部数据转成
Document对象。常见:PyPDFLoader: 读取文本型 PDFTextLoader: 加载 TXT/Markdown 等纯文本WebBaseLoader: 抓取网页正文
⚙️ 环境准备
pip install --upgrade langchain langchain-community langchain-openai pypdf
pip install bs4 html2text # 可选:网页加载
注意:
PyPDFLoader依赖pypdf,只能读取文本型 PDF。扫描件需要 OCR(见常见问题)。
🧩 示例代码:day8_load_pdf.py
# file: day8_load_pdf.py
import os
from pathlib import Path
from typing import List
from langchain_community.document_loaders import PyPDFLoader, TextLoader, WebBaseLoader
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1) 加载 PDF
def load_pdf(path: str) -> List[Document]:
loader = PyPDFLoader(path)
return loader.load()
# 2) 加载 TXT
def load_txt(path: str) -> List[Document]:
return TextLoader(path, encoding="utf-8").load()
# 3) 加载网页
def load_web(url: str) -> List[Document]:
return WebBaseLoader(url).load()
# 4) 切分
def split_docs(docs: List[Document], chunk_size=1200, chunk_overlap=200) -> List[Document]:
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
add_start_index=True,
)
return splitter.split_documents(docs)
# 5) 最小“基于上下文回答”
def answer_with_context(chunks: List[Document], question: str, model="kimi-k2-0711-preview") -> str:
context = "\n\n".join(d.page_content for d in chunks[:3])
prompt = ChatPromptTemplate.from_messages([
("system", "你是严谨的中文阅读助手。仅基于上下文回答,如果上下文没有答案,请说“根据提供的上下文无法确定”。"),
("user", "问题:{q}\n\n上下文:\n{ctx}")
])
llm = ChatOpenAI(model=model, temperature=0.2)
chain = prompt | llm | StrOutputParser()
return chain.invoke({"q": question, "ctx": context})
if __name__ == "__main__":
pdf_path = "docs/sample.pdf"
assert Path(pdf_path).exists(), f"找不到文件:{pdf_path}"
docs = load_pdf(pdf_path)
print(f"✅ 读取完成:{len(docs)} 页。示例元信息:", docs[0].metadata)
chunks = split_docs(docs)
print(f"✅ 切分完成:{len(chunks)} 个文本块")
question = "这份PDF的主题是什么?"
answer = answer_with_context(chunks, question)
print("\nQ:", question)
print("A:", answer)
📌 常见问题
- PDF 是扫描件/图片?
PyPDFLoader无法识别,需要 OCR:pytesseract + pdf2imageunstructured或其他云 OCR 服务
- 切分参数如何选?
chunk_size=800~1500,chunk_overlap=100~200- 需在检索实验中微调
- 为什么 metadata 重要?
- 用于答案引用出处(页码/标题/URL)
- 后续检索链返回时可标注来源
✅ 今日任务
- 使用
PyPDFLoader读取一份 PDF,输出metadata - 调整切分参数
(1200,200)与(800,120),比较差异 - 用
answer_with_context()提 3 个问题,验证“仅基于上下文回答”效果 - 额外:加载一个 TXT 与一个网页,打印
metadata
🔮 预告 Day 9~14
- Day 9:清洗与切分策略(标题分段、层级结构)
- Day 10:Embedding + 向量库(Chroma)检索
- Day 11:难 PDF(表格、图片 OCR)
- Day 12:RAG 检索链 + 引用标注
- Day 13:多文档融合与重排序
- Day 14:评测与优化(准确性、幻觉率、延迟、成本)