
检索结果不准、生成内容脱离原文这四个字在RAG项目里能劝退大多数人。我做过几个文档问答和知识库助手发现只要涉及“4.2”这种小节编号往往就是团队从“能跑通”转向“能靠谱”的关键节点。上一篇还在讲管线搭好这一篇就开始暴露问题用户问“报销流程多久到账”系统检索回来的却是“差旅费报销标准”或者模型依据检索片段生成时把“不适用于外籍员工”直接忽略输出成“所有员工均可享受”。不是模型不够强也不是向量库选得不好而是检索和生成中间的“信息保真”链条断了。这篇文章我直接把常见症状、排查思路、能落地的调整手段写透包括分块参数怎么定、召回重排怎么做、提示词怎么约束引用还有我自己项目中踩坑换来的经验。适合正在做知识库问答、客服助手、文档摘要这类RAG应用的人参考。1. 先定位问题你遇到的到底是“检索不准”还是“生成不贴”很多人一上来就调Embedding模型其实先做诊断更省时间。同样一个query把检索到的top5片段单独拿出来看如果片段内容本身和问题不相关那是检索的锅如果片段相关但最终回答没用上、或者改写了关键信息那是生成的锅。这两个问题解决路径完全不同。1.1 两种典型症状的表现差异检索不准的表现很直接top5里能看到大量无关片段或者相关片段排在后面被截断没进入上下文。比如问“合同违约金的法定上限”返回的片段全是“合同签订流程”“违约金发票税率”这类表面关联但实际无用的内容。生成不贴的表现更隐蔽检索片段完全正确但模型在整合时丢失了限定条件、数字、否定词或者补了一段原文没有的“常识性解释”。比如原文写“该政策自2024年1月1日起实施不适用于存量项目”模型回答却变成“自2024年1月1日起实施”有条件限制的信息被无意间删掉。这两种症状经常同时出现但处理优先级不同。我建议先把检索结果截图保存下来用最原始的方式肉眼评估——把query喂给检索接口不接生成模块直接看返回片段。这一步能帮你快速判断主攻方向。1.2 用“最小测试集”快速区分责任方准备20到30个典型问题覆盖数字、否定、限定条件、专有名词、多轮表述这五类。写一个简单脚本遍历所有query输出每个query的top5片段原文然后人工标注“相关/部分相关/不相关”。如果“不相关”占比超过30%问题集中在检索链路分块不合理、Embedding模型不匹配、检索方式单一、没有重排序。如果“相关”占比很高但最终回答仍有错误问题集中在生成链路提示词约束不足、上下文拼接方式有问题、解码参数不合适。我遇到过最典型的案例检索top1片段明明包含答案但模型回答了一堆通用套话。原因是系统提示词写得太宽泛模型把“结合资料”理解成“参考资料但不限于”。后面把提示词改成“仅基于给定的检索片段回答如果片段中没有答案直接回答不知道”问题立刻缓解。2. 检索不准的根源拆解分块、Embedding、召回策略检索准不准本质上取决于“查询语义”和“文档片段语义”之间的匹配度。匹配度低不是某一步的错而是从原始文档到向量表征的整条链路都在损耗信息。2.1 分块策略不是所有文档都该切成512字符很多人直接把PDF按固定字符数切成512个token的窗口这恰恰是最容易出问题的点。固定长度分块会切断段落和章节的语义边界比如一个完整条款被切成两半前半部分讲适用条件、后半部分讲执行时间查询“适用条件”就只能召回前半部分模型看到的上下文不完整自然答不准。更好的做法是根据文档结构分块。表格按行列语义拆分条款按编号层级拆分介绍性内容按段落和主题句拆分。如果切出来的块过短比如只有一两句话可以把相邻的小块合并保证每个检索单元至少包含一个完整的“语义命题”。我在处理合同类文档时用章节标题条款编号作为boundary。一个条款块包含“第X条 定义”到下一个条款前的全部内容。实测下来这种分块方式比固定512字符分块在召回命中率上高出20%左右尤其针对“依据第几条”这类精确查询效果明显。分块长度没有标准答案但有一个经验区间中文场景下每个块控制在200到800个字符比较稳妥。太短则缺乏上下文太长则向量表征被稀释且超过模型上下文窗口后还会带来成本问题。最好用验证集跑一版不同分块大小的对比看检索命中率和最终回答准确率的综合变化。2.2 Embedding模型选择通用模型和领域模型差距很大Embedding模型决定了文本被映射到向量空间后的语义距离。通用模型对“长尾业务词”和“专有缩写”的表征往往不够精准。比如“ROI”在通用模型里可能靠近“投资回报率”但在业务语料里可能表示“区域运营指标”。如果词表里没有充分对齐检索结果就会跑偏。我的建议是先用开源通用模型跑基线再用业务语料做二次微调。微调Embedding模型的成本不高几百条“问题-相关片段”对就能有明显效果。也可以用领域预训练模型直接替换但注意中文领域模型和英文术语混合场景要单独验证。另外还要注意Embedding模型的维度。高维度如1024/1536表征能力更强但存储和检索开销也大低维度如384/768响应更快适合大规模文档。不是维度越高越好关键是匹配你的数据规模和延迟要求。2.3 召回策略向量检索不是万能药只依赖向量检索的RAG遇到关键词精确匹配、缩写、数字区间这类场景很容易翻车。比如用户搜索“IS-9527标准”如果分块里写的是“IS 9527”“9527号国际标准”向量距离很可能较大纯向量召回就有风险。混合检索是常见解法向量检索负责语义相似关键词检索如BM25或基于词频的算法负责精确匹配然后合并两个结果集。合并召回结果时我习惯用RRFReciprocal Rank Fusion这类融合算法而不是简单去重拼接。RRF给每个文档在多个排序列表中的名次取倒数求和能有效提升两个检索器共同命中的文档的权重又能保留单一检索器的强信号。实现上不复杂。先分别得到向量top20和关键词top20然后对两个列表里的文档按照RRF公式计算融合分重新排序后取top5或top10。这样既不会丢掉语义关联也能兜住精确词汇匹配。实测在包含大量产品型号和工单编号的文档集里混合检索能把命中率从65%拉到85%以上。3. 生成不贴原文的解决思路让模型“死磕”检索片段检索上来了生成还是会漂。原因在于大模型天生倾向于“自由发挥”。想让它严格贴原文除了在提示词上硬约束还要从上下文组织方式、模型参数、结果校验几个角度一起下手。3.1 提示词设计把“参考”改成“必须基于”很多RAG系统提示词写的是“请根据以下资料回答用户的问题。”这个句式在模型看来资料是参考信息不是事实边界。我经过多轮调优把提示词改成如下结构你是一个严谨的问答助手。你只能依据上下文列表中提供的文档片段来回答问题。回答时遵循以下规则如果问题可以在片段中找到明确答案直接提取原文中的关键信息作答不得额外添加未出现的细节。如果片段中只有部分信息明确说明已知信息和缺失信息。如果片段中没有相关信息直接回答“根据提供的资料无法回答”禁止编造。涉及数字、日期、条件、限制词时必须原样引用并说明适用前提。回答中标注引用来源序号如[1]、[2]。这段提示词的核心是“禁止编造引用约束”。别小看“标注来源序号”这一句话它能让模型在生成时更注意片段边界减少幻觉。同时把来源序号加到上下文中也方便前端展示溯源用户看到“引用自合同第X条”时信任度完全不同。3.2 上下文组织片段顺序和去重影响输出质量多个检索片段拼接进上下文时顺序有讲究。很多实现直接按检索分数从高到低拼接但模型对上下文不同位置的信息关注度不同——更倾向于关注首尾内容。如果关键答案在第二个片段中间可能被模型忽略。我的做法是把检索片段按与问题的相关性排序后再做一次“答案预判”式的重排。具体来说先让模型或轻量分类器快速判断每个片段与问题是否直接相关把直接相关的片段放在上下文最前面或最后面间接相关的放在中间。同时删掉明显重复或互相矛盾的片段避免模型困惑。另外一个容易被忽略的点是片段拼接时要加入分隔符和元信息。比如在片段前面加上“【片段1 来源合同-第4章-第2条】”让模型知道每个片段的边界和出处。这比密密麻麻一句话接一句话要清晰得多模型的follow能力会好不少。3.3 解码参数与其他技巧调低温度不代表万事大吉把temperature调低确实能减少随机性但并不能解决“事实错误”问题。事实错误更多来自模型对被检索片段的“不自觉补全”。更有效的组合是temperature设为0.1到0.3之间减少发散。top_p设置在0.8到0.9之间保持一定多样性但不过度。加上“禁止重复原文未提及的数值和结论”的负面约束。如果使用开源模型还可以尝试在生成后做“答案-原文一致性校验”。用一个小模型或规则脚本检查回答中的关键数字、实体是否在检索片段中出现。如果出现“答案里的数字在原文片段里找不到”的情况就把该回答拦截改为“资料不足无法确认”。这种方法虽然简单但在生产环境里能挽回大量不合理输出。4. 重排序被很多人跳过却效果显著的环节检索系统返回top20直接塞给生成模型不仅浪费上下文还会引入噪声。重排序Rerank是连接检索和生成之间的“精排层”目的就是把真正和问题相关的片段排到最前面。4.1 为什么要单独加一层Rerank向量检索和关键词检索都是“粗排”它们追求的是召回率宁可多返回也不漏掉。但生成模型只关注精准的上下文。如果top5里混入两个无关片段模型可能会被干扰生成偏离原文的内容。重排序模型通常是交叉编码器结构问题和片段一起输入直接输出相关性分数比双塔embedding更精准。缺点是速度较慢不适合对超大量文档实时打分。所以典型流程是先用向量/混合检索粗召回top20到top50再用重排序模型精排取top5到top8最后再交给生成模型。我见过最夸张的一个优化案例加了重排序后最终回答准确率提升了将近30个百分点。原因就是top5里真正相关的片段从1个变成了3个模型有了足够的事实支撑。4.2 重排序模型选择与融合策略开源生态里有一些做Sentence-Level Rerank的模型中文效果不错的也有几个。选型时注意看训练数据是否包含中文以及模型在公开榜单上的表现。生产环境先跑一个小样本测试比较“直接取top5”和“重排后取top5”的准确率差异选效果稳定的上。重排序结果要和原有检索分数融合时最简单的方式是直接把重排序分数作为主排序依据原得分只在重排分数相差极小的时候作为tie-breaker。如果重排序模型输出的是0到1的相关性概率可以设置一个阈值低于阈值的片段直接丢弃减少上下文污染。另外重排序不仅仅适用于文本片段。如果你检索的是表格或图片也可以用跨模态模型来排序但复杂度会上一个台阶。先从纯文本开始见效最快。5. 构建评测闭环没有评价指标优化就是盲人摸象检索和生成是两部分但如果只凭感觉调参很容易出现“这次调好了换个问题又坏了”的循环。正确做法是建立一套可重复的评测集和指标每次改动都跑一遍用数据说话。5.1 离线评测召回率、命中率、忠实度对于检索模块可以标注一批“查询-相关片段”正负样本。指标用召回率top20内包含至少一个相关片段的查询占比和命中位置相关片段在top5内的平均排名。更严格的可以算MRR倒数排名相关片段排得越靠前分数越高。对于生成模块要分别评估忠实度生成答案中的关键信息是否都能在上文片段中找到依据。可以人工标注也可以用自然语言推理模型自动判断。完整性问题对应的信息点在答案中是否全部覆盖。避答率遇到无法回答的问题时系统是否如实说“不知道”而不是强行编造。这些指标不需要全上先选2到3个核心的就行。重点是保证评测集的多样性覆盖不同文档类型和问题类型。5.2 在线反馈与Bad Case迭代离线指标只能反映一部分问题线上真实用户提问更复杂。我习惯定期从日志里捞Bad Case按错误类型分组检索召回错误、上下文信息缺失、生成遗漏限定条件、生成过度泛化。每类每周至少分析10个具体案例形成记录表。根据Bad Case的记录倒推优化方向。如果很多Bad Case是“关键词能匹配但语义检索没召回”那就在混合检索里加大关键词权重。如果很多是“相关片段在top3里但答案不贴”那重点调提示词和片段顺序。总之单点优化不如闭环迭代每次改动都跑一遍离线评测集同时抽看新Bad Case确保没有引入了新问题。6. 一整套可落地的优化顺序参考这个问题没有银弹但我可以给你一套我试过、效果比较稳定的操作顺序从低投入高回报的开始避免一上来就微调模型。第一步把分块方式从固定长度改成按文档结构切分这几乎零成本收益却最明显。第二步建立50条左右的评测集包含业务真实问题标注相关片段和标准答案。第三步把纯向量检索改成混合检索引入关键词召回和RRF融合。第四步加入重排序模型粗召回top20精排取top5。第五步重写生成提示词明确禁止编造要求引用片段序号并调低temperature。第六步增加简单的一致性校验在返回给用户前过滤掉明显与检索片段冲突的答案。第六步做完绝大多数“检索不准、生成不贴原文”的问题都能显著改善。之后如果还想继续提升再考虑领域Embedding微调或微调生成模型但收益边际就小了投入产出比要看具体情况。我个人实际操作中的体会是最隐蔽的问题往往出在分块和上下文拼接方式上。很多团队花大力气换了更强的Embedding最后发现效果提升还不如把合同条款完整切开来得明显。检索管道是一条链路哪里信息丢失最终都会体现在生成质量上。最后分享一个我一直在用的小技巧把检索片段和最终回答放在同一个日志里可视化对比。每次生成完把命中的前3个片段连同回答一起打到调式面板上一眼就能看出模型用了哪个片段、有没有乱发挥。这个习惯帮你省下大量猜原因的力气。