☰
RAG项目实战:5个高频踩坑点与解决方案全解析
2026/11/3 7:21:24 网站建设 项目流程

我搞 RAG 项目也有不短的时间了,从最早拿 LangChain 搭个 demo 跑通,到后来在真实业务场景里被各种离谱问题折磨,中间踩过的坑确实不少。市面上讲 RAG 原理和搭建的文章很多,但真正把“实战中最常遇到的问题”掰开揉碎讲清楚的,反而不多。很多朋友私下问我,说为什么照着教程做出来的 RAG 知识库,一问就胡说八道,或者干脆答非所问。说实话,这些问题我基本都遇到过,而且绝大部分不是模型不行,是 RAG 链路里的某个环节没做好。

这篇内容我打算把 RAG 项目中最常遇到的 5 个问题一次性讲透,每个问题都会说清楚现象、背后的原因、怎么解决,以及我实测下来觉得好用的配置和避坑经验。不管你是刚接触 RAG 的新手,还是已经在做本地知识库、智能问答、Agent 方向的开发者,这篇内容应该都能帮你省下不少排查时间。

1. 文档解析与数据清洗的“隐形坑”

1.1 现象:知识库文件放进去,检索结果却一片混乱

很多人做 RAG 的第一步就是把 PDF、Word、网页内容扔进去,然后分块、向量化、入库,以为这就完事了。但实际做下来你会发现,检索出来的文本片段经常是断裂的、重复的,甚至根本读不懂。我之前接过一个项目,客户的知识库是几百份扫描版制度文件,PDF 里全是图片,直接解析出来就是一堆乱码和空行,检索出来的内容自然没法看。

这种问题的根源在于解析环节没有做好。RAG 的工作流程里,文档解析是地基,地基没打稳,后面无论怎么调向量模型、调 rerank,效果都很难有质的提升。PDF 有多种类型:文字版、扫描版、混合版;Word 里有表格、页眉页脚、批注;网页里有导航栏、广告位。如果不做针对性的解析和清洗,这些杂质会全部混入向量库,直接污染检索结果。

1.2 解决思路:从源头控制输入质量

我现在的做法是,先对文档做一次“体检”,再决定用哪套解析方案:

  • 文字版 PDF:直接用 pypdf 或 pdfplumber 提取文本,速度快,但要注意处理断行和页码信息。很多 PDF 提取出来的英文单词会被硬拆成两行,需要根据标点和行尾字符做拼接。
  • 扫描版 PDF / 图片:必须走 OCR 流程。本地环境我用 PaddleOCR,识别中文表格和印刷体效果都不错;不想本地折腾的话,也可以接云厂商的 OCR 接口。OCR 之后还需要根据版面分析结果,把标题、正文、表格结构化,而不是简单按行拼接。
  • Word 文档:python-docx 可以按段落读取,但表格内容要单独提取。这里有个细节容易被忽略——Word 里的“批注”和“修订痕迹”会被正常读取,如果你没有提前接受修订,解析出来的内容可能是带删除线的废文本。
  • 网页内容:我用 Readability 这类工具先做正文抽取,把脚本、样式、导航全部剥掉,只保留核心正文和标题。

清洗环节我也建议不要偷懒。我见过不少项目直接把解出来的文本做个去空格就入库了,结果页眉页脚全变成检索结果,答出来的话里面带着公司内部编号,特别出戏。文本清洗至少要做这几步:去页码、去页眉页脚、去除空行和多余空白字符、统一换行符、对长段落做语义边界判断。

1.3 实操细节:表格和版式信息的处理

表格是解析里面最容易被低估的一块。RAG 系统在处理表格时,如果直接把 Markdown 格式的表格灌给 embedding 模型,检索效果往往不好,因为 embedding 模型对二维结构不敏感。我现在的建议是:表格数据量小的话,转成“字段-值”的自然语言描述入库;表格非常大,就单独做结构化存储,检索时走另外的召回逻辑,不要把表格和正文混在一个 chunk 里。

