用 LangChain 搭 AI PDF 聊天机器人 2026:30 分钟搞定

🤖 智能体 2026-08-14 · 更新于 2026-11-05 约 7 分钟阅读

一个 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 会记录每个块在原文中的位置,需要引用页码时用得上。

四、回答不准怎么修

这是最容易出问题的地方,按顺序排查:

  1. 召回到了吗? 先把 retriever.invoke(问题) 的结果打出来看。如果正确段落压根没被检索到,问题在切分或嵌入,不在模型。
  2. 切碎了吗? 表格、公式、多栏排版被切成碎片是常态。换 chunk_size 或者改用按标题切分。
  3. 上下文够吗? 答案跨越多页时,单块检索必然丢信息。加句子窗口检索(检索小块,喂给模型时带上前后文)。
  4. 提示词约束了吗? 一定要写"没有就说不知道"。不写,模型会拿自己的知识补,答案看着合理但和文档无关。

前三步解决 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。质量会降一档,但完全离线。

相关文章

延伸阅读 · 含推广链接

API 账单分散在几家平台时,续费和管理都费神。合到一个账号下,成本和额度一眼看得清。

合并到一个账号 →

❓ 常见问题

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.

相关文章
2026-07-19
AI Agent上下文管理:让Agent记住重要的事
2026-07-16
AI Agent数据隐私合规:你的Agent可能在违规
2026-07-19
AI Agent vs 传统自动化:区别在哪,什么时候用哪个
2026-08-02
LlamaIndex 2026:文档Agent与RAG管道详解
2026-07-16
2026年AI Agent项目GitHub Stars排行榜:最受欢迎的开源项目
2026-07-17
AI Agent配置管理:改配置不改代码

本站文章由编辑人工撰写,收录的工具均经过实测或公开资料核验。文中链接指向工具官网或 GitHub 仓库,仅作信息参考,不构成付费推广。

💬 评论 (0)

暂无评论,来说两句吧~

登录后评论