用 Java 接入大模型(十六):检索质量优化——查询改写、混合检索与重排

简介: 本文详解RAG效果不佳的根源——“检索到了”不等于“答对了”。提出三层归因法:1)答案未召回(查改写/切分);2)召回但排序靠后(需重排);3)排序靠前仍答错(优化拼装与提示词)。强调先分层诊断,再精准施策,避免盲目优化。

前三篇把离线链路搭完了:切分、向量化、入库。库建好之后,很多人会松一口气,然后发现效果并不好——资料明明在库里,答案也明明在资料里,模型却答不对。 这一篇讲的就是从"检索到了"到"答对了"之间那段距离,以及一件比具体技术更重要的事:先判断问题出在哪一层。


一、"检索到了"和"答对了"之间,有三层问题

这是本文最重要的一节。因为在动手优化之前,你必须先知道自己在优化什么——不然很容易在错误的环节上使劲,越优化越困惑。

RAG 的失败可以拆成三层,症状相似但原因完全不同:

第一层:答案所在的块,压根没进召回列表。

检索阶段就漏了。后面的重排、拼装再精巧也没用——没进列表的东西,后面所有环节都看不见它。 这一层的原因是检索能力问题:查询和文档的表达不匹配、切分把答案切散了、向量模型对这类内容不敏感。

第二层:块进来了,但排在很后面。

如果一次只取前 5 个,而正确答案排在第 12 位,它就会被截掉。这一层是排序问题,不是检索问题——召回是成功的,只是顺序不对。重排正是为这一层设计的。

第三层:块排得挺靠前,模型还是答错了。

这一层已经和检索无关了。可能的原因:拼装的上下文里有矛盾信息、块太多把关键信息淹没了、块的顺序让模型忽略了中间部分、或者提示词没要求它"基于给定资料回答"。

三层的解法完全不同:第一层去查改写和切分,第二层上重排,第三层改拼装和提示词。

所以下面这几节,应该对照你的实际症状来挑——不是全都做一遍。一个实用的判断方法是:把某个问题的召回结果原样打印出来读一遍。 如果正确答案根本不在里面,那是第一层;如果在里面但位置靠后,那是第二层;如果它就在前几名而模型仍然答错,那是第三层。这个问题第十三篇也提过,因为它是定位问题最快的方法。


二、查询改写:收益最大,却最常被跳过

这一节放在最前面,是因为它被跳过的比例最高。

原因很自然:用户输入什么问题,就拿什么问题去检索——这个逻辑太顺了,顺到没人会去想它有没有问题。但实际情况是,用户的问题和文档的表述常常不是一套语言。

看几个典型的落差:

  • 口语化:用户问"这个怎么退",文档标题写的是"退款流程说明"。字面几乎不重合,但语义是一回事。
  • 指代:多轮对话里用户说"那它呢"、"这个呢"。这三个字单独拿出来,没有任何检索价值。
  • 缺关键词:用户描述的是现象("打开就闪退"),文档记的是原因("初始化超时导致进程退出")。
  • 问题太宽:用户一次问了三个不相关的问题,当成一个查询去检索,向量会被平均掉,哪个都不像。

针对这几种,改写有几种做法:

基础改写:让模型把口语化的问句改成更接近文档表述的检索语句。

结合历史消解指代:把"那它呢"补全成"XX 功能的退款时效是多少"。这一步必须用到对话历史——单独看当前这句话是无解的。这也是为什么多轮场景下的检索比单轮难。

多子问题拆解:把复合问题拆成几个独立查询,各自检索、各自召回,最后合并去重。代价是检索次数变多。

HyDE(假设性文档生成):先让模型"编"一段可能的答案,再用这段答案去检索。

这个方法一开始听着反直觉——答案都是编的,怎么能用来检索?但它的道理是:编出来的答案,在表达方式上更接近文档,而不是更接近问题。 比如用户问"退款要几天",模型编的答案是"退款一般在 X 个工作日内到账",这句话的措辞和文档里的句子高度相似,检索自然更容易命中。

代价是每次查询多一次模型调用,延迟和成本都会上去。所以它适合那些查询量不大、但准确性要求高的场景。

要不要做改写,有个务实的判断:先统计一下线上查询里,"字面上和任何文档都不相近"的比例有多高。如果这个比例不低,改写的收益就明显;如果你的用户本来就习惯用专业术语提问,改写的收益可能有限。


三、混合检索:向量不擅长的那些东西