我最近在试一个思路,就是把页面的层级结构也存成元数据,比如“章节标题”“小节标题”作为 metadata 字段保存。这样检索命中之后,可以沿着 metadata 往回追溯,把上层章节标题也拼进上下文,回答的时候就不至于只看到孤零零的一段话,不知道出自哪里。这个思路在很多开源项目里已经实现了,比如一些做版式还原的方案,核心就是把视觉信息和语义信息分开处理。

2. Chunk 切分:参数不是越大越好

2.1 现象:检索结果要么太细碎,要么答非所问

很多零基础教程教的是“设个 chunk_size 和 chunk_overlap 就行”,实际上这也是 RAG 效果差的重灾区。我见过有人把 chunk_size 设成 2000,结果每个 chunk 都长到 embedding 模型都处理不过来;也有人设成 100,结果一句话被切得七零八落。这个问题的高级之处在于:没有一套参数能适配所有文档,必须根据文档类型和检索需求来调。

chunk 切太细,检索到的片段信息量不足,模型无法理解上下文;切太粗,一个 chunk 里塞了多个主题,embedding 的语义向量会被“平均化”,单个问题的相关度被稀释,检索精度直线下降。还有 chunk overlap 设得太大,会让同一个内容出现在多个 chunk 里,召回时大量重复片段挤占上下文窗口。

2.2 解决思路:先定策略,再调参数

我现在做 chunk 切分,会先回答几个问题:

  • 这个文档的内容是百科全书式的一问一答,还是连续的长篇论述?前者适合按“问题-答案块”切分,后者适合按 Markdown 标题层级切分。
  • 检索的粒度是“找一句话”还是“找一段论述”?这决定了 chunk size 的大方向。比如做合同审核,一个条款就是一个完整语义单元,这时更适合用自定义分隔符切分,而不是按字数硬切。
  • 是否需要跨 chunk 的上下文关联?如果需要回答“某条款的例外情形”,chunk 里就最好带上条款编号和例外情形的引用关系。

参数方面,我常用的一组起点值是:chunk_size=512(按 token 计算的话约 700-800),chunk_overlap=100(token)。这不是最优解,但作为基准很稳。后续再根据检索效果的反馈往两个方向调:如果发现答案缺失细节,适度调小 chunk_size;如果发现上下文不连贯,适度调大 chunk_overlap。

2.3 实操细节:markdown 标题切分和父子块策略

如果你的文档是 Markdown、HTML 这种有结构的内容,优先按标题层级切分,而不是按固定字符数。LangChain 的MarkdownHeaderTextSplitter、LlamaIndex 的MarkdownNodeParser都能做这件事。按标题切分的好处是:语义边界和自然阅读顺序保持一致,embedding 的效果会明显好于无脑固定窗口。

再进阶一步,我强烈建议试试父子块(Parent-Child Chunk)策略。简单说,就是库里同时存“小片段”(比如 200 字)和“大片段”(比如完整章节)。检索时先用小片段做向量召回,拿到命中的小片段后,把属于同一父块的大片段整体取出来,作为上下文喂给模型。这样既保证了检索精度,又给了模型足够的上下文长度。我在实际项目里用这个方案,问答完整度提升非常明显。

3. 向量检索失效:混合检索和语义匹配的盲区

3.1 现象:相似度很高,但答案是错的

向量检索的原理是把文本映射到高维空间,用距离度量相似度。它擅长理解“意思相近”的内容,但在精确匹配上反而很弱。最典型的场景是:用户问“HTTPS 协议的默认端口是什么”,库里明明有“HTTPS 默认端口 443”,向量检索却因为“端口”和“默认端口”在语义空间中的相关性略低于预期,把另一篇讲“HTTP 80 端口”的文章排到了前面。

