我们先把话说在前面:一个看起来能用的 RAG 知识库问答系统,真正放进业务里跑,十有八九会在第一周就被用户吐槽“答非所问”。更扎心的是,当你把日志翻出来检查时,经常发现模型本身没问题,Prompt 写得也还行,甚至召回的文档看起来也是相关的——但最终答案就是不对。这种情况我见过太多次了。
这个标题之所以叫“RAG 实战”,是因为我不打算跟你复述教科书里的 RAG 概念,而是要聊那些真正让系统跑偏的细节:数据怎么切、向量检索为什么失灵、查询改写要不要做、重排序到底值不值得上、以及如何系统性地排查“答非所问”的根因。
这篇文章适合正在做知识库问答、尤其是已经被“答非所问”折磨过的工程师和技术负责人。就算你只是刚入门、想用 RAG 搭一个内部知识助手,这篇文章也能帮你避开那些文档里不会写的坑。
1. 先搞清楚:答非所问,问题到底出在哪一环
1.1 瓶颈通常不在生成模型,而在检索链路
很多团队一开始都会怀疑“是不是大模型太笨了”,于是换个更大的模型,结果发现该答错还是答错。实际上,在大模型能力已经够用的情况下,RAG 系统的瓶颈十有八九在检索侧。你可以把整个问答过程想成“先到图书馆找资料,再让一个聪明的助手根据资料写回答”。如果从图书馆拿回来的资料就是错的、碎片的、或者是无关的,那助手再聪明也写不出正确答案。
我遇到过的最典型情况是:知识库里明明有答案,但系统就是搜不到。用户问“公司年假怎么算”,库里文档写的是“带薪休假天数计算规则”,两者语义上有关系,但向量检索却没能把它们关联起来。这种“答非所问”不是模型问题,而是检索召回环节没接住用户的语言。
RAG 的完整链路被拆开来看大概是这么几步:文档加载、切块、向量化、索引构建、用户查询向量化、召回、重排序、组装上下文、大模型生成。每一步都会引入误差。很多教程只会带你跑通 Demo,但从 Demo 到可用的产品,距离就藏在这些环节里的坑里。
1.2 一个常见的错误假设:向量相似就代表答案正确
这是我最想强调的一点。向量检索找到的是“语义相似”的文本块,但“语义相似”和“包含答案”完全是两回事。举个简单例子:知识库里有篇文章叫《员工手册常见问题》,里面有一句“本手册不包含年假细则,请参考《休假管理制度》”。如果用户问“年假有几天”,这句话在语义上跟年假高度相关,但它并没有给出答案。检索系统很可能把它当高相似度内容召回,然后大模型基于这句话,自然只能给出一个含糊甚至错误的回答。
更麻烦的是,如果切块的粒度太大,把一个段落里包含的多个主题混在一起,召回的相关性也会被稀释。比如一块文本同时讲“请假流程”和“年假计算”,用户问年假时,这块文本可能因为包含太多无关信息,相似度被拉低,反而排在了后面。这些问题的根子,都在于数据进入检索系统时没有被合理结构化。
所以,评估一个 RAG 系统,不能只看“最后答案对不对”,而要分段看:检索阶段有没有把包含答案的文本块召回、重排阶段有没有把最相关的块顶到前面、生成阶段有没有忠实于上下文。哪一环断了,都会表现为答非所问。
2. 知识库构建端的三个关键细节
2.1 切块策略:太大太小都是坑
切块是 RAG 里最容易被轻视、却又影响最大的环节。切得太小,一个完整答案被拆成两半,检索只召回其中一半,生成时信息残缺,自然答不全;切得太大,块里混入大量无关内容,向量表示被稀释,召回精度也会下降。
我踩过最深的坑是对着一份 FAQ 文档用固定 500 字符切块,结果有一条问答的答案是 800 字,直接被切到了两个块里。用户问的时候,系统召回的是含有前半部分答案的块,后半部分始终没被检索到,于是模型只能给出“部分正确但明显不完整”的回答。后来我把切块策略改成了按标题和段落边界优先,再辅以固定大小兜底,效果立刻好了不少。
一个比较实用的经验是:对结构化的 FAQ 文档,尽量按“一问一答”为粒度切块,宁可一个块里有一个完整问答,也不要强行拆开;对长文章,建议用递归切块,按标题、段落、句子的优先级逐级切分,让每个块尽量是语义完整的单元。同时要设置重叠区间,一般 chunk_overlap 取 chunk_size 的 10%~20%,避免关键信息刚好落在切分边界上被丢掉。
2.2 向量检索的局限,以及“知识库能不能存图片”
先回答一个我经常被问的问题:RAG 知识库能存图片吗?严格来说,常规的文本向量 RAG 并不能直接存图片,因为 embedding 模型处理的是文本而不是图像像素。如果你只是把图片文件本身放在对象存储里,再把图片的“描述文字”或“OCR 识别文本”做向量化入库,那么用户是可以检索到这张图片的——但前提是你要先把图片转成文字描述。如果要做真正的“以图搜图”或多模态问答,那就需要多模态 embedding 模型,这是另一套方案,别指望用文本 RAG 搞定。
回到向量检索本身:它的强项是语义召回,但弱点是“关键词精确匹配”不行。比如知识库里有“CTR”,用户问的是“点击率”,如果 embedding 模型没有学到这个对应关系,召回效果就不稳定。所以在实际项目中,我更推荐做混合检索:同时跑向量召回和 BM25 关键词召回,再用 RRF(Reciprocal Rank Fusion)或分数归一化加权合并。这样即使向量那边失手,关键词那边也能兜底。
向量化这一步还有个细节容易被忽略:中文场景下 embedding 模型要选中文语料训练过的,比如 bge-m3、m3e 这类,英文模型直接用在中文知识库上,效果会明显下降。别小看这一步,我见过不少项目换了个中文 embedding 模型,召回命中率直接涨了十几个点。
2.3 知识库的类型:文本 RAG、结构化库 RAG、知识图谱 RAG 怎么选
很多同学把“知识库”理解为“一堆文档”,但其实 RAG 面对的知识库形态有很多种,选错类型也是答非所问的重要原因。最基础的是非结构化文本 RAG,适合处理 FAQ、制度文档、产品手册这类内容;第二种是结构化数据库 RAG,典型做法是 Text-to-SQL,用户用自然语言查询,系统把问题转成 SQL 语句去数据库里取数,适合“上个月华东区销售额是多少”这类精确计算问题;第三种是知识图谱 RAG,也叫 GraphRAG 或 Ontology RAG,适合处理“A 公司的 B 业务和 C 公司是否有间接关联”这种多跳关系推理问题。
这三种形态不是互斥的,实际落地经常是混着用。比如一个企业内部助手,制度类问题走文本 RAG,经营数据类问题走结构化 RAG,供应链上下游关系这类走图谱 RAG。判断的依据很简单:用户的问题需要“看懂文字”回答,还是“算了数字”回答,还是“理清关系”回答。三种需求对应三种知识库形态,硬拿文本 RAG 去回答数据统计问题,能不答非所问吗?
这里多说一句,Ontology RAG 并不是什么玄学,它就是先用本体定义好实体和关系的类型,再用图谱结构约束检索路径,让模型沿着关系链去收集证据。对“多跳问答”场景,效果比普通向量检索好得多,但构建成本也高,一般团队等到文本 RAG 玩明白了再上也不迟。
3. 查询端的优化:不是用户怎么问,你就怎么查
3.1 查询改写:把用户的模糊话翻译成知识库的语言
用户不会按照你知识库的写作风格提问,这是答非所问最常见的原因之一。知识库里面写的是“发票开具流程”,用户问的是“怎么开发票”,关键词完全对不上;知识库写“离职交接清单”,用户问“我要走了要办什么手续”,口语和书面语差距更大。这时候如果拿用户原始问句直接检索,召回效果通常不好。
一个很实用的办法是加一道查询改写:让大模型先把用户问题翻译成更贴近知识库风格的关键词组合,或者从问题中提取核心实体和意图,再拿去检索。这个做法成本低、见效快,你只需要在检索之前多调一次大模型,把原始问题转成几个候选查询词。我常用的 prompt 大概是这样的:
你是一个知识库检索助手。请把用户的自然语言问题改写成适合文档检索的查询片段,可以拆分成多个短查询,用中文输出,不要加多余解释。 用户问题:{question}例如把“我要离职了公司有什么规定”改写成“离职流程 离职交接 相关规定”。这样原文里隐含的多个检索点就被显式拆出来了,多路召回都能命中。
更加进阶一点的是 HyDE 方案:先让大模型根据用户问题生成一个假设性的理想答案,再用这个假设答案去检索相关文档,最后让大模型基于真实检索到的文档重新生成正式回答。这个思路背后的逻辑是:假设答案本身就是一种更贴近知识库语言表述的查询表示,因此能提高召回命中率。但 HyDE 有一次额外的大模型调用,延迟和成本要考虑清楚。
3.2 重排序:召回 50 条不如精排 5 条
向量检索阶段一般会召回数量偏多的候选块,比如 top 50,然后把这些候选块直接塞给大模型。但问题在于,前 50 个块里可能只有前 3 个是真正有用的,剩下的全是干扰信息。大模型面对一堆混淆上下文,答非所问的概率会明显上升。
所以一个高性能 RAG 系统里,重排序这一步基本是标配。召回阶段先多拉一些候选,然后用一个专门的重排序模型(比如 bge-reranker、Cohere Rerank)逐条计算候选块和用户问题的相关度,把 top 50 压缩成 top 5 左右,再组装成 Prompt 喂给大模型。我自己实测下来,加了重排序之后,答案的准确率提升非常明显,尤其是知识库文档量大了之后,这个提升几乎可以用“质的飞跃”来形容。
上下文组装还有一个细节:不要把多个高相关但内容重复的块都塞进去。可以用一个简单的去重逻辑,相似度太高的相邻块只保留信息最完整的那一条,减少上下文冗余。大模型上下文窗口再大也经不起无效信息占位。
4. 典型问题排查:从现象到根因
4.1 五个常见“答非所问”现象的深度诊断
第一个现象:答案在复述文档原话,但没有直接回答问题。原因通常是召回到的块太长了,里面确实有相关句子,但模型没找到那一句;或者 Prompt 里没有强调“基于上下文提炼并回答问题”,导致模型把原文直接抄了一部分出来。解决方法是缩小切块粒度,并在 Prompt 里加一句“只能基于上下文内容,用自己的话组织答案,不要复述原文”。
第二个现象:知识库里有答案,但系统说找不到。排查思路是先做“闭卷测试”,直接把用户问题和文档块打印出来,看这块到底有没有被召回。如果没被召回,大概率是 embedding 模型和切块粒度的问题;如果召回了但排名太靠后,那就是重排序没做或模型选得不对。这个现象最常见的原因,是用户问句和文档表述之间词汇差异太大,向量模型没能拉近距离,这时候上混合检索就能救回来。
第三个现象:问题简单,但答案绕来绕去不切题。这通常是召回结果里出现了“看似相关但不含答案”的干扰块。前面提到的“有篇文章提到年假但不包含年假细则”就是典型例子。建议在重排序之后再加一道基于规则或小模型的过滤:如果候选块里没有出现用户问题里的核心实体词,整体降权。当然,更治本的办法是在知识库建设时就做好数据清洗,把这种“提到但没讲清”的内容和正式内容区分开。
第四个现象:模型一本正经地编造知识库里没有的细节。这是典型的幻觉问题,根源不只是模型本身,也可能是 Prompt 里让模型“自由发挥”的空间太大了。我的经验是:在 Prompt 里显式声明“如果你无法从上下文中找到明确答案,请回复抱歉并列出已找到的相关线索”,这个约束比换个模型更直接有效。与此同时,要检查召回的知识块里是否真的包含了答案,如果 top 5 里都没有,那再怎么约束 Prompt 也白搭。
第五个现象:同样的知识库,换了个问题模板,答案质量波动巨大。这说明系统对查询表述非常敏感,因为查询改写或向量模型没有很好地泛化。建议对高频问题做一定量的“问题模板增强”,也就是把同一意思的多种问法一起做向量入库,让每个 FAQ 都对应多条不同措辞的查询,这样用户无论怎么问都能撞上其中一种问法。
4.2 排查速查表:现象、根因、解决动作
| 现象 | 可能根因 | 优先排查动作 |
|---|---|---|
| 答案复述原文,不直接回应用户问题 | 切块过大 / Prompt 缺少提炼指令 | 改小切块;Prompt 增加“用自己的话回答” |
| 知识库有答案但系统回复找不到 | 向量模型不适应中文 / 词汇差异大 | 换中文 embedding 模型;上 BM25 混合检索 |
| 答案绕来绕去,不切题 | 召回结果含大量干扰块 | 加重排序;按核心实体词过滤候选块 |
| 模型编造知识库没有的细节 | 上下文缺少答案 / Prompt 约束太弱 | 强约束 Prompt;检查 top 5 候选块是否真含答案 |
| 换一种问法答案质量波动大 | 查询改写不稳定 / 问题模板覆盖不足 | 对 FAQ 做多问法增强;查询改写并多路召回 |
这套速查表不是教条,但它能帮你把“模糊的答非所问”拆成“具体链路里某一个环节的缺陷”,然后有针对性地优化,而不是瞎调参数。
5. 评估与持续优化:让效果可量化
5.1 不要只盯着“答案对不对”,要拆开评估三层指标
很多团队优化 RAG 全凭感觉,今天换下切块参数,明天换下 Prompt,但就是没建立评估闭环。我给的建议是一定要拆开三层看:检索层、排序层、生成层。
检索层重点关注召回率,也就是“包含答案的文档块有没有出现在召回列表里”。排序层看的是重排序之后,正确答案是否排进了 top 5。生成层则看两个东西:一是 faithful(忠实度),即答案是否严格基于检索到的上下文;二是 answer relevancy(答案相关性),即答案是否真正回应了用户问题。业界已经有一些开源评估框架,比如 RAGAS,直接可以算这些指标,也可以自己准备一百个问答对,人工打标跑回归。
我自己的习惯是:搭一个最小的评测集联系,不需要很大,五十到一百条有代表性的问答就够了,但覆盖面要广,包括常见问题、冷门问题、模糊口语问题、多跳问题。然后每改动任何一个环节,重新跑一遍评测集,把召回率、排序命中率、忠实度这些数字拉出来对比。没有这个闭环,你所谓的“优化”就是瞎撞运气。
5.2 一条可落地的优化路径:先建基线,再逐层调优
如果你现在手里有一个答非所问的 RAG 系统,我建议不要同时改多个环节,而是按这个顺序逐步调:第一步,检查知识库源头,把重复、过期、残缺的文档清掉,这可能能解决一半的问题;第二步,固定切块策略和 embedding 模型,跑通基线评测,拿到召回率和忠实度;第三步,上混合检索,对比向量召回和 BM25 的效果,看谁拖了后腿;第四步,加重排序模型,观察排序命中率的变化;第五步,做查询改写和多问法增强,专门解决用户问法和文档表述不一致的问题;第六步,再回头调 Prompt 和上下文组织策略。
这个路径的核心逻辑是“先保证系统能把正确的文档找回来,再保证找回来的文档是够用的,最后才是让模型把答案组织好”。很多人恰好把这个顺序做反了,一开始就疯狂调 Prompt,结果检索那边根本拿不到正确答案,Prompt 调得再好,也还是在错误的上下文里做文章。
我最后分享一个真实感受:做 RAG 知识库问答,最容易让人挫败的倒不是技术难点,而是它看起来太像“一个能跑的 Demo”了,于是团队容易低估数据侧和检索侧的工作量,把大量时间花在更换大模型上。换个更好的模型当然有用,但它的收益会很快见顶。真正决定知识库问答系统能不能扛住真实业务压力的,还是那些琐碎的、看起来一点都不酷的环节——数据清洗、切块策略、查询改写、重排序、评测闭环。把这些做扎实了,答非所问的问题自然会消退大半。