ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

检索增强生成全链路解析:从文档加载到评估的大模型知识库工程实践

检索增强生成全链路解析:从文档加载到评估的大模型知识库工程实践 很多开发者第一次接触大模型时第一反应是“调用 API 太简单了”把 Prompt 封装好用户问题丢给 GPT 或国内商用模型一个聊天机器人就上线了。但真正进入企业项目就会发现API 只是一个入口真正难的部分在于怎么让模型“看到”私有知识、怎么控制幻觉、怎么评估系统效果、怎么在有限显存环境下做推理部署。这些问题背后正是 LLM 工程化的完整链路。如果说 LLM 工程是一门需要体系化学习的课程那么 GenAI 和 RAG 就是其中最核心的两个板块。RAG 不是“把一个 PDF 塞给大模型”这种一句话能讲清的操作而是一套数据工程、检索系统、生成策略和评估体系的组合。这也是这门 Udemy 课程第三部分的重点从纯 Prompt 调用走向生产级知识库应用。这篇文章会以该课程第三部分为线索梳理 RAG 完整链路文档加载、切分、向量化、检索、生成、Agentic RAG 和 OAG 等进阶方向、FP16/FP32/BF16 精度问题以及 RAG 知识库评估指标。如果你正在做知识库问答、想系统学习 LLM 工程或者正从传统后端转向 AI 应用开发这篇内容应该能帮你建立一份更清晰的行动地图。1. 为什么 RAG 是 LLM 落地绕不开的方向先做一个判断在 2025 年这个时间点RAG 已经不只是“一个技术方案”而是 LLM 应用落地事实上的标准形态。聊天、写作、翻译可能用不到 RAG但凡是涉及私有数据、实时数据、专业文档的场景RAG 几乎都是第一选择。原因在于 LLM 本身有三大硬约束。第一知识截止。预训练模型的知识停留在训练数据截止时间训练之后发生的事情它不知道。如果你问它某个产品上周发布的新功能它大概率会一本正经地编一个答案。第二私有数据不可见。企业内部的规章制度、产品手册、客服工单、专利文档模型在训练时根本没有见过。直接让模型回答这些内容它只能靠“合理猜测”而合理猜测在专业场景里往往就是事故。第三幻觉问题。即使模型不知道答案它也会因为语言模型的本质而生成一个看起来通顺的回复。这不是模型“坏”而是概率生成机制导致的必然结果。解决幻觉最有效的工程手段之一就是用检索到的真实内容去约束生成范围。没有 RAG 的时候要让模型掌握新的领域知识主流方案是微调Fine-tuning。但微调的成本高、周期长每次知识更新都要重新训练一轮。更关键的是微调擅长改变模型的行为方式和表达风格却并不擅长记忆大量新事实。让一个 7B 模型通过微调背下一本 500 页的产品手册既不经济也容易过拟合。RAG 的思路是把“记忆”从模型内部搬到外部给模型一本可以随时翻阅的参考书这个参考书就是你的知识库。模型回答之前先从参考书里找出与问题最相关的几个片段再把这些片段作为上下文交给模型生成答案。用“开卷考试”来类比 RAG 和普通 Prompt 生成的区别非常直观。但在真实项目中这套“开卷考试”的工程复杂度远高于想象。很多人以为 RAG 就是“向量数据库 Prompt”实际做下去才发现文档怎么拆、按什么粒度拆、检索用什么向量、top-k 怎么设、重排怎么做、效果用什么指标来衡量每一步都可能让最终效果出现数倍的差距。2. RAG 完整链路与核心概念2.1 RAG 的标准流程一个标准的 RAG 系统由四个环节组成。第一数据准备阶段。原始文档需要经过加载、解析、清洗、切分变成适合检索的文本块chunk。这个环节经常被低估但它的质量直接决定了整个知识库的上限。如果原始文档是 PDF页眉页脚、表格、扫描图片都会成为干扰项如果切分粒度不对再好的向量模型也检索不准。第二索引构建阶段。每个文本块通过 embedding 模型转换为向量写入向量数据库。同时可以保留原始文本和元数据来源、页码、标题等方便后续溯源。第三检索阶段。用户输入问题后先把问题也转换为向量然后在向量数据库中做相似度检索取回最相关的 top-k 个文本块。更复杂的系统会在这个阶段加入关键词检索和重排模型。第四生成阶段。把检索到的文本块和用户问题组装成 Prompt交给 LLM 生成最终答案。为了减少幻觉Prompt 中通常会明确要求模型只基于检索内容作答无法回答时要明确说明。2.2 RAG 和微调不是二选一很多入门者会在 RAG 和微调之间纠结。我的建议是先分清楚你要解决的是哪一类问题。RAG 适合解决“知识更新”和“私有数据接入”的问题。比如 FAQ 问答、产品文档问答、行业报告摘要。它的优势是知识可以随时更新、答案可以溯源一次数据更新不需要重新训练模型。微调更适合解决“行为方式”和“输出格式”的问题。比如让模型学习某个业务场景下的固定话术、指令遵循习惯或者把输出格式严格限定成系统需要的 JSON 结构。微调改变的是模型的“性情”RAG 给的是模型的“素材”两者服务的层次完全不同。生产系统里常见的做法是两者结合先微调出一个懂业务话术的底座模型再用 RAG 为它提供实时事实。对大部分团队来说RAG 的性价比上限更高因为它不涉及训练资源。不要一上来就微调先把 RAG 链路跑通。2.3 从 RAG 到 Agentic RAG 和 OAG课程第三部分的内容里进阶方向会从“单次 RAG”延伸到“Agentic RAG”和“OAG”这类新概念。理解这两个概念有助于看清楚 RAG 演进的脉络。Agentic RAG 的核心变化是把 RAG 从“一次检索 一次生成”升级为“多轮决策循环”。简单说就是让 LLM Agent 承担检索规划的角色。它先理解用户问题决定是否要检索、检索什么拿到检索结果后判断信息是否足够如果不够就改写查询词再检索或者调用外部工具获取信息最后综合多轮结果生成答案。这种设计解决的是复杂问题拆解场景。比如用户问“帮我对比一下 A 产品和 B 产品在三个维度的差异”单次向量检索很难把三个维度一次找全。Agent 可以把这个问题拆成多个子问题分别检索再汇总。这就是 LLM Agent 与 RAG 结合的典型价值。OAG 指的是 Ontology-Augmented Generation即本体增强生成。它和 RAG 的区别在于RAG 检索的是非结构化的自然语言文本OAG 使用的是结构化的本体或知识图谱。本体会预先定义实体、关系和规则比如在通信协议文档中“ACK/NACK”“RRC 状态”这样的概念和它们之间的关联会被显式建模。生成时系统先从本体中查询符合逻辑约束的事实再交给 LLM 生成。在专业领域比如 3GPP 协议规范、医疗指南、金融监管文件这类文档中OAG 比普通 RAG 更容易保证语义一致性。普通 RAG 容易把不同版本的协议内容混在一起而基于本体的检索可以根据版本关系和实体约束做更精确的过滤。RAG 和 OAG 并不互斥一个成熟的专业知识库系统往往两种手段同时使用。3. 文档加载解析全流程容易被低估的一环如果说 RAG 链路中有一个最容易被低估、但又最决定效果上限的环节那一定是文档加载和切分。很多团队把大量时间花在调 Prompt 和换模型上却忽略了知识库的“地基”。实际上如果输入到向量库的文本本身就是脏的、碎的、无上下文的再好的检索模型也救不回来。3.1 不同文档类型的加载难点先从加载开始。现实项目里遇到的文档远比教科书例子复杂。PDF 是最常见的格式但 PDF 内部差异很大。文本型 PDF 可以直接抽取文字但页眉页脚会污染正文表格型 PDF 抽取后可能变成一段无结构的字符串扫描型 PDF 需要先 OCR而 OCR 又可能引入错字。Word 文档的问题在于样式标记和批注PPT 的难点在于每页内容太碎片化。HTML 页面则需要抽取正文、剥离导航和脚本。这些工作属于典型的数据工程听起来不“AI”但直接决定下游质量。在这个阶段可以借助 LangChain 的 document loader 体系。它提供了针对 PDF、Word、HTML、Markdown、PowerPoint 等多种格式的加载器。也可以引入 Unstructured 等解析库来做表格识别和 OCR。不过不能只依赖现成工具业务文档的布局规则往往需要自己写解析逻辑。3.2 文本切分策略切分是另一个关键决策点。切分的核心矛盾是块太大向量化后语义会被稀释检索不够精准块太小上下文不完整LLM 生成时看到的背景信息太少。经验上的常见范围是 200 到 500 个 token。但这只是起点具体值要根据文档类型和测试效果调整。切分方式也分几个层次。固定大小切分最简单按 token 数硬切但会切断句子和段落。递归字符切分是 LangChain 中最常用的方式它按分隔符优先级递归尝试尽量保住段落和句子结构适合通用文档。按 Markdown 标题切分适合结构化文档能保证每个块对应一个语义完整的章节。语义切分则利用 embedding 的相似度来决定边界效果最好但计算成本高。切分时还要设置 overlap也就是相邻块之间的重叠区域。Overlap 的意义在于如果一条知识恰好落在切分边界上没有重叠就会导致它被切断检索时自然找不到。一般建议 overlap 设置为 chunk size 的 10% 到 20%。下面给出一段基于 LangChain 的 PDF 加载与切分示例代码。这段示例使用 LangChain 0.x 早期的 API 写法新版中的导入路径可能有调整请以你安装的包版本为准。# document_prepare.py from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载 PDF 文档 loader PyPDFLoader(./data/product_manual.pdf) documents loader.load() print(f加载得到 {len(documents)} 页内容) # 2. 递归字符切分 splitter RecursiveCharacterTextSplitter( chunk_size400, # 每个文本块的目标大小token 级别 chunk_overlap50, # 相邻块重叠大小 separators[\n\n, \n, 。, , , , , ], ) chunks splitter.split_documents(documents) print(f切分后得到 {len(chunks)} 个文本块) # 3. 查看第一个块的元信息和前 200 字 print(chunks[0].page_content[:200]) print(chunks[0].metadata)在真实项目中不建议把所有文档类型都统一走同一条切分逻辑。正确做法是先对文档做分类规章制度类按章节切产品手册类按功能模块切FAQ 类按问答对整体保留。切分策略应该被当成一个可配置的参数允许在评估后反复调整而不是写死一次就不动了。4. 环境准备与依赖安装要动手跑通 RAG 示例需要先准备一套 Python 环境。下面的环境清单不针对特定版本建议以“安装时最新稳定版”为准本文重点演示通用思路。操作系统推荐 macOS 或 LinuxWindows 也可以运行但依赖安装时更容易遇到编译问题。Python 版本建议 3.9 以上最好使用 3.10 或 3.11。核心依赖拆成三部分向量化与检索框架langchain、langchain-community、langchain-openai。向量存储chromadb 或者 faiss-cpu。文档解析pypdf、unstructured、pandas。模型调用openai如果使用国内大模型则改成对应 SDK。创建虚拟环境并安装依赖python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install langchain langchain-community langchain-openai pip install chromadb faiss-cpu pip install pypdf unstructured如果你要调用 OpenAI 接口需要提前配置环境变量。如果使用其他模型服务或本地部署的模型替换对应的 embedding 和 LLM 类即可。export OPENAI_API_KEY你的密钥如果你所在环境无法直接使用境外模型服务可以选择国内厂商提供的兼容接口或者使用本地部署的模型。这个选择不影响 RAG 的架构思路只影响具体调用方式。5. 向量化与检索RAG 的核心决策点5.1 Embedding 模型选择文本块准备好之后下一步是向量化。这一步的核心是选择一个合适的 embedding 模型。Embedding 模型会把一段文本映射成高维向量语义相近的文本在向量空间中的距离也更近。目前选择很多OpenAI 提供了 text-embedding-3-small 和 text-embedding-3-large国内也有 BGE、M3E 等开源中文 embedding 模型还有各种本地可部署的模型。选择 embedding 模型时有三个考量因素。第一是语言适配。如果你的知识库主要是中文建议优先在中文语料上表现更好的模型或者做多语言模型而不是直接使用以英文为重心的小模型。第二是维度与成本。维度越高通常表达能力越强但存储和计算成本也越高。text-embedding-3-small 默认只有几百维对于大多数知识库场景已经够用。第三是推理环境。如果知识库部署在内部网络无法访问外部 API那就必须选择可在本地 GPU 或 CPU 上运行的 embedding 模型。开源模型在本地部署完全没有问题。5.2 向量数据库选型向量数据库的作用是存储向量并支持快速相似度检索。选型可以从项目规模出发。FAISS 是 Meta 开源的向量检索库不是一个完整数据库不提供数据持久化和权限管理但它轻量、高效适合原型验证和小规模项目。Chroma 是一个更完整的本地向量数据库提供了简单的 API 和持久化能力适合中小团队快速起步。Milvus 和 Qdrant 是面向生产环境的分布式向量数据库支持水平扩展、多租户、权限控制和丰富的过滤能力适合数据量达到几十万甚至上百万条级别的系统。选型建议个人学习和搭建原型直接用 FAISS 或 Chroma企业项目评估 Milvus、Qdrant如果团队已经引入了 Elasticsearch 8.x 且同时在用 ES 做全文检索也可以考虑用它内置的向量检索能力以减少运维组件的数量。5.3 检索策略向量检索、混合检索与重排很多人把 RAG 的检索等同于“向量相似度检索”但这只是起点。向量检索适合语义相关性判断比如“最大负载多少”能找到包含“负载能力”的文档即使没有“最大负载”这四个字。缺点是对精确关键词不敏感。关键词检索比如 BM25恰恰相反它擅长精确匹配比如产品型号、错误码、人名但无法理解同义改写。真实知识库中这两类问题同时存在。于是就有了混合检索同时执行向量检索和关键词检索再把两部分结果合并用重排序模型Rerank重新打分选出最终进入 Prompt 的内容。重排阶段通常把候选从 20 条压缩到 3 到 5 条能显著提升生成质量但也会带来额外的计算延迟。对于刚起步的团队建议先用纯向量检索跑通流程再加入关键词检索和重排逐步验证每一步带来的收益不要一上来就堆复杂组件。下面是一个基于 Chroma 和 LangChain 的完整检索问答示例。这里使用 LangChain 中常见的RetrievalQA封装。# rag_qa.py from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings from langchain_openai import ChatOpenAI from langchain.chains import RetrievalQA # 1. 初始化 embedding 模型 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) # 2. 把上一步切分好的 chunks 写入 Chroma 向量库 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) # 3. 构建检索器 retriever vectorstore.as_retriever( search_typesimilarity, search_kwargs{k: 4}, ) # 4. 初始化生成模型 llm ChatOpenAI(modelgpt-4o-mini, temperature0) # 5. 构建 RAG 问答链 qa_chain RetrievalQA.from_chain_type( llmllm, retrieverretriever, return_source_documentsTrue, ) # 6. 执行查询 query 产品手册中该设备的最大负载是多少 result qa_chain.invoke({query: query}) print(答案, result[result]) print(\n--- 参考来源 ---) for doc in result[source_documents]: print(来源, doc.metadata.get(source, 未知)) print(内容, doc.page_content[:100])这个代码里有几个细节值得注意。search_kwargs{k: 4}表示只取回 4 个最相关的文本块。k 值太小容易漏答案k 值太大则会把无关内容塞给 LLM让答案跑偏。一般从 4 到 6 起步再根据评估效果调整。temperature0是问答场景的常见配置。知识库问答要求稳定、忠实不需要创造性输出。而写文案、头脑风暴类场景才需要提高温度。return_source_documentsTrue一定要保留否则你很难知道答案是来自知识库还是模型自己编的。生产系统中来源溯源是知识库功能的基本要求。5.4 从示例到生产系统上面的示例跑通之后要往生产系统走还有几件事需要补充。一是索引的增量更新。文档会更新、删除、追加不能每次全量重建。实践中会引入调度任务和消息队列按文档哈希或版本号判断是否需要重新向量化。二是向量数据库的安全性。私有知识库通常涉及敏感数据。向量库本身往往没有严格的权限模型需要在应用层做好访问控制对用户查询和检索结果做权限过滤。三是检索质量监控。线上系统一般会记录每次查询的召回文档、相关性分数和最终答案并定期抽样做人工评估。这样当某个知识点的检索质量下降时可以及时定位是数据更新问题还是模型变更问题。6. LLM 精度问题FP16、FP32、BF16 在工程中的取舍在 RAG 示例中你可能用的是托管模型 API不需要关心权重存储的精度问题。但如果你要做本地推理、私有化部署或者用开源模型搭建知识库那么 LLM 的精度问题就是绕不开的工程决策。6.1 为什么精度问题值得关注大模型的权重是一组浮点数。浮点数的表示精度和范围由格式决定。同一个模型参数用不同精度存储和计算会直接影响三件事显存占用、推理速度、输出质量。先给一个粗略的显存估算。一个 7B70 亿参数参数的模型以 FP32 存储参数大小约为 7 × 4 字节等于 28GB。FP16 或 BF16 是半精度每个参数 2 字节所以约 14GB。如果再算上推理过程的中间激活值实际占用会更高。这也是为什么 8GB 显卡几乎跑不动 7B 模型的全量推理通常需要配合 4bit 量化。6.2 三种精度格式背后的设计差异FP32 是单精度浮点数宽度 32 位其中指数位 8 位尾数位 23 位。它是 CPU 和 GPU 上最通用的标准格式精度最高。模型预训练和科学计算常用 FP32 作为基准。缺点是显存占用大、计算速度相对慢。FP16 是 IEEE 半精度浮点数宽度 16 位其中指数位 5 位尾数位 10 位。相比 FP32它节省一半显存计算速度也更快。但指数位只有 5 位导致它能表示的数值范围变小。当某个权重数值很大时会被表示成无穷大当数值极端小时又容易变成 0。这就是“溢出”和“下溢”问题。在训练大模型时如果直接用 FP16梯度很容易在反向传播中丢失。BF16 是 Brain Floating Point也是 16 位但指数位有 8 位和 FP32 相同尾数位只有 7 位。它的巧妙之处在于“牺牲精度保留范围”。因为和 FP32 拥有相同的指数范围BF16 在高数值范围下更不容易溢出训练时稳定性明显优于 FP16。代价是尾数少同一个数值BF16 能表示的小数精度比 FP16 低。6.3 三种精度的对比精度格式位宽指数位尾数位主要优点主要缺点常见用途FP3232 位8 位23 位精度最高显存占用大、计算慢预训练基准、科学计算、调试FP1616 位5 位10 位显存减半、速度快数值范围小训练易溢出混合精度训练、GPU 推理BF1616 位8 位7 位数值范围与 FP32 相同训练稳定尾数少精度略低大模型预训练、推理部署6.4 实际工程中的选择建议在本地部署开源 LLM 做知识库推理时更稳妥的选择通常是 BF16而不是 FP16。因为大模型推理阶段对数值范围更敏感FP16 在极端权重分布下偶尔会出现输出质量劣化。而在需要极致推理速度时GPU 对 FP16 的计算吞吐往往更高这时可以使用 FP16 并做针对性评测确认任务指标没有明显回退再做选择。如果显存实在不够就需要考虑量化方案把权重降低到 INT8 甚至 INT4。量化后模型文件更小推理速度提升但会有更明显的精度损失。是否可接受不能拍脑袋决定必须在业务测试集上做对比评估。这里真正容易踩坑的地方是很多人看到模型量化后“看起来回答还挺正常”就直接上生产结果在专业术语密集、格式要求严格的场景中量化模型频繁答非所问。任何精度选择都应该和评估体系绑定用数据决策而不是凭感觉。7. RAG 知识库指标如何评估一套 RAG 系统很多开发者在把 RAG 系统搭起来之后会遇到一个尴尬问题系统已经能运行了但到底好还是不好说不清楚。如果连好坏都无法量化后续优化就无从下手。RAG 系统的评估是这部分课程非常强调的内容。7.1 先从离线评估集开始在讨论指标之前先建立一个基本前提评估需要一套固定的测试集。没有测试集谈指标都是纸上谈兵。测试集的构建方式是从知识库中挑选一批有代表性的文档针对它们设计 50 到 100 条问题并为每个问题标注期望答案或至少标注“应该从哪些文档片段中检索”。这个集合称为黄金集。以后每次调整切分参数、更换 embedding 模型、修改检索策略都在同一套黄金集上跑看指标变化。7.2 检索质量指标RAG 系统的检索环节直接决定了“模型看到了什么”。如果检索结果里根本没有正确答案生成环节再强也白搭。所以检索质量必须独立评估。Hit Rate 是最直观的指标表示在检索返回的 top-k 结果中是否至少有一条是相关的。如果检索了 100 个问题其中 85 个问题在 top-5 结果里有正确答案Hit Rate5 就是 85%。它反映的是“有没有召回”。MRR 全称是 Mean Reciprocal Rank用于衡量第一个相关结果出现在什么位置。如果第一个相关问题排在第 1 位得 1 分排在第 3 位得 1/3 分。MRR 越高说明相关结果越靠前。它反映的是“召回到什么位置”。Precisionk 和 Recallk 则进一步细化了准确率评估。Precisionk 表示返回的 k 个结果中有多少比例是相关的Recallk 表示所有相关文档中被召回的比例。这些指标之间的关系可以这样理解Hit Rate 是底线MRR 是排序质量Recall 和 Precision 是更严格的双向评估。在实际项目中通常先看 Hit Rate再关注 MRR最后结合业务需求看是否要调整 k 值。7.3 生成质量指标检索质量好不代表最终答案好。LLM 可能没有遵循 Prompt 指令、可能遗漏关键信息、也可能在检索内容之外自行发挥。生成质量需要单独评估。Faithfulness忠实度衡量生成内容是否忠实于检索到的上下文。如果模型明明看到资料里写“最大负载 50kg”却在答案里说“最大负载 80kg”就是不忠实。这个问题本质上就是幻觉。Answer Relevancy答案相关性衡量生成内容是否切题是否回答了用户的问题。有时候模型复述了一堆原文但用户问的是“怎么解决”模型回答的是“是什么”就是不相关。这两个指标在生产系统中都可以自动计算也可以抽样人工评分。自动计算的典型工具是 RAGAS它利用 LLM 作为评测器输入问题和答案以及检索文档返回上述指标的分数。但要注意用 LLM 评 LLM 会有偏好偏差关键业务场景建议保留人工抽检环节。7.4 一个最小化的 MRR 计算示例理解 MRR 最快的方式是亲自动手实现一次。下面的伪代码演示了 MRR 的计算逻辑。# eval_mrr_example.py def reciprocal_rank(relevant_rows, k): relevant_rows: 检索结果列表按相关性分数降序排列 k: 只看前 k 条结果 返回该查询的 reciprocal rank 值 for rank, row in enumerate(relevant_rows[:k], start1): if row[is_relevant]: return 1.0 / rank return 0.0 # 示例一次查询的检索结果按相关性从高到低 query_result [ {doc_id: doc_1, is_relevant: False}, {doc_id: doc_2, is_relevant: True}, {doc_id: doc_3, is_relevant: False}, ] k 3 rr reciprocal_rank(query_result, k) print(f本次查询的 reciprocal rank: {rr}) # MRR 就是多次查询 reciprocal rank 的均值 all_queries [ {doc_id: doc_1, is_relevant: False}, {doc_id: doc_2, is_relevant: True}, {doc_id: doc_3, is_relevant: False}, ] def mean_reciprocal_rank(queries, k): total 0.0 for query in queries: total reciprocal_rank(query, k) return total / len(queries) mrr_value mean_reciprocal_rank([query_result, query_result], k3) print(fMRR3: {mrr_value})在实际项目中你可能不会手写这些指标而是借助 Ragas 或其他评测框架。但理解计算逻辑非常重要。只有理解每个指标在奖励什么、惩罚什么你才能在调优时判断“指标上升是否真的意味着系统变好”。8. 常见问题与排查思路RAG 系统在开发阶段的问题非常多很多现象看起来相近但原因完全不同。下面整理了一份常见问题排查表可以收藏备用。问题现象可能原因排查方式解决方案启动时依赖安装失败Python 版本不兼容或缺少编译环境查看 pip 错误日志确认 Python 版本升级到 Python 3.10或使用 conda 创建隔离环境知识库检索不到相关内容文档切分过大或过小语义被稀释或截断打开向量库查看文本块语句是否完整调整 chunk_size 和 chunk_overlap重新向量化答案和用户问题不相关检索返回了无关内容且进入了 Prompt打印 source_documents 查看召回文档调整 top-k、加入混合检索或重排模型回答出现幻觉检索结果为空模型被迫自行补全检查检索结果的相似度分数是否过低设置相似度阈值明确要求模型不知道就说不知道检索到正确内容但答案反而混乱塞进 Prompt 的上下文太多或相互矛盾检查 source_documents 的数量和内容降低 k 值或对文档做去重和版本过滤专业术语大量出现时效果变差embedding 模型对专业领域理解不足在黄金集上对比不同 embedding 模型的指标替换为领域微调过的 embedding 模型或引入关键词检索向量库持久化后重启数据丢失未配置持久化目录或使用了纯内存模式检查向量库初始化参数配置 persist_directory 或使用服务端向量数据库本地部署模型显存不足精度选了 FP32/FP16模型参数过大查看显存占用日志切换到 BF16 或量化到 INT8/INT4验证效果答案经常引用过时文档知识库没有增量更新机制检查文档更新时间戳引入数据版本管理和索引重建任务排查 RAG 问题有一个通用顺序先确认检索阶段有没有正确召回再确认 Prompt 组装有没有问题最后再怀疑生成模型。很多人第一反应是“模型不行”结果花了很多时间换模型问题依旧。实际上RAG 系统的大部分效果问题都出在数据和检索环节。9. 最佳实践与工程建议RAG 项目做到最后拼的往往不是某个环节的极致优化而是工程体系是否完整。下面这几条最佳实践来自常见生产项目的经验总结。9.1 从最小可行 RAG 开始不要在一开始就引入 Agentic RAG、混合检索、知识图谱等复杂组件。第一步应该用最快的速度跑通“文档加载 向量化 检索 生成”的最小链路让业务方看到效果也让自己对数据情况有感知。然后再用评估数据驱动优化。否则复杂系统一旦出问题定位成本会非常高。9.2 把文档预处理当成第一优先级知识库项目的天花板往往在文档预处理阶段。建议对文档格式做一次全面盘点把“哪些文档适合直接切分”“哪些文档需要 OCR”“哪些文档需要专门解析表格”列成明细表。对经常出现的固定版式可以写专门的解析函数而不是寄希望于一个通用解析器搞定所有文档。9.3 检索环节先保证召回再做精排在检索链路设计上可以用两级思路第一级尽量放宽条件保证相关文档能被召回第二级用重排模型或更精细的过滤逻辑把真正有用的内容排到前面。先优化 Hit Rate再优化 MRR最后才看生成质量。这个顺序不能反。9.4 Prompt 要明确约束生成行为生成阶段的 Prompt 至少应该包含三部分系统指令、检索上下文、用户问题。系统指令要明确告诉模型“只能依据检索内容回答不要使用训练时学到的知识补充如果检索内容不足回答不知道”。同时要求模型在回答时引用来源编号。这个设计能显著降低幻觉率也让溯源成为可能。9.5 记录完整的运行日志线上 RAG 系统建议记录每次请求的完整链路原始问题、改写后的问题如果有、召回的文档列表、相似度分数、送入 Prompt 的最终上下文、模型输出、耗时。这些日志既是调试故障的依据也是后续搭建自动化评测集的数据来源。很多团队忽略了这一步等到上线后效果变差才发现没有任何线索可以回溯。9.6 安全与权限是最后的底线如果知识库包含了内部资料或用户隐私数据在架构设计之初就要考虑权限隔离。不要让一个用户可以检索到另一个用户私有范围的内容。向量数据库层的过滤能力很重要应用层也要做身份认证和操作审计。涉及敏感数据的项目务必在测试环境验证权限过滤逻辑后再上线并准备好回滚方案。10. 总结LLM 工程师的核心能力模型回到这门 Udemy 课程第三部分的整体定位它真正想训练的能力不是“会调用 API”而是“会设计一套完整的 GenAI 系统”。从 RAG 链路到 Agent 概念从精度问题到评估指标这些知识点串联起来指向的是 LLM 工程师的四种核心能力。第一是数据工程能力。知道如何从真实业务文档中提取可用知识如何处理 PDF、Word、HTML 等复杂格式如何设计切分策略。第二是检索系统设计能力。知道如何选 embedding、如何选向量数据库、如何设计混合检索和重排而不是只会调用一个现成接口。第三是模型部署与成本控制能力。理解 FP16、BF16、量化的取舍知道在有限的 GPU 资源下如何平衡速度和质量。第四是评估能力。能建立测试集能看懂指标变化能通过数据定位瓶颈。这是区分“能跑 Demo”和“能上生产”的分水岭。如果这篇文章让你有收获建议马上选一份实际文档按照第二章到第五章的流程搭一个最小知识库再做一套 50 条问题的评估集记录当前指标。然后试着调整 chunk_size、切换 embedding 模型、加入重排观察指标和答案质量的变化。做 LLM 应用开发最大的误区是以为“视频课刷完就是会了”。真正值回票价的方式是把课程里的评估方法用到自己的知识库里把系统从“能跑”改到“好用”。RAG 工程中还有很多细节值得深入比如更复杂的文档解析、Agent 多轮检索、基于本体的约束生成、以及更细粒度的评测体系。每一条都值得再单独写一篇实践笔记。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进