还有一个常见盲区是:专有名词、产品名、代码变量名。小写“rag”和“RAG”在向量空间里不一定被认为是一回事;“LangChain4j”这种混合大小写的名称,切词是一团乱麻。如果知识库是中文为主、英文技术词密集,单纯用 embedding 做召回,命中率会让人崩溃。

3.2 解决思路:关键词检索兜底,混合检索上位

我现在所有 RAG 项目都默认做混合检索:向量召回 + BM25 关键词召回,再通过 RRF(Reciprocal Rank Fusion)合并排序。向量负责语义相关性,BM25 负责精确匹配和词频信号,两者互补。实测下来,混合检索在高精度匹配场景里的效果提升,比换更强的 embedding 模型还明显。

具体配置上,如果用 Elasticsearch,可以同时建 dense_vector 字段和 text 字段,用knn查询加match查询做组合。如果用 Milvus 或 Qdrant,也能通过hybrid searchAPI 实现,权重比例我一般从向量 0.7 对关键词 0.3 起步,再根据数据分布调整。

3.3 实操细节:召回分数异常时的排查顺序

如果你发现检索结果里相似度分数普遍很高但内容跑偏,优先检查这几项:

  • embedding 模型是否和查询端一致。我有一次排了半天,最后发现导入数据用的是某个开源 embedding 模型,线上查询却换了一个版本,两边向量空间根本不匹配,分数高得离谱但语义毫不相干。
  • 是否对 query 做了必要的改写。短查询和长文档之间的“词汇鸿沟”很大,直接拿用户原话去检索,效果往往不好。可以先让 LLM 把问题改写成一个“更详细、更中立”的查询语句,或者拆成多个子查询并行召回。
  • 是否做了 rerank 重排。只靠向量相似度排序,前 5 个结果里可能混着 3 个不相关的内容。加一个 rerank 模型(bge-reranker、Cohere Rerank 这类),把向量召回的前 20 条重排一遍,再取前 5 条进上下文,效果会有质的改变。Rerank 这一步在开源社区基本是标配了,很多 RAG 框架里都已经内置,建议直接打开。

4. 上下文管理和“Lost in the Middle”效应

4.1 现象:给模型的材料越多,回答问题反而越错

这是另一个让我印象极深的坑。你为了“不漏信息”,把召回的前 10 个 chunk 全部拼进 prompt,结果模型对中间部分的内容视而不见,只盯着开头和结尾看。学术上管这叫 Lost in the Middle——大模型对长上下文中间位置的注意力显著下降。我之前做过一次测试,知识库内容 3000 token,模型回答质量居然不如只喂 1200 token 的时候。

很多刚开始做 RAG 的人有个误区,认为给模型的上下文越多越好,觉得这样“信息量足”。实际上,输入的上下文长度越长,噪声比例越高,模型被误导的概率也越大。与其把所有召回来的东西全塞进去,不如做一次更严格的质量筛选。

4.2 解决思路:先压缩,再拼接

我现在处理上下文的思路是“压缩优先”。召回 20 条,先用 rerank 筛到 5 条;这 5 条如果很长,再做一轮摘要式压缩,把每个 chunk 变成 3-5 行的核心信息摘要。LLM 不一定需要完整原文,它需要的是“和问题相关的关键事实 + 出处来源”。压缩后的内容,既减少了 token 消耗,又提高了线索浓度。

对于必须保留原文的场景(比如法务问答、合同审查,一个字都不能错),我会调整 chunk 组织方式:把最相关的内容放在 prompt 的开头和接近结尾的位置,中间放次相关内容。这种“信息布局”听上去很玄,但实测确实能改善回答质量。

另一个容易被忽略的点是:把检索到的每个 chunk 标注清楚来源,比如“出自《员工手册》第三章,第 2 节”。模型在回答时能参照出处组织语言,同时减少它复述不相干内容的概率。这个改动成本极低,但对回答的准确率和可信度帮助极大。

4.3 实操细节:上下文压缩的具体手段