向量检索擅长语义相似,但有几类内容它天然不敏感:

  • 专有名词:产品名、人名、地名,尤其是生僻的;
  • 编号与单号:订单号、工单号、型号;
  • 错误码:ERR_4032、TimeoutException;
  • 缩写:团队内部约定俗成的短写。

这几类内容的共同点是:它们的语义信息很弱,识别价值全在字面上。 而向量是把语义压缩成数值,字面细节恰恰在压缩中被抹掉了。所以你问"ERR_4032 怎么解决",向量检索很可能召回的是一堆"错误处理"相关的泛泛内容,而不是那条写着 4032 的文档。

关键词检索(倒排索引)正好补上这一块——它只看字面匹配,编号、错误码命中得非常准。

混合检索就是把两者结合:一路走向量,一路走关键词,然后融合结果。

融合方式常见的有两种:

  • 加权求和:给两路结果各配一个权重,按分数加总。简单,但两路的分数尺度不同,权重要调。
  • RRF(倒数排名融合):不看分数,只看排名——每一路里排第 1 的加 1/(k+1) 分,排第 2 的加 1/(k+2) 分,以此类推,最后加总。

RRF 的好处是不需要校准两路的分数量纲,实现简单、效果稳定,所以在混合检索里被广泛采用。它的直觉是:一个结果如果在两路里都排在前面,那它很可能是真的好。

如果你的资料里有大量编号、错误码、专有名词,混合检索的收益通常比换一个更好的向量模型还明显——因为它补的是能力缺口,不是精度提升。


四、重排:为第二层问题设计的

前面说过,重排解决的是"召回了但排后面"。它值得做,因为向量检索的排序精度天生有限。

原因在于向量检索用的是双编码器:文档和查询各自独立编码成向量,然后比距离。它的优点是快——文档向量可以提前算好,查询只要算一次就能和百万个向量比。但缺点是文档和查询之间没有任何交互,两边的语义是各自压缩的,细节丢失了。

重排模型用的是交叉编码器:把查询和文档放在一起输入模型,让它们直接"对话"。这样能捕捉到细粒度的相关性,精度明显更高。代价是慢得多——每对(查询, 文档)都要过一次模型,没法预处理。

所以两者的分工是明确的:

  • 向量检索负责从海量里粗筛,比如从 10 万块里取 50 个;
  • 重排负责把这 50 个精细排序,取前 5 个送去拼装。

关键在于只对 top-K 做重排。 如果对所有召回结果都重排,成本会失控。K 取多少是个权衡——取太小,真正相关的可能没进来;取太大,重排的耗时上去。实践中从几十这个量级起步比较常见,具体要按你的延迟预算调。

重排模型怎么选,看两点:一是中文效果(和第十四篇选 embedding 一样,别看英文榜单,用自己的数据测);二是延迟(重排是在线路径上的同步操作,慢一点用户就能感觉到)。如果一个重排模型把延迟从 200ms 拉到 2s,那它带来的精度提升可能不值得。

还要注意一个取舍:重排是"锦上添花",不是"雪中送炭"。 如果正确答案根本没被召回(第一层问题),重排救不了——它只能在给定的候选里重新排序。所以顺序必须是:先保证召回得了,再考虑排得准。


五、最后一公里:上下文怎么拼

这一节最容易被忽略,但它属于第三层问题,而且改起来最便宜——不用重跑任何离线任务,改几行代码就能验证。

几个要点:

块数不是越多越好。 一个常见的直觉是"多给点资料总没坏处",但实际相反:拼进去的块越多,噪音越多,模型越容易被无关内容误导。而且输入 token 增加意味着成本和延迟同时上升(第九篇算过这笔账)。

有一个已知的普遍现象值得注意:当上下文很长时,模型对中间部分的关注度明显弱于开头和结尾。 这意味着——如果你非要放 10 个块,最相关的那几个应该尽量放在开头和结尾,而不是中间。 这个调整不花任何成本,但效果差异明显。

必须去重。 第十三篇讲过切分时的重叠会导致相邻块内容重复。如果两个高度相似的块同时被召回,它们会占掉两倍位置却只提供一份信息。拼装前做一次相似度去重,很便宜。

把出处带上。 每个块拼进去时标上来源(文档名、章节),并明确要求模型在回答里引用。第十二篇讲过理由:这是用户发现"检索错了"的唯一机制。 如果模型答得通顺但引用了一份不相关的文档,用户至少能看出来。

提示词里要明确约束。 告诉模型"只根据给定资料回答,资料里没有就说没有"。不写这句,模型遇到资料不全的情况会用自己的先验补齐——这正是第十二篇说的"RAG 降低但不消除幻觉"的成因之一。

