很多人第一次接触“大模型知识库”时都会产生一个直观的判断我把 PDF 上传到对话框它也能准确回答文档里的问题这不就是 RAG 吗这个感觉没错但结论偏了。把 PDF 上传给大模型本质上是“文件内容读取 长上下文问答”而 RAG 是“检索增强生成”它的关键在“检索”二字而不是“读取”。你往对话框里丢一份 PDF系统可能只是把全文解析后塞进上下文真正的 RAG 必须在大量文档中先定位出与问题相关的段落再把这些片段交给模型组织答案。前者适合“读一本书”后者适合“翻一个图书馆”。这篇文章会把这件事拆透。我们先说清楚误区是怎么形成的再讲 RAG 的真正原理然后用一个最小可运行的 Python 示例把 PDF 解析、文本分块、向量化索引、检索问答整条链路跑通最后聊一聊 RAG 的指标评测和进阶方向。读完你能明白一个 RAG 项目里最需要投入精力的环节不是“上传 PDF”而是解析、分块和检索质量。1. 先纠正一个判断传PDF不等于RAG1.1 “上传PDF就能问答”为什么容易造成误解近一年大模型应用普遍支持“上传文件”这个动作网盘里、文档工具里、知识库产品里都能把 PDF 拖进去直接提问。表面看用户操作完全一致上传一份 PDF随后提问AI 回答。于是“RAG”这个概念被泛化了很多产品文档也直接把文件问答称作 RAG 知识库。但这里隐藏着一个关键差别从产品体验上你看到的是“上传→提问→回答”从技术实现上至少存在两种完全不同的路径。第一种路径是“长上下文硬读”。系统把 PDF 解析成文本不管文件多大直接拼进提示词让大模型一次性读完再回答。这种方法对几十页以内的资料有效因为模型上下文窗口够大。但是如果文件超过几十万字就很容易出现中间信息被忽略、回答不完整、甚至模型“读漏”的情况。第二种路径才是 RAG。系统不会把整份文件都塞给大模型而是先把整个文档库拆成若干文本块建立索引用户提问后系统先通过检索找到最相关的几个文本块再把这些小块拼进提示词。大模型只在“相关上下文”上作答。也就是说如果产品只是能把 PDF 里的内容复述出来不能证明它用了 RAG。判断 RAG 是否真实存在要看它是否具备“检索后再生成”的结构而不是看它是否支持上传文档。1.2 “传PDF”和“RAG”之间差的到底是什么直接传 PDF 给大模型用户心智里是“我把资料给你了你帮我读”RAG 的心智则是“你有一个文档库我能从中找出和我的问题最匹配的证据再回答问题”。前者弱化了一个重要步骤相关性检索。当你只有一份 PDF、文件内容在模型上下文窗口内时相关性检索看似没必要。但当你切换到企业知识库时问题立刻暴露文档不是一份而是几千份一次上传全部塞入上下文完全不现实用户的提问不会指定“请翻到第 4 页的第三段”而是直接问“这个产品的退款政策是什么”系统需要快速从海量片段里筛出真正相关的证据再把证据交给大模型。这种场景下上传 PDF 只是最外层的入口。真正的 RAG 链路是解析准入 → 清洗切分 → 向量索引 → 检索召回 → 重排融合 → 生成回答。每一步都影响效果。把 PDF 传给大模型只是完成了“获取资料”的动作远没有进入“检索增强”的核心。1.3 如果坚持“传PDF就是RAG”会踩什么坑我见过不少开发者的真实困惑我已经把 PDF 解析好也拼接进提示词了为什么我的“RAG”效果很差原因往往在于他们做的并不是 RAG而是“带上下文的长文本问答”。这类项目在单文件、小文件场景下没问题一旦进入真实业务会出现三个典型坑没有“文档库”的规模感。只有一份 PDF 时顺序拼接也许可行但十份、一百份 PDF 的体量下不检索就无法定位相关信息。没有“文本块”的概念。模型回答质量取决于输入片段是否准确如果不做分块和过滤无关内容会分散模型的注意力。没有“溯源”机制。用户问“依据是什么”系统答不上来因为系统根本没有“召回的是哪一段”这个中间结果。所以这篇文章的第一个建议是不要把所有“文档问答”都叫 RAG也不要以为支持上传 PDF 就等于拥有知识库。RAG 的核心价值不在“读”而在“找”。2. RAG到底解决什么问题深入理解“检索增强生成”2.1 RAG 的准确定义与核心组件RAG 的英文全称是 Retrieval-Augmented Generation直译是“检索增强生成”。它要做的事情可以概括为在模型生成回答之前先从外部知识源中检索相关内容把检索结果作为上下文提供给大模型让生成结果更准确、更新鲜、有依据。从结构上看RAG 链条由两个大模块组成检索模块负责“找资料”。该模块不一定只有向量检索也可以包含关键词搜索、数据库过滤、重排序等。生成模块负责“写答案”。它接收用户问题与检索到的文本片段通过提示词约束模型只依据片段回答。所以RAG 不是一个大模型的新版本也不是 PDF 解析插件。它更像一套“知识管道”把大模型本身不具备的专业资料、私有文档、实时数据输送到模型面前让它基于真实内容作答。2.2 为什么需要 RAG三种典型场景很多人会问大模型上下文窗口不是越来越大吗把文档直接灌进去不就行了这个问题放在一两份 PDF 上是成立的但放到真实系统里不成立。RAG 主要解决三类问题。第一知识时效问题。大模型训练数据有截止时间而企业内部文档、产品手册、法律法规随时更新。RAG 可以把最新资料放在检索库里不重新训练也能让回答保持更新。第二私有知识边界问题。企业的内部制度、合同条款、设备说明书大模型在训练时不可能见过。RAG 相当于一个“知识闸门”让模型只在授权文档范围内回答降低把不相干训练知识混进来的概率。第三可溯源与可控性需求。工作中你会发现答案“看起来对”不够很多场景必须能回看依据比如客服工单、合规审查、医疗法律资料。RAG 链路天然保留“检索到的片段”可以指出答案来自哪份文档的第几段。如果用一句话概括 RAG 的未来定位它不是模型能力的替代品而是模型与真实数据之间的一层“桥梁”。2.3 为什么“上下文硬读”替代不了RAG“上传 PDF → 模型直接读全文”这种实现在工程上被称为“塞上下文”。它在少量文件时可用但存在几个硬伤。从成本看大模型按 token 计费把几千页 PDF 全部塞给模型一次问答消耗的 token 可能非常惊人。从质量看模型处理超长内容时存在注意力稀释问题中间位置的信息容易被忽略从更新看每增加一份新文档都需要重新组织整个上下文无法形成可复用的知识库从权限看对超大文档库做粗暴拼接很容易把不该透露的内容一起带出来。RAG 的价值就在于此它把“问题”和“海量资料”的连接方式从“全量阅读”改成了“按需检索”。无论你有 5 份还是 5 万份文档模型每次只需要看与当前问题最相关的几个片段成本可控准确率也更容易提升。3. PDF解析为什么是第一个坑格式、乱码、扫描件与版式3.1 PDF 不是“可以直接读的文档”而是“版式文件”很多刚入门 RAG 的开发者会把 PDF 当作文本文件处理上来就 read、split、喂模型。这个理解会带来大量问题。PDF 的底层结构不是一段连续的文本流而是由页面对象、字体资源、图形操作、文字定位信息组成的排版容器。简单说PDF 记录的是“哪个字符放在哪个坐标位置”而未必记录“这句话应该按什么顺序读”。它没有稳定的段落结构也不会天然给出标题层级。这导致一个结果当你用程序提取 PDF 文本时得到的经常不是自然的句子而是一堆按坐标顺序排列的文字片段。如果 PDF 是多栏排版提取结果可能先从第一页左栏顶部走到底部再从上到下读右栏甚至同一段落被切成两半。对于双栏论文、产品手册这种顺序错乱会直接污染后续检索。3.2 文本型PDF、扫描件PDF与表格型PDF处理差异根据 PDF 内容生成方式处理难度差异很大。文本型 PDF文字本身可复制例如用 Word、LaTeX 转出的文档提取相对容易。只要页面上没有复杂的表格和分栏pdfplumber等工具能拿到较好的文本。扫描型 PDF本质上是一张张图片没有任何文字层。必须用 OCR 识别或交给支持视觉能力的多模态大模型。OCR 的质量受清晰度、中英文混排、表格线影响识别错误率比普通文本提取高得多。表格型 PDF表面看着整齐提取后却很容易把单元格拆乱。表格上下文中单元格横向纵向关系非常关键如“单价”和“数量”的数据依赖如果简单按行提取检索时就会丢失对应关系。图片型内容热词里经常有人问“Python 提取 PDF 中的图片”这在 RAG 中也很有价值比如设备故障图示、产品截图。提取图片后再走 OCR 或多模态模型属于“多模态 RAG”的范畴。3.3 PDF解析的三个生产级忠告忠告一不要用“转 Word 再解析”代替 PDF 文本提取。Word 转换会重新排版原有的表格结构、页眉页脚、注释都可能在转换中改变之后再做知识库反而引入更多噪音。忠告二警惕页眉页脚、页码、水印对检索的污染。这些内容并不承载可检索语义却会出现在每一页的文本里。不清理时检索结果很可能出现大量重复页眉把真正的正文挤掉。建议在解析后统一过滤页眉页脚噪声。忠告三只处理自己有权使用的文档。PDF 可能包含版权保护内容企业文档也有访问边界。在构建知识库前务必确认授权范围尤其是把解析后的向量索引放到云端或共享存储时去标识化和权限管控要做到前面。4. 一个标准RAG链路是如何运转的解析、分块、向量化、检索、生成4.1 RAG 链路全景从工程视角看基础 RAG 可以拆成五个阶段文档解析把 PDF、Word、网页等格式转为干净文本清洗与分块去除页眉页脚控制文本块大小并保留上下文衔接向量化与索引用 Embedding 模型把文本块转为向量写入向量索引检索召回与重排对用户问题编码找出最接近的若干文本块生成回答把问题和检索结果一起送入大模型约束其用给定材料作答。一个容易被忽视的事实是生成阶段的效果基本取决于前四个阶段的质量。很多团队把时间花在调 Prompt 上却忽略了 PDF 本身没解析好、向量库里的块本身就没法回答问题。这是本末倒置。4.2 为什么解析后还要分块分块是 RAG 项目里最不起眼、却对效果影响最大的环节之一。块太长向量表示会被拉平检索精度下降块太短语义不完整模型即使召回也没法回答分块边界切得不好一个完整知识点被拆成两半检索时两边都找不到正确答案。没有标准答案的分块策略但有几条常见原则与你的文档结构对齐比如按标题、小节、表格边界切分保留一定重叠避免一句话被硬生生截断控制块的长度让 Embedding 模型能够有效编码。实际项目里需要针对自己的文档做实验单证、学术论文、客服对话、规章制度各自适合的分块方式并不相同。4.3 向量检索与关键词检索的适用边界经典检索里BM25 等关键词检索在“专业术语匹配”场景很好用而向量检索更适合“语义相近、用词不同”的查询。比如用户问“设备报警了怎么办”文档里写的可能是“设备故障时的处理步骤”用词不重叠但语义相关关键词检索很难命中向量检索却可以。热词里频繁出现dense vector search指的就是这种基于稠密向量的向量检索。它通过神经网络将文本映射到高维向量空间把“语义距离”转化为“向量距离”。它的局限也很明显对未见过的专有名词、缩写、精确编号比如设备型号“ABC-3000”向量检索可能不如精确的关键词匹配。因此在生产系统里越来越多的方案是混合检索关键词召回 向量召回 重排序然后再交给模型。4.4 一句话总结这套链路真正的 RAG 不是“传给模型一段 PDF 内容”而是“从文档库里按问题挑出最重要的内容再传给模型”。 如果前面每一步都做得粗糙模型再强大也无法给出高质量回答。5. 最小可运行示例用PDF搭建一个本地RAG知识库说明以下代码用于演示完整链路而不是生产级方案。生产项目需要考虑文档清洗、更优的切分策略、检索可观测性和权限管理。5.1 环境准备与项目结构我们使用 Python 完成示例依赖如下# 建议 Python 3.10 及以上 pip install pdfplumber sentence-transformers faiss-cpu openai如果faiss-cpu安装失败可在 FAISS 官方文档中查询适合当前系统的安装方式。示例还会用到sentence-transformers它首次执行时会自动下载向量模型BAAI/bge-small-zh-v1.5。如果你希望离线运行需要提前把模型下载到本地。项目目录建议如下rag_simple/ ├── extract_pdf.py # PDF解析 ├── build_index.py # 分块 向量化 建索引 ├── search_demo.py # 检索测试 ├── rag_answer.py # 检索 大模型生成 └── demo.pdf # 你想要测试的 PDF5.2 第一步提取 PDF 文本文件路径extract_pdf.pyimport pdfplumber def extract_text_from_pdf(pdf_path: str) - str: full_text [] with pdfplumber.open(pdf_path) as pdf: for page_index, page in enumerate(pdf.pages, start1): page_text page.extract_text() or if not page_text.strip(): print(f[提示] 第 {page_index} 页未提取到文本 f可能是扫描页或图片型 PDF需要走 OCR。) continue # 保留页码标记方便后续定位如果不希望页码进入索引可去掉。 full_text.append(f Page {page_index} \n{page_text}) return \n\n.join(full_text) if __name__ __main__: text extract_text_from_pdf(demo.pdf) with open(extracted.txt, w, encodingutf-8) as f: f.write(text) print(fPDF 解析完成文本长度{len(text)} 字符)运行python extract_pdf.py注意text会被写入extracted.txt后续索引程序读取这个文件。如果遇到扫描版 PDF当前脚本无法提取到文字必须先用 OCR 工具生成文本层再把识别结果输入后续链路。5.3 第二步分块 向量化 构建索引文件路径build_index.pyimport json from pathlib import Path import faiss from sentence_transformers import SentenceTransformer def split_text_into_chunks(text: str, max_chars: int 500, overlap: int 80) - list[str]: 教学版分块函数。 思路按换行拆成段落逐段拼接当前块达到 max_chars 时截断 并保留上一块末尾 overlap 个字符避免边界语义被硬切。 paragraphs [p.strip() for p in text.split(\n) if p.strip()] chunks, current [], for para in paragraphs: # 如果当前块已经比较长再加入新段落会超过上限先落库 if current and len(current) len(para) 2 max_chars: chunks.append(current) # 保留上一块尾部字符作为下一块的语义衔接 current current[-overlap:] if overlap else current (current \n para).strip() # 处理某个段落本身超长的情况 while len(current) max_chars: chunks.append(current[:max_chars]) current current[max_chars - overlap:] if overlap else current[max_chars:] if current: chunks.append(current) return [c.strip() for c in chunks if len(c.strip()) 20] def build_index(text_path: str, index_path: str faiss.index, mapping_path: str chunks.json, model_name: str BAAI/bge-small-zh-v1.5): text Path(text_path).read_text(encodingutf-8) chunks split_text_into_chunks(text) print(f清洗后文本分块数量{len(chunks)}) model SentenceTransformer(model_name) # 归一化后内积相似度等价于余弦相似度 vectors model.encode(chunks, normalize_embeddingsTrue) dim vectors.shape[1] index faiss.IndexFlatIP(dim) index.add(vectors) faiss.write_index(index, index_path) # 保存文本块检索后需要根据向量索引取回原始文本 with open(mapping_path, w, encodingutf-8) as f: json.dump(chunks, f, ensure_asciiFalse, indent2) print(f向量维度{dim}) print(f索引已写入{index_path}) print(f文本块已写入{mapping_path}) if __name__ __main__: build_index(extracted.txt)运行python build_index.py这段代码有两处值得注意faiss.IndexFlatIP是“内积”索引编码时使用了normalize_embeddingsTrue因此内积等价于余弦相似度。这个选择适合入门真实项目里可以替换为 IVF、HNSW 等索引类型。chunks.json与faiss.index必须成对保存。删除任何一边都会导致无法从向量索引回溯到原始文本。5.4 第三步检索测试文件路径search_demo.pyimport json from pathlib import Path import faiss from sentence_transformers import SentenceTransformer def search(query: str, topk: int 3, index_path: str faiss.index, chunks_path: str chunks.json, model_name: str BAAI/bge-small-zh-v1.5): model SentenceTransformer(model_name) index faiss.read_index(index_path) chunks json.loads(Path(chunks_path).read_text(encodingutf-8)) q_vec model.encode([query], normalize_embeddingsTrue) scores, ids index.search(q_vec, topk) results [] for score, idx in zip(scores[0], ids[0]): results.append({ score: float(score), content: chunks[idx] }) return results if __name__ __main__: query input(请输入检索问题) results search(query) for i, item in enumerate(results, 1): print(f--- 结果 {i} (相似度 {item[score]:.4f}) ---) print(item[content]) print()运行python search_demo.py输入一个问题后程序会打印最相关的三个文本块。到这里你已经完成了 RAG 的前半段索引与检索。你可以不用看最终生成结果先观察“检索出来的文本块到底相不相关”。这是一个非常重要的调试习惯先确认检索好再谈生成好。5.5 第四步检索 大模型生成回答文件路径rag_answer.pyfrom openai import OpenAI from search_demo import search def build_prompt(query: str, results: list[dict]) - str: context \n\n.join( f[{i 1}] {item[content]} for i, item in enumerate(results) ) return f请严格参考以下资料回答用户问题。 如果资料中没有相关信息请明确说明“根据当前资料无法回答”不要编造。 资料 {context} 用户问题{query} 回答 def rag_answer(query: str, base_url: str http://localhost:11434/v1, api_key: str EMPTY, llm_model: str qwen2.5:7b): client OpenAI(base_urlbase_url, api_keyapi_key) # 先检索 results search(query, topk3) prompt build_prompt(query, results) resp client.chat.completions.create( modelllm_model, messages[{role: user, content: prompt}], temperature0.2, ) return resp.choices[0].message.content if __name__ __main__: q input(请输入你想问的问题) answer rag_answer(q) print(回答) print(answer)代码中的base_url指向本机 Ollama 的 OpenAI 兼容接口llm_model需要改成你已经拉取的本地模型名。如果你使用云端大模型 API请把base_url、api_key、llm_model替换为服务商提供的信息。生产项目里不应把密钥写死在代码中应通过环境变量或配置中心管理。6. 运行结果与效果验证6.1 如何判断链路已经跑通跑通标准有两条程序不报错回答内容确实来自 PDF。为了验证建议先问一个“只有文档里才有答案”的具体问题而不是开放性问题。比如 PDF 是企业差旅制度你可以问“差旅住宿标准上限是多少”如果 PDF 是产品说明书可以问“报警代码 E12 的含义是什么”。完整运行流程是python extract_pdf.py python build_index.py python search_demo.py # 手动输入问题测试检索 python rag_answer.py # 检索 生成预期输出应该分成两层search_demo.py返回与问题相关的内容块rag_answer.py返回结合这些内容生成的回答。如果检索返回的文本块与问题无关那么生成结果再流畅也没有意义。6.2 第一层验证观察检索结果效果验证一定要从“检索层”开始。输入同一个问题先不看最终答案看检索召回的文本块是否覆盖了解答所需的知识点。举一个例子假设用户的问题需要“条款 A”和“条款 B”共同回答但检索只召回了条款 A没有召回条款 B那么哪怕生成的句子通顺本质上也是信息缺失。这时候应该调整分块粒度或提高召回数量topk或优化 Embedding 模型而不是改 Prompt。如果检索结果里出现了很多重复页眉、页码、无关内容立刻回到解析环节提前过滤噪声。6.3 第二层验证观察最终回答生成层验证要关注三点是否有依据回答是否确实基于build_prompt中的“资料”是否漏信息文档中有明确答案模型是否只说了一半是否承认不知道当文档里没有答案时模型有没有编造。你可以做一个简单实验故意问一个与 PDF 完全无关的问题比如把 PDF 换成烹饪手册然后问“某设备的保修期是多久”。合格的 RAG 系统应该回答“根据当前资料无法回答”而不是把某段内容硬凑成答案。如果模型强行编造说明系统缺少约束需要改进提示词或限缩上下文。7. RAG知识库指标有哪些怎么测怎么看热词里经常出现“RAG 测评怎么做”“RAG 知识库指标有哪些”这确实是最容易被忽略的一环。很多人把系统搭出来觉得“看起来能答”却没有量化指标后续优化无从下手。RAG 效果应该分两层看检索层质量和生成层质量。7.1 检索层核心指标检索质量回答的问题是系统有没有把真正相关的文档块排到前面。指标作用直观理解Recallk召回了多少相关片段前 k 条结果里真正相关的占全部相关片段的比例Precisionk返回结果里有多少是相关的前 k 条结果里有多少条值得看MRR第一个正确答案的排名用户是否在一开始就看到正确答案NDCG排序质量与位置加权相关度越高的结果是否排在越前面以 Recallk 为例如果某问题需要 3 个知识片段才能完整回答前 5 条检索结果只命中 1 个Recall5 就是 1/3。这说明召回不足原因可能与分块粒度、Embedding 模型、混合检索策略有关。实际项目中不要只看一个指标。比如Precisionk很低但Recallk很高说明相关结果确实被召回了但混进了很多无关内容需要通过重排序解决。7.2 生成层核心指标生成质量回答的问题是模型有没有正确使用检索结果作答。指标作用直观理解Faithfulness / Groundedness生成内容是否忠于资料模型说的话是否都能在参考资料里找到依据Answer Correctness答案是否准确对标准答案做相似度或人工判断Citation Precision / Recall引用是否正确标注来源是否真的是内容出处生成层的“忠实度”比“流畅度”更关键。一个模型可能生成一段非常流畅的答复但细节和数据都是自己编的。RAG 的主要目标之一就是减少这种幻觉因此必须检查答案能否在检索片段中找到依据。7.3 没有人工评测一切都是空转上述自动化指标只能做初筛。真正上线前建议人工构建 50 到 100 个真实业务问题分成几类事实型问题、综述型问题、跨文档关联问题、无答案问题。然后根据以下维度打分检索到的内容是否足够回答问题最终答案是否正确最终答案是否有依据是否主动承认不知道。这类评测集需要持续维护。每当业务文档更新都可能影响系统表现并不是“建一次索引就永久有效”。8. 常见问题与排查思路问题现象可能原因排查方式解决方案PDF 解析后中文乱码字体编码特殊或 PDF 缺少 Unicode 映射查看提取文本换工具对比输出尝试 pdfplumber、PyMuPDF 等工具扫描件必须走 OCRPDF 解析出的段落顺序错乱多栏排版或表格结构打开原始 PDF 对比观察是否出现左右栏交叉在解析后做版式处理或对复杂页面单独清洗检索结果与问题无关分块过大向量只记住整体主题或 Embedding 模型不适合领域检查召回文本块内容调整 max_chars 重新建索引调小分块并增加重叠换领域相关向量模型引入关键词混合检索相关问题未被召回分块边界切断了知识点查看完整文档找到知识点实际所在位置按标题、段落切分保留重叠提高 topk生成的回答没有依据生成模型未严格受上下文约束查看最终使用的 Prompt 与检索片段强化 Prompt 约束降低 temperature必要时接“引用验证”环节上传的是扫描版 PDF提取不到文字PDF 本质是图片检查每一页是否有文本层OCR 后再进入索引或使用多模态模型理解页面检索结果高度集中在页眉页脚清洗阶段没去除页码、水印、页眉观察结果片段解析时保留正文结构过滤非正文信息文档数量达到几十万后检索变慢使用 Flat 索引或索引管理不规范查看索引类型与查询耗时使用 HNSW、IVF 等近似最近邻索引增加缓存排查时有一个通用顺序先看检索结果再看 Prompt最后才怀疑大模型能力。因为 RAG 中“答错”的原因有大量来自“没找到”“没检索到”“找错段落”而不是模型不会写。9. 进阶方向dense vector search、ontology RAG与Agentic RAG基础 RAG 跑通后很多团队会发现它只能回答“单点事实型问题”一旦遇到需要多跳推理、全局归纳或复杂关系判断的任务效果明显下降。热词里的dense vector search、ontology RAG、agentic RAG正是针对不同缺陷的进阶方案。dense vector search是当前向量检索的标配思路它利用 Embedding 模型把文本编码成稠密向量解决“同义词不同表达”的问题。它适合语义召回却对精确编号、冷门专有词不敏感。因此进阶方案通常把向量检索与稀疏检索如 BM25 做结合形成“混合检索 重排”。推荐在基础项目稳定后优先升级到这个模式。ontology RAG强调“本体和知识图谱”。它把实体与关系显式建模例如“A 产品隶属于 B 系列由 C 供应商提供备件”这些三元组会以图结构存储。当用户问“B 系列的备件供应商有哪些”这类跨实体问题时仅靠向量检索很难串联多个线索而本体层可以按关系路径推理。它的代价是需要构建和维护图谱不适合文档数量少或实体关系不明显的场景。agentic RAG是将大模型从“直接回答者”升级为“检索代理”。模型不再只执行一次检索而是先判断是否需要多个子问题、是否要改写用户问题、是否要反复检索。例如用户问“上半年华南区域哪款设备故障率最高且维修成本最低”模型可以先拆解成多个查询按区域筛选设备、查故障率、查维修成本再综合分析。这类方法灵活适合复杂业务问题但也意味着系统复杂度和失败点增加必须做好中间步骤的可观测日志。对大多数开发者我的建议是先不要盲目追新概念。把基础 RAG 做扎实再根据业务问题选择升级路径。如果只是文档问答混合检索就能解决大部分问题如果要做知识推理与关系分析再研究图谱或 Agent 策略不迟。10. 写在最后你的下一个RAG项目该从哪里开始回到开头的问题把 PDF 上传给大模型到底算不算 RAG如果产品只是把 PDF 转成文本拼进上下文让模型直接阅读那它更像“带文件上传的长上下文问答”。真正的 RAG必须有“构建索引”和“检索片段”这两个环节并且检索结果可以直接被溯源、查看和优化。如果你想动手做一个 RAG 知识库顺序应该是这样先用少量真实 PDF 跑通本文的最小链路确认索引和检索没有问题构造 50 个左右的业务问题手动检查检索召回与最终答案观察失败案例判断问题出在解析、分块、向量检索还是生成侧把基础链路优化到稳定再引入混合检索、重排、图谱或 Agent 级策略涉及企业内网或敏感文档时优先考虑本地部署模型并严格控制文档访问权限。RAG 不是一个“上传文件”的功能也不只是“向量数据库”的代名词。它是一条需要长期迭代的工程链路。从一份 PDF 开始真正走到检索链路的深处你才能体会到为什么“把文档喂给模型”只是整个故事的开头。