用 LangChain 搭 AI PDF 聊天机器人 2026:30 分钟搞定
一个 PDF 聊天机器人就是四步管道:加载、切分、向量化、检索回答。这篇给一份能直接跑的完整代码,说明每个参数为什么这么设,然后讲清楚它会怎么坏、怎么修。
用 LangChain 搭 AI PDF 聊天机器人 2026:30 分钟搞定
把一份 PDF 变成能对话的问答机器人,听起来复杂,拆开只有四步:把文档读进来、切成小块、变成向量存起来、提问时先找出相关片段再让模型回答。整条管道不到一百行代码。
这份教程给的是能直接跑的最小实现,跑通之后再谈优化。
一、先装依赖
pip install langchain langchain-community langchain-chroma langchain-openai langchain-text-splitters pypdf
| 包 | 干什么 |
|---|---|
langchain |
主包与链式组合 |
langchain-community |
文档加载器集合 |
pypdf |
读 PDF 文本 |
langchain-chroma |
本地向量库,零配置 |
langchain-openai |
模型与嵌入接口 |
模型不必是 OpenAI。DeepSeek、通义、本地 Ollama 都提供 OpenAI 兼容接口,改 base_url 和 api_key 就行。
二、四步管道,一次写完
import os
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
os.environ["OPENAI_API_KEY"] = "你的key"
# 1. 加载
docs = PyPDFLoader("handbook.pdf").load()
# 2. 切分
splitter = RecursiveCharacterTextSplitter(
chunk_size=800, chunk_overlap=120,
add_start_index=True,
)
chunks = splitter.split_documents(docs)
# 3. 向量化并持久化
store = Chroma.from_documents(
chunks, OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./chroma_db",
)
retriever = store.as_retriever(search_kwargs={"k": 4})
# 4. 检索 + 回答
prompt = ChatPromptTemplate.from_messages([
("system", "只依据给出的上下文回答。上下文里没有就直说不知道,不要推测。"),
("human", "上下文:\n{context}\n\n问题:{question}"),
])
def ask(question: str) -> str:
hits = retriever.invoke(question)
context = "\n\n".join(
f"[第{h.metadata.get('page', '?') + 1}页] {h.page_content}" for h in hits
)
chain = prompt | ChatOpenAI(model="gpt-4o-mini", temperature=0) | StrOutputParser()
return chain.invoke({"context": context, "question": question})
print(ask("这本书讲了哪三个核心方法?"))
第一次跑会建索引,之后只读 ./chroma_db,不用重复嵌入。
三、每个参数为什么这么设
| 参数 | 建议值 | 理由 |
|---|---|---|
chunk_size |
500~1000 字符 | 太小丢上下文,太大检索不准还占提示词 |
chunk_overlap |
10%~20% | 防止一段话被从中间切断,两边各留一点 |
k |
3~5 | 再多就是往提示词里灌噪音,成本也涨 |
temperature |
0 | 事实问答要确定性,不要创作性 |
text-embedding-3-small |
起步够用 | 便宜、快;对精度要求高再换大的 |
add_start_index=True 会记录每个块在原文中的位置,需要引用页码时用得上。
四、回答不准怎么修
这是最容易出问题的地方,按顺序排查:
- 召回到了吗? 先把
retriever.invoke(问题)的结果打出来看。如果正确段落压根没被检索到,问题在切分或嵌入,不在模型。 - 切碎了吗? 表格、公式、多栏排版被切成碎片是常态。换
chunk_size或者改用按标题切分。 - 上下文够吗? 答案跨越多页时,单块检索必然丢信息。加句子窗口检索(检索小块,喂给模型时带上前后文)。
- 提示词约束了吗? 一定要写"没有就说不知道"。不写,模型会拿自己的知识补,答案看着合理但和文档无关。
前三步解决 80% 的问题,第四步只是保险。
五、从这里往上加什么
跑通之后,实际项目通常会按这个顺序加功能:
| 需求 | 加什么 |
|---|---|
| 记得上下文 | 把历史消息拼进提示词,或换 LangGraph 管理状态 |
| 多份文档分开管 | Chroma 里加 collection,按来源过滤 |
| 回答要给出处 | 提示词要求引用页码,前端渲染成可点链接 |
| 速度慢 | 缓存嵌入结果,或换本地嵌入模型 |
| 要给别人用 | FastAPI 包一层 /ask 接口,前面加个简单页面 |
FastAPI 那层大概二十行:定义请求体、调用 ask()、返回 JSON,再加个跨域配置。别一开始就上,先把问答质量调对。
六、几个真实的坑
扫描版 PDF 读不出字:pypdf 只能提取文本层,扫描件需要先 OCR(pytesseract 或 OCRmyPDF)。先跑一遍看 len(docs[0].page_content),接近 0 就是这个问题。
中文切分效果差:默认分隔符偏英文。中文场景把分隔符写成 ["\n\n", "\n", "。", "!", "?", ";", ",", ""],让它在句子边界断开。
表格全乱:PDF 表格转文本会丢结构。要么用专门的表格解析(如 pdfplumber),要么把表格单独转成 Markdown 再入库。
成本比想象高:每次提问都要嵌入一次问题(很便宜),但一次建库要嵌入全部块。几百页的文档,先算一下块数量再决定用哪档嵌入模型。
换了模型,旧索引还在用旧向量:换了嵌入模型必须删掉 ./chroma_db 重建,否则新旧向量不在一个空间,检索结果会莫名其妙。
常见问题
Q:一定要 Chroma 吗?
A:不必。FAISS、Qdrant、pgvector 都可以,as_retriever() 接口一样。文档几千页以上建议换 Qdrant 或 pgvector。
Q:能同时问多个 PDF 吗?
A:把所有 PDF 的 load() 结果拼成一个列表再切分即可,元数据里的文件名会保留,方便按来源过滤。
Q:回答能完全准确吗? A:不能。RAG 做到的是"答案有据可查",不是"答案绝对正确"。给出处、要求引用原文,是提高可信度最有效的两个手段。
Q:没有 OpenAI 账号怎么办?
A:本地 Ollama 就能跑:ChatOpenAI(base_url="http://localhost:11434/v1", api_key="ollama", model="qwen2.5:7b"),嵌入可以用 OllamaEmbeddings。质量会降一档,但完全离线。
相关文章
❓ 常见问题
Why LangChain instead of building raw?
Components handle the fiddly parts (splitting, retrieval, prompt assembly); raw code is educational, LangChain is productive.
Which embedding model?
text-embedding-3-small is cheap and good enough; local users can use Ollama embeddings.
Does this work with scanned PDFs?
No - PyPDFLoader needs text; add OCR (pytesseract) for scans.
How accurate is it?
Depends on chunk quality and the LLM; the k=4 retrieval with source citations is the accuracy lever.
本站文章由编辑人工撰写,收录的工具均经过实测或公开资料核验。文中链接指向工具官网或 GitHub 仓库,仅作信息参考,不构成付费推广。