上下文压缩不只是“截断”,我常用的手段有几种:

  • 用 LLM 对每个 chunk 做条件性摘要:Prompt 里指定“只提取与以下问题相关的事实和数字,忽略无关内容”。相比静态摘要,条件性摘要的信息密度高很多。
  • 用metadata 过滤:检索阶段就结合时间戳、文档类型、章节路径等过滤,减少进入 rerank 的候选集规模。
  • 用对话历史扩展当前 query:多轮对话场景下,把历史消息的关键实体和意图合并进当前问题,再去做检索,能避免“这”“那”这类代词指向不清的问题。

5. 评测与迭代:靠感觉调参是走不远的

5.1 现象:每次改了参数,效果时好时坏,但说不出原因

这是 RAG 项目最要命的一点——没有评测体系,就无法迭代。我见过很多团队开发 RAG 知识库,上线后靠人工抽几条问题看看“答得不错”就宣布完成。但真实用户问法千奇百怪,你测的 10 条覆盖不了真实分布。没有量化指标,你根本不知道下一次改动到底是变好了还是变差了。

评测难做,是因为 RAG 链路长:召回准不准、rerank 排序对不对、LLM 有没有忠实于原文,每个环节的失败都会叠加到最终答案里。如果只看最终答案,你怎么都定位不到问题在哪个环节。

5.2 解决思路:把评测拆成“检索质量”和“生成质量”两个维度

我先说检索质量。检索评测可以做成“数据集 -> 预期命中文档”的模式:准备一组测试问题,每个问题标注出预期命中的 1-2 个文档(或 chunk 区间)。跑一遍检索流程,统计命中率(Recall@K)、排序质量(MRR)。这一步不需要 LLM 参与,纯算 A/B,成本很低,但能快速帮你判断哪次改动真正有效。

生成质量评测目前比较通行的做法是引入 LLM 当裁判,用另一套强模型对回答做三维打分:忠实度(是否忠于给定上下文)、相关性(是否回答到点上)、完整性(是否遗漏关键信息)。三个维度我都在项目里试用过,相关性最核心,忠实度决定这条回答能不能用,完整性决定体验。

基于这两个维度的评测结果,我建议为每个 RAG 项目建立10-30条的高质量评测集。不用特别大,核心是覆盖真实业务里的高频问题、易错问题、边界问题。

5.3 实操细节:RAG 框架内置的评测工具别浪费

现在很多框架都内置了评测工具,直接用能省不少事:

  • LangChain 生态里有langsmith评测体系,LangChain4j 也有对应的评测模块,可以记录每次 prompt 和检索 trace。
  • Ragas 开源库专门做 RAG 评测,支持上下文相关度、忠实度、答案相关性多个指标,简单项目直接用即可。
  • Elasticsearch 自带的_search查询分析、kibana 的可视化,也能辅助判断召回质量分布。

我自己的实践是:所有调参(chunk 大小、overlap、检索 topK、重排阈值、prompt 模板)都必须跑评测集,用评测数据说话。这不是说人工检查没用,而是不能只靠人工检查。之前有一次我把重排阈值从 0.4 调到 0.6,人工看几条觉得差不多,但跑评测集后相关性得分从 0.72 涨到 0.81,之后那些看起来“差不多”的改动,我再也不会根据自己的感觉拍板了。

很多朋友问我要不要直接上 Agentic RAG(比如用 Agent 规划、多步检索、意图判断、动态选择工具,新一代框架如 Agentscope 2.0 也把 RAG as Service 作为一个核心方向)。我的看法是:基础 RAG 的问题如果没解决,上 Agent 只会把错误放大。Agentic RAG 更像是基础 RAG 稳定之后的“增量优化路线”,比如在文档结构复杂的场景里,让 Agent 先判断该查哪张表、哪个章节,再做检索,确实比端到端一把梭效果更好;但如果底层文档解析、embedding、rerank 这些环节都在带病运行,再聪明的 Agent 也补不了地基的窟窿。先做小的可靠,再做大而全的智能,这是我踩过不少坑之后的真实体会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询