块之间要有清晰的分隔。 别把几个块首尾相接成一大段,那样模型分不清哪句来自哪份文档,引用也就无从谈起。


六、落地顺序:别一次全上

这几件事的收益和成本不一样,建议按这个顺序推进:

  1. 先修拼装和提示词。 零成本、可即时验证。很多人做完这一步就明显改善了,也顺便排除了第三层问题。
  2. 再加查询改写。 一次模型调用,解决指代和表达鸿沟。这是收益最大的一步,尤其多轮场景。
  3. 然后上重排。 需要引入模型和延迟预算,但能把"排后面"的问题压掉。
  4. 最后评估混合检索。 如果你的资料里有大量编号、错误码、专有名词,这一步的收益会非常突出;如果没有,可以缓一缓。
  5. HyDE、多查询扩展这类更重的手段,放在最后,只在你已经确认前面几步都用尽、且准确性要求很高时才考虑。

这个顺序的理由是:每一步都要能被单独验证。 如果一次全上,效果好了你也不知道是哪一步的功劳,效果不好更不知道是哪一步的问题。


七、小结与检查清单

  • 先分层再优化:召回没进来(第一层)→ 查改写和切分;进来了排后面(第二层)→ 上重排;排前面还答错(第三层)→ 改拼装和提示词
  • 最快的定位手段:把召回结果原样打印读一遍,一眼就能看出属于哪一层
  • 查询改写:收益最大也最常被跳过;重点是口语化落差、指代消解、多子问题拆解;HyDE 有效但多一次调用
  • 混合检索:补的是"向量对字面不敏感"这个能力缺口,编号、错误码、专有名词场景收益明显;RRF 融合不需要校准量纲
  • 重排:交叉编码器 vs 双编码器,只对 top-K 做;它是锦上添花,救不了召回不到的情况
  • 拼装:块数不是越多越好、最相关的放首尾、去重、标出处、提示词里明确"只依据给定资料"
  • 推进顺序:拼装与提示词 → 查询改写 → 重排 → 混合检索 → 更重的手段;每一步都要能单独验证

一道判断标准:问你"现在这套 RAG 的问题主要出在哪一层",如果答不出来,那你做的所有优化都只是在碰运气——而运气这件事,在没有指标的地方是看不出来的。


下一篇

优化做完了,下一个问题很自然:怎么知道它到底变好了? 凭感觉是不行的——同一批问题,改一版觉得好一点,再改一版又觉得差一点,来回摆动几天没有结论。下一篇讲 RAG 的效果评测:评测集怎么建、召回率和命中率分别测什么、答案质量这种"没法算对错"的东西怎么评、以及怎么让评测结果真的能指导决策。


本文是「用 Java 接入大模型」系列第十六篇。上一篇讲了向量数据库选型与 Java 落地,建议按顺序看。文中参数与比例(如重排 top-K)为实践起点,真实取值请按你的资料规模与延迟预算实测确定。

目录
相关文章
|
12天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
4942 33
|
13天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
4578 52
|
7天前
|
Linux 开发工具 iOS开发
2026最新codex下载安装保姆级使用教程(下载+安装+登录+使用,含所有历史版本)
本文详解OpenAI Codex国内快速上手指南:提供Win/macOS/Linux全平台免安装包、npm/Homebrew等多方式安装、账号/API Key双登录、简体中文切换,覆盖下载、安装、汉化、使用及高频避坑(如微软商店失败、npm超时等),10分钟即可跑通。
1643 3
|
13天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
2197 7
|
5天前
|
人工智能 安全 API
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(dsh)是DeepSeek AI于2026年开源的轻量级Agent运行框架,基于Cordis内核,践行“一切皆插件”理念。支持Web/桌面双端,开箱即用;内置多模型接入、丰富插件生态(超3.5万),适配编程、办公、创作等场景。(239字)
1090 0
|
7天前
|
云安全 人工智能 安全
|
20小时前
|
云安全 人工智能 安全
|
6天前
|
人工智能 并行计算 调度
本地 AI 一键部署工具 LocModel:Windows 端集成画图、视频生成、Agent 的开源模型运行方案
LocModel 是专为 Windows 设计的一站式本地 AI 工具,支持零配置启动、自动硬件检测、GGUF 模型一键加载、文生图/视频生成及 Agent 自动化任务。适配 8G 显存,内置显存分片调度,轻松实现 AI 漫剧批量创作与离线大模型调用。(239 字)

热门文章

最新文章