说实话,这两年“AI 知识库”这个词几乎被聊烂了,好像不提 RAG 就不是搞 AI 的。但真上手你会发现,多数教程要么贴一段 LangChain 代码让你自己跑,要么扔给你一个 Dify 让你点按钮,卡在“知道概念但做不出来”和“做出来了但效果稀烂”之间的朋友,占绝大多数。
我最初入坑时也是从“手头一堆 PDF 文档不知道怎么喂给 AI”开始的。当时市面上还没有现在这么多可视化工具,踩了大半个月的坑才把从 PDF 解析、文本切片、向量化、检索到最终问答的整条链路跑通。这篇就把这条路线从头捋一遍,尽量按零基础能复现的标准来写,适合刚接触 RAG、被各种名词绕晕、准备给团队做内部知识库的读者。
1. 入门之前,先把 RAG 的思路搞明白
很多人一上来就问“RAG 用什么框架”,我建议先别急。框架只是最后一步的搬运工,你连“为什么要用 RAG”和“RAG 到底分几步”都没谱的话,后面每一步做出来都是歪的。
1.1 RAG 到底解决什么问题
RAG 全称是 Retrieval-Augmented Generation,检索增强生成。拆开看就是“先把知识找出来,再让大模型看着答案说”。普通聊天模型的知识停留在训练截止日期那一刻,今天你刚拿到的产品手册、公司制度、技术文档,它一概不知道。过去很多团队为了让它“知道”,会去微调模型,贵且慢,改一次文档就要重新训练一次,性价比极低。
RAG 的思路则是把知识外置。你处理好的文档切片被存进向量数据库,用户提问时系统先去库里检索最相关的几段文本,把它们拼进提示词,再交给大模型做总结回答。模型不需要“背”你的文档,它只负责“顺着你给的资料说话”。这样既能回答新知识,又不用重训模型,换文档只需要更新知识库。
这个“先检索、后生成”的模式,就是整套 AI 知识库的地基。后面所有工作——PDF 解析、切片、Embedding、向量库选型、检索策略——都是在为“检索更准、生成更稳”服务。
1.2 RAG 与微调的分工,别一开始就想歪
零基础最常见的一个误区,是想用微调解决知识缺失。我见过有人把上百页 PDF 丢给模型微调,结果既没学会内容,还出现了幻觉。为什么?微调适合纠正模型的说话方式、输出格式、特定风格,不适合喂新事实。模型参数大小有限,塞不下你几千页的规章制度,即使硬塞进去也会遗忘,这就是所谓“灾难性遗忘”。
RAG 的定位是“外部记忆”,微调的定位是“行为调教”,两者是互补关系。入门阶段,除非你要让模型变成特定角色去对话,否则一律先做 RAG,等 RAG 效果稳定后再考虑是否用微调做辅助。
2. 第一关:处理 PDF 是最容易被低估的环节
标题里专门点出“从上传 PDF 开始”,是因为 PDF 解析是整套 RAG 链路里最脏最累、也最影响最终效果的环节。很多人把 PDF 拖进知识库就完事,结果回答时模型东拼西凑、找错页码,根本原因就是源头解析坏了。
2.1 PDF 远没有你想象的那么好解析
PDF 本质上是一个“版面描述格式”,它记录的是每个字符、每条线、每张图放在页面什么位置,而不是像 Word 或 Markdown 那样有清晰的段落结构。这意味着“把 PDF 里的文字提取出来”这件事,本身就不是一行代码能搞定的。
实际工作中 PDF 大致分两类。第一类是文本型 PDF,文字可以直接选中复制,通常是 Word 打印或 LaTeX 生成的;第二类是扫描件 PDF,本质就是一张张图片,文字是像素不是字符,必须靠 OCR 识别。这两种的处理路径完全不同。
我见过最坑的情况是,一份 PDF 看起来是文本型,但某些页是扫描合成的,提取出来的内容一半正常一半乱码。所以第一步永远是“抽几页试试水”,别拿整份文件直接灌进流水线。
2.2 文本型 PDF 的解析工具与取舍
如果是文本型 PDF,Python 生态里常见的工具是 PyMuPDF(fitz)、pdfplumber、pypdf 三件套。直接说结论:通用场景首选 PyMuPDF,速度快,对中文支持也算稳定;需要保留表格信息的场景用 pdfplumber,它能识别单元格和行列结构。
import fitz # PyMuPDF def extract_text_from_pdf(pdf_path): doc = fitz.open(pdf_path) full_text = [] for page in doc: full_text.append(page.get_text()) return "\n".join(full_text) text = extract_text_from_pdf("员工手册.pdf") print(text[:500])这段代码跑通之后,要留意 PyMuPDF 提取到的文本是按页面输出的,版式复杂的 PDF 可能会出现段落顺序错乱。比如两栏排版的文档,提取后左侧一栏和右侧一栏会交错在一起,先左后右还好,遇到多栏错位就只能换 pdfplumber 或下一步按块级元素处理。
实操的时候我一般还会做一步“清洗”:把所有全角/半角空格统一、把连续换行压缩成段落分隔符、剔除页眉页脚。页眉页脚是检索时的大毒瘤,如果知识库里 200 个文档都带着“第 x 页 共 y 页”和公司名,检索结果相关性会被这些噪声带偏。
2.3 扫描件和表格图怎么办
扫描件必须进 OCR。现在开源方案里 PaddleOCR 算是效果最好的之一,CPU 也能跑,只是慢。如果文件量大,我建议优先用 PaddleOCR 的 PP-Structure 系列,它能输出版面分析结果,识别标题、表格、正文区域,比单纯抽文字强很多。
表格是个特殊存在。纯文本提取的表格会变成一行行无结构文字,模型看着费劲,检索质量也差。有两个处理思路:一是用 pdfplumber 提取表格结构后保留为 Markdown 表格格式,喂给模型时让它直接读;二是把表格区域截图保存为图片,依赖多模态大模型去读。前一种方案兼容性好,后一种方案在表格复杂时效果更好。
这里提醒一句:解析环节千万别只做一次。我在做公司知识库时,不同部门交上来的 PDF 模板五花八门,有 PPT 导出的,有 CAD 图纸打印的,有高拍仪扫描的,没有一个统一解析策略能通吃。稳妥的做法是先抽样 20 份不同类型的文档人工检查解析结果,再决定按哪条处理路径走。
3. 知识库的核心三件套:切片、向量化与检索
PDF 处理完,你得到了一批干净文本。接下来要把这批文本变成可供大模型使用的“知识单元”。这个阶段的核心操作可以拆成三步:切片、Embedding 向量化、写入向量库并检索。
3.1 切片策略:决定召回质量的第一块多米诺骨牌
切片就是把长文本裁成小块。很多人觉得这步无所谓,随便按 500 字切一刀就行。但切片大小和策略直接决定检索的召回准确率。
先说一个反直觉的结论:切片不是越小越好。零基础的朋友常以为块越小、语义越精确,实际上如果切得太碎,一个问题对应的信息被拆成好几块,检索只召回其中一块,模型看到的上下文就缺了一半,回答自然残缺。切片太大则带来另一个问题,比如一整章内容塞进一个块,里面三分之一是废话,Embedding 向量会被噪声稀释,相关性得分被拉低,而模型处理超长文本的 token 成本也高。
我常用的切片策略分两种。一种是把按标题层级分割的章节直接作为切片单元,因为自然段落天然语义完整;另一种是固定大小加重叠窗口,比如每块 500 个字符、重叠 100 个字符,保证跨块语义不割裂。Dify 这类可视化工具里有一个 “分段标识符”设置,默认是 \n\n,你可以额外指定 ## 或 第X章 这类标题前缀,让系统优先按标题切。
一个具体建议:技术文档、规章制度这类结构清晰的文档,优先按标题切;对话记录、新闻资讯这类分段明确的,按段落切;没有明显边界的杂文,再退回到固定大小切片。
3.2 Embedding 模型怎么选
切片完成之后,每块文本要被转化为一串浮点数,也就是向量。这个转化动作依赖 Embedding 模型,它决定了“语义上的相似”能不能被计算出来。
面向中文场景,选择 Embedding 模型要考虑三个问题:中文效果、部署成本、和你的主模型兼容性。如果用国外闭源 API,OpenAI 的 text-embedding-3-small 对中文支持尚可,但部分垂直行业术语识别一般;如果追求中文效果,BGE 系列(BAAI/bge-large-zh-v1.5)、M3E(moka-ai/m3e-large)都是开源选择,效果不输商业 API,还可以本地部署。
我这段时间做本地知识库,用的是 Ollama 拉取 bge-m3 模型,操作很简单,一行命令就能把模型跑起来,通过 HTTP 接口调用即可。这个方案特别适合零基础读者:不用去申请各种 API key,不用处理支付,装个 Ollama 就能全链路跑通。
需要强调:主 LLM 和 Embedding 模型是两个不同的模型,别混为一谈。我在项目里常看到新手用一个模型既做对话又做向量化,虽然能跑,但检索效果往往打折扣,因为对话模型和向量模型的训练目标完全不同。
3.3 向量库与检索:语义检索不是万能的
向量化完成后,这些向量会写入向量数据库。常见的开源选择有 Chroma(轻量级,适合入门)、Qdrant(功能全面)、Milvus(适合大规模生产)、以及关系型数据库里的 pgvector(适合已有 PostgreSQL 的场景)。
零基础入门阶段,我建议直接用 Chroma,它的 API 简单到可以当 Python 库用,安装即可启动,不需要单独部署服务。
import chromadb client = chromadb.PersistentClient(path="./kb_store") collection = client.get_or_create_collection("my_manual") collection.add( ids=["chunk_001", "chunk_002"], embeddings=[[0.1, 0.2, ...], [0.3, 0.4, ...]], # 你算出的向量 documents=["切片1的内容", "切片2的内容"], metadatas=[{"source": "员工手册.pdf"}, {"source": "员工手册.pdf"}] )检索阶段最常见的问题是“语义检索不是万能的”。Embedding 模型理解语义相似,但它是把整句话压缩成一个向量,关键词精确匹配能力很弱。比如用户问“2024 年报销流程”,如果知识库里写的是“费用报销的审批步骤”,语义上能检索到;但如果用户问的是特定编号“XG-2024-01”,语义向量就很难精确命中,因为向量模型更擅长理解“意思”而非“字符”。
所以真正靠谱的知识库,通常会在语义检索之外加一层关键词检索,把精确匹配和语义匹配结果做一个加权融合。Dify 里的 “混合检索” 选项就是这个原理。这也是为什么我特别强调别一上来就迷信“大模型什么都能理解”,混合检索是实现可用知识库的关键细节。
4. 两条实操路线:Dify 图形化 vs 本地代码
理解了原理之后,就看你要走哪条落地路线了。我给零基础读者的建议非常直接:如果你只想快速做出一个能用的知识库,先用 Dify;如果你是想真正理解内部原理,再亲手写一套本地 RAG。两条路我都走过,各自优缺点下面说透。
4.1 路线一:用 Dify 在网页上搭一条完整流水线
Dify 是一个开源的大模型应用开发平台,它把知识库、Prompt 编排、模型管理、应用发布全部做成了可视化界面。你不需要写一行代码,只要按顺序点击配置,就能完成从上传 PDF 到问答应用的全流程。
操作步骤如下:
- 部署 Dify(可以用 Docker Compose 一键拉起),进入工作台。
- 创建“知识库”,新建数据集,把 PDF 拖进去。
- 设置分段标识符和最大分段长度,Dify 会自动完成切片。
- 配置 Embedding 模型,这一步选择你上一步定的模型。
- 索引完成后,创建一个“聊天助手”类型的应用,关联刚才的知识库。
- 在应用设置里选好生成模型,开启“引用和归属”功能,提问测试。
这个路线最大的价值是让你在十分钟内看到完整 RAG 效果。而且 Dify 的默认参数已经比较合理,适合零基础跑通第一版。我用它给一个团队做过内部制度问答,从上传文档到上线只花了一个下午,速度非常可观。
提示:Dify 知识库建立后,底层会自动创建索引任务,如果文档较多,排队等待是正常现象,不用反复刷新或重复上传。
4.2 路线二:Python + Ollama 本地搭建最小可运行 RAG
如果你是开发背景,或者想彻底摸清每个环节,我强烈建议走一遍本地代码路线。不用 LangChain 全家桶,用几段核心代码把原理串起来,效果最直观。
整个流程可以拆成四段代码:PDF 解析(上一章讲过)、切片、向量化、检索问答。
切片后,用 Ollama 的 Embedding 接口把每个切片转成向量:
ollama pull bge-m3import requests import json def embed_text(text): resp = requests.post( "http://localhost:11434/api/embeddings", json={"model": "bge-m3", "prompt": text} ) return resp.json()["embedding"] chunk_vectors = [embed_text(chunk) for chunk in chunks]然后存进 Chroma,查的时候同样把问题转成向量,再取最相似的几个切片拼接到提示词里:
from openai import OpenAI client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama") def ask_question(question): q_vec = embed_text(question) results = collection.query(query_embeddings=[q_vec], n_results=3) context = "\n\n".join(results["documents"][0]) response = client.chat.completions.create( model="qwen2.5:7b", messages=[ {"role": "system", "content": "你只能依据提供的资料回答,资料中没有的内容要明确说不知道。"}, {"role": "user", "content": f"资料如下:\n{context}\n\n问题:{question}"} ] ) return response.choices[0].message.content我在本地用 qwen2.5:7b 加 bge-m3 跑过几百条真实问答,回答质量足以应对一般内部文档查询。这套方案的全部依赖就是 Ollama 和两份 Python 脚本,非常适合零基础当作“毕业项目”来练手。
4.3 图片能不能进 RAG 知识库
很多人会问“RAG 知识库能存储图片吗”。直接说结论:常规 RAG 流程本身处理不了图片,但可以通过两种方式绕过去。
第一种是图片文字化。如果图片里主要是文字信息(比如截图、扫描件),先让 OCR 识别成文本,再进知识库;第二种是多模态化。把图片按路径保存进知识库的元数据里,问答时先检索到对应文本,再把图片路径丢给支持视觉的大模型去理解。
我见过一个做“产品宣传图知识库”的方案,就是先把图片说明文字做索引,检索结果附带图片 URL,前端把它显示出来。这种方式用户感知上就是“知识库能查图片”,实际后端仍是文本检索为主。总之,纯图库不适合用 RAG 做,RAG 的核心始终是文本语义。
5. 踩坑排查与实测经验
这条路线看起来清晰,但实际执行中会冒出一堆零散问题。我把这几个月里反复踩过、也帮朋友排查过的坑集中列出来,按频率排序。
5.1 PDF 解析乱码和丢内容的排查
症状是知识库里有文档,但问答时引用内容驴唇不对马嘴,或者干脆搜不到。这类问题 80% 出在 PDF 解析阶段。
排查顺序很重要。第一,先手动打开 PDF,确认是否扫描件——扫描件没 OCR 直接进知识库,等于让知识库“读书却不识字”。第二,用代码提取一段文本,肉眼检查是否有大量空格、换行错乱、乱码。第三,查看页眉页脚是否混入了正文切片,如果有,在预处理里加排除规则。
我之前遇到过一个特殊案例:某文档用特殊字体嵌入,PyMuPDF 提取正常,但单独提取某些字符时顺序错乱。后来发现需要按“字符在页面上的坐标”排序,而不是按 PDF 内部顺序输出。这个场景用 pdfplumber 的 extract_words 然后按坐标排序,就能解决。
5.2 检索召回差、答非所问的排查
如果解析没问题,但模型总答非所问,问题多数出在检索环节。我排查的思路如下:
- 先看召回结果。把用户问题喂进去,看看被检索到的 top3 切片内容是否相关。如果不相关,就是 Embedding 或切片出问题了;如果相关但回答不对,就是提示词拼写或模型理解的问题。
- 再调整切片长度。切片太长会让向量特征被稀释,太短会让信息碎片化,试着把长度从 1000 字符降到 500 字符对比效果。
- 最后考虑混合检索。如果专有名词、编号、型号出现频率高,一定要开关键词权重,Dify 里勾选混合检索,本地代码就加一层 BM25 检索做融合。
5.3 关于 Dify 知识库排队中的亲历记录
在线编辑场景下,Dify 上传文档后偶尔会卡在“排队中”状态。遇到这个情况先别慌,看两处:一是是否开启了索引模式但 Embedding 模型 API 限流,如果限流就稍等或换模型;二是文档格式是否正常,某些加密 PDF 会导致解析进程挂起。
我实测下来,批量上传大文件时常遇到排队卡住,最终解决办法是一次别传太多,分批上传建立索引。Dify 的索引服务对并发支持一般,大文件建议控制在单份 20MB 以内,太大就先用工具拆分 PDF 再传。
5.4 其他容易被忽略的小坑
- 提示词里忘了约束“只能根据资料回答”。没这句话,模型会放飞自我,编造知识库中不存在的内容。
- 知识库规模很小时,语义检索的得分差异不明显,导致每次召回结果随机性大。解决方法是把 top_k 调大(比如 5~8 个切片),让模型自己筛选有用信息。
- 切片里的重复内容会在召回时被重复引用,白白消耗 token。清洗阶段可以跑一遍简单去重,相同的段落只保留一份。
- RAG 的效果和主模型能力强相关。知识库检索再准,如果主模型是那种只会复述原文的小模型,回答仍然很生硬。预算允许的话,至少选 7B 以上参数量的模型。
6. 给零基础的完整学习路线速查
看完前五章,你脑子里应该已经有一条完整链路了。我用这套方法带过几个零基础入门的朋友,按下面的顺序安排学习,基本两周内都能做出自己的知识库应用。
6.1 按周拆解的学习进度安排
第一周重点在概念和工具手感。先用 Dify 或类似可视化平台跑通“上传 PDF → 提问 → 出答案”的最小闭环。这个阶段不求理解代码,只求建立“知识库原来是这样工作”的整体感知。同时看几篇 RAG 原理文章,把检索、Embedding、向量库这几个名词的基本含义搞清楚。
第二周开始动手写代码。按第五章的方案装 Ollama,把两套模型跑起来,写一个最小的 Python 脚本完成“解析-切片-向量化-检索”的调用。这里不要求写得多规范,能跑就行。跑通后试着换不同切片长度和不同文档,对比问答效果。
第三周可以深入某个环节做优化。比如把 PDF 解析工具从 PyMuPDF 换成 pdfplumber,感受表格提取差异;或者给检索加一个 BM25 混合层,看精确命中是否变好。到这个阶段,你已经不是零基础了,你已经能判断别人的教程写得好不好、自己的知识库问题出在哪。
6.2 一些个人体会
最后说点偏主观的经验。我发现很多零基础的人(包括当初的我)卡住,不是因为智商不够或资料太少,而是因为教程给得太完美。网上那些“三步搭建企业级知识库”的帖子,往往省略了 PDF 解析、切片调参、检索评估这些脏活。实际上,RAG 这个技术本身就是 20% 模型代码加 80% 数据处理,你把 PDF 处理好、切片切对、召回调好,应用效果已经超过大多数半吊子项目。
关于学习材料,我的建议是少看博客多读官方文档。Dify 的文档和 Ollama 的文档都写得不错,跟着做比搬运二手代码强。遇到问题不要急着发帖问,先把报错信息拆开看,大概率是依赖版本冲突或者路径不对。自己排查一次,收获比看十篇文章大得多。
这个方向后续还能扩展的地方很多,比如给知识库加权限管理、做文档版本更新提醒、接入企业微信机器人、用 Agent 串联多个工具。但不管扩展多少,地基始终是那三件事:干净的数据、合理的切片、可靠的检索。把这三件事做扎实,你就已经站在多数人前